AIで音声ファイルから歌詞のSRTファイルを生成する

生成AI

Suno AIなどで生成した楽曲を動画にする際、手間の掛かる作業が歌詞の表示です。
編集している動画を再生しながら、歌に合わせて歌詞を一つ一つ配置していくのは大変です。
そこで、AIの力を借りてSRTファイルを作ってもらうことにしました。

SRTファイルというのは、字幕とその開始時間と終了時間が記載された字幕ファイルです。
動画編集ソフトによっては、SRTファイルをドラッグ&ドロップするだけで、動画に字幕を付けてくれます。

音声ファイルの文字起こしには、無料で使えるGoogle AI Studioを利用しました。

Google AI Studio

ログイン後の画面です。

Modelは「Gemini 1.5 Pro」を選択しました。

画面下のチャット入力欄にある+(プラス)マークから、「Upload to Drive」を選択して音声ファイルを指定します。
「Upload to Drive」が表示されない場合は、最初にGoogle Driveと接続してください。

ファイルが選択された状態で、チャット欄にプロンプトを入力します。
今回は歌詞が分かっているので、ちょうどよく区切った歌詞も渡しています。
こうすることで、誤字や変なタイミングで区切られることを防げます。

音声ファイルから歌詞のタイムコードを付ける。
タイムコードはミリ秒単位。
イントロ、アウトロ部分はタイムコードに含めない。
歌詞の歌い出しが開始時間、歌い終わりが終了時間。
最終出力はSRTファイル形式に整形する。
srtの時間の形式の例: 00:00:05,800
歌詞は以下のとおり。

おひさまが沈んだら
しずかな夜の時間を楽しもう
ごほうびのコーヒーを
とっておきのマグに注ごう
おしゃべりは明日にまわして
つかれたら一休みしよう
かぜに乗る星たちの歌
れーすのカーテンを閉めて
さあ、おやすみなさい
またあしたね

「Run」を押すと、AIが音声ファイルを解析して結果を出力してくれます。

1
00:00:05,800 --> 00:00:08,151
おひさまが沈んだら

2
00:00:08,151 --> 00:00:11,081
しずかな夜の時間を楽しもう

3
00:00:11,081 --> 00:00:13,761
ごほうびのコーヒーを

4
00:00:13,761 --> 00:00:16,641
とっておきのマグに注ごう

5
00:00:16,641 --> 00:00:19,681
おしゃべりは明日にまわして

6
00:00:19,681 --> 00:00:22,561
つかれたら一休みしよう

7
00:00:22,561 --> 00:00:25,601
かぜに乗る星たちの歌

8
00:00:25,601 --> 00:00:28,881
れーすのカーテンを閉めて

9
00:00:28,881 --> 00:00:31,041
さあ、おやすみなさい

10
00:00:31,041 --> 00:00:32,641
またあしたね

返ってきた結果の右上「…(More Option)」をクリックし、「Copy rendered」を選択すると、クリップボードにコピーしてくれます。
それをテキストファイルに貼り付け、拡張子はsrt、文字コードはUTF-8として保存すれば完成です。

私の場合は、srtファイルをaviutlのオブジェクトファイル(exo)に変換して使用しています。
exoにすることで、aviutlの拡張編集にそのまま放り込めます。
自分用のすごく雑なコードですが、一応置いておきます。

import re
import sys

FrameRate = 24
TextX = 0
TextY = 290
TextSize = 24
TextFont = "築豊明朝"

# 適当にexoのフォーマットを用意する
ObjectFormat = '''[<idx>]
start=<start>
end=<end>
layer=3
overlay=1
camera=0
[<idx>.0]
_name=テキスト
サイズ=<size>
表示速度=0.0
文字毎に個別オブジェクト=0
移動座標上に表示する=0
自動スクロール=0
B=0
I=0
type=0
autoadjust=0
soft=1
monospace=0
align=4
spacing_x=2
spacing_y=0
precision=1
color=ffffff
color2=000000
font=<font>
text=<text>
[<idx>.1]
_name=シャドー
X=0
Y=0
濃さ=40.0
拡散=10
影を別オブジェクトで描画=0
color=000000
file=
[<idx>.2]
_name=標準描画
X=<x>
Y=<y>
Z=0.0
拡大率=100.00
透明度=0.0
回転=0.00
blend=0
'''

# srtファイル解析
def extractSrt(file_path):
    data = []
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            # 行番号
            if line.isdigit():
                continue
            # 時間情報
            match = re.match(r'^(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})$', line)
            if match:
                start_time, end_time = match.groups()
                text = ""
                for next_line in f:
                    next_line = next_line.strip()
                    if not next_line:
                        break
                    text += next_line + '\n'
                data.append([start_time, end_time, text.strip()])
    return data

# フレーム変換
def time2frame(timestr):
    # 正規表現で時間を分解
    match = re.match(r"(\d+):(\d+):(\d+),(\d+)", timestr)
    if not match:
        raise ValueError("不正な時間形式です。")
    hours, minutes, seconds, milliseconds = map(int, match.groups())

    # 秒数に換算
    total_seconds = (hours * 3600) + (minutes * 60) + seconds + (milliseconds / 1000)

    # フレーム値に変換
    frames = total_seconds * FrameRate

    return int(frames)

# 文字列を4096文字のバイト文字列に変換
def str2ByteStr(s):
    byte_data = s.encode("utf-16")[2:]
    padding = b'\x00' * (2048 - len(byte_data))
    byte_data += padding
    hex_str = byte_data.hex()

    return hex_str

# exoフォーマットの変数部分を置き換える
def editFormat(idx, start, end, text):
    s = ObjectFormat.replace("<idx>",str(idx))
    s = s.replace("<start>", str(start))
    s = s.replace("<end>", str(end))
    s = s.replace("<x>", str(TextX))
    s = s.replace("<y>", str(TextY))
    s = s.replace("<size>", str(TextSize))
    s = s.replace("<font>", TextFont)
    s = s.replace("<text>", text)
    return s

# 歌詞のデータをexo形式の内容に変換
def data2exo(data):
    idx = 0
    out = ""
    for d in data:
        start = time2frame(d[0])+1
        end = time2frame(d[1])-1
        text = str2ByteStr(d[2])
        out += editFormat(idx, start, end, text)
        idx += 1
    return out

# SJISとして保存
def saveText(text, file_path):
    with open(file_path, 'w', encoding='shift_jis', newline='\r\n') as f:
        f.write(text)

def main():
    #SRTファイルを引数で渡す
    if len(sys.argv) < 2:
        return
    file_path = sys.argv[1]

    data = extractSrt(file_path)
    exo = data2exo(data)
    saveText(exo, "out.exo")

main()

タイトルとURLをコピーしました