1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自分専用カラオケ動画をPythonで作る:AI音源分離 × Whisper × 歌詞同期の完全ガイド【2026年版】

1
Posted at

「JOYSOUNDにもDAMにも入ってない曲、自宅で歌いたいときどうしてますか?」――マイナーな洋楽、ボカロの新曲、同人音楽、海外のアニソン……精密採点はもちろん、歌詞すらカラオケ機に出てこない曲って結構あります。

最近のAI(Whisper)と音源分離(Demucs)を組み合わせると、原曲mp3を投げるだけで「歌詞が同期して流れるカラオケ動画(mp4)」を1本まるごと自動生成できるようになりました。配信PCにディスプレイを繋げば、自宅カラオケが完成します。

この記事では、原曲ファイル → カラオケmp4の全自動パイプラインをPythonで構築します。

著作権について:作成したカラオケ動画の公開・配信には、原曲の著作権処理(JASRAC・原盤権・歌詞使用許諾)が別途必要です。本記事は個人利用・自宅カラオケを前提としています。

この記事でわかること

  • ✅ 原曲mp3 → 歌詞付きカラオケ動画(mp4)の全自動パイプライン
  • ✅ Whisperで日本語歌詞を高精度に文字起こしする設定
  • ✅ ASS字幕でカラオケ風「歌っている部分が色変わり」を実装
  • ✅ ffmpegで背景画像 + 歌詞をオーバーレイした動画化
  • ✅ コードを書かずに済ませたい人向けのおすすめ手段

全体パイプライン

原曲.mp3
   ├─ Demucs(音源分離)→ off_vocal.wav(カラオケ音源)
   └─ Whisper(音声認識)→ lyrics.json(歌詞 + 秒単位タイムスタンプ)
                                   ↓
                             ASS字幕生成(.ass)
                                   ↓
            ffmpeg:背景画像 + off_vocal.wav + 字幕 → karaoke.mp4

ポイントは歌詞認識を「ボーカルだけ」に対して行うことです。原曲のままWhisperに投げると、楽器の音に引きずられて精度が落ちます。

前提条件

  • Python 3.10+
  • ffmpeg(字幕焼き込みに必須)
  • 8GB以上のRAM(Whisper-large-v3を回すなら16GBあると安心)
pip install demucs openai-whisper pysubs2 pillow numpy soundfile

GPUがあれば pip install torch --index-url https://download.pytorch.org/whl/cu121 でCUDA版torchを入れておくと、Whisperが10倍以上速く動きます。


ステップ1:原曲をオフボーカル化する

ここはDemucsで2-stem分離します。本記事ではコード最小限に留めますが、「ボーカル除去だけ詳しく知りたい」方は別記事の歌ってみた用のオフボーカル音源をPythonで自作する方法に詳細ノウハウをまとめています。

# step1_separate.py
import subprocess
from pathlib import Path


def split_vocals_and_instrumental(input_path: str, out_dir: str = "stems") -> tuple[Path, Path]:
    """
    原曲を vocals.wav と no_vocals.wav に分離して両方のパスを返す。
    """
    Path(out_dir).mkdir(exist_ok=True)
    subprocess.run(
        ["demucs", "--two-stems", "vocals", "-n", "htdemucs_ft", "-o", out_dir, input_path],
        check=True,
    )
    stem = Path(input_path).stem
    base = Path(out_dir) / "htdemucs_ft" / stem
    return base / "vocals.wav", base / "no_vocals.wav"

vocals.wav は次の歌詞認識用、no_vocals.wav は最終的なカラオケ音源として使います。


ステップ2:Whisperで歌詞を文字起こしする

ここがこの記事の肝です。Whisperは音声認識モデルですが、歌唱にも意外と強いです。ただし「楽器に埋もれた歌」は苦手なので、ステップ1で抽出したvocals.wavを入力にすることで日本語歌詞でも実用精度になります。

# step2_transcribe.py
import whisper
from pathlib import Path
import json


def transcribe_lyrics(vocals_wav: Path, language: str = "ja") -> list[dict]:
    """
    ボーカル音声 → [{"start": 12.3, "end": 14.1, "text": "歌詞..."}, ...]
    """
    model = whisper.load_model("large-v3")
    result = model.transcribe(
        str(vocals_wav),
        language=language,
        word_timestamps=True,
        condition_on_previous_text=False,
        temperature=0.0,
        no_speech_threshold=0.4,
        logprob_threshold=-1.0,
    )

    segments = []
    for seg in result["segments"]:
        segments.append({
            "start": seg["start"],
            "end":   seg["end"],
            "text":  seg["text"].strip(),
            "words": seg.get("words", []),
        })
    return segments


if __name__ == "__main__":
    segs = transcribe_lyrics(Path("stems/htdemucs_ft/song/vocals.wav"))
    Path("lyrics.json").write_text(json.dumps(segs, ensure_ascii=False, indent=2))

パラメータの意味:

設定 効果
model="large-v3" 日本語精度が一段階上。CPUだと重いがGPU推奨
word_timestamps=True 単語単位の秒数を取得(カラオケの色変えに必須)
condition_on_previous_text=False サビが繰り返し出る曲で「同じ行の使い回し」を防ぐ
temperature=0.0 結果を決定論的に(毎回同じ)
no_speech_threshold=0.4 間奏部分を「歌なし」として正しく扱う

Whisperは「音楽中の歌唱」が公式トレーニングセットに少ないため、早口・シャウト・コーラス重ねには弱いです。バラードや歌唱がはっきりしている曲では8〜9割の精度、ロック/メタルでは6〜7割くらいの感覚です。


ステップ3:ASS字幕ファイルを生成(カラオケ風の色変えあり)

ASS(Advanced SubStation Alpha)字幕は、「歌っている単語だけ色が変わる」のカラオケ表現\k タグでサポートしています。これをPythonで自動生成します。

# step3_make_subtitles.py
from pathlib import Path
import json


ASS_HEADER = """[Script Info]
Title: Auto-generated Karaoke
ScriptType: v4.00+
PlayResX: 1920
PlayResY: 1080

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Karaoke,Noto Sans JP,72,&H00FFFFFF,&H0000A0FF,&H00000000,&H80000000,1,0,0,0,100,100,0,0,1,3,2,2,40,40,80,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
"""


def to_ass_time(t: float) -> str:
    h = int(t // 3600)
    m = int((t % 3600) // 60)
    s = t % 60
    return f"{h:d}:{m:02d}:{s:05.2f}"


def make_ass_subtitles(lyrics_json: Path, ass_path: Path) -> Path:
    """
    word_timestampsから「歌詞行ごとのカラオケ字幕」を作る。
    """
    segments = json.loads(lyrics_json.read_text())
    lines = [ASS_HEADER]

    for seg in segments:
        words = seg.get("words") or []
        if not words:
            continue

        karaoke_text = ""
        for w in words:
            duration_cs = max(1, int((w["end"] - w["start"]) * 100))
            text = w["word"].strip()
            karaoke_text += f"{{\\k{duration_cs}}}{text}"

        line = (
            f"Dialogue: 0,{to_ass_time(seg['start'])},{to_ass_time(seg['end'])},"
            f"Karaoke,,0,0,0,,{karaoke_text}"
        )
        lines.append(line)

    ass_path.write_text("\n".join(lines), encoding="utf-8")
    return ass_path

\k50 は「50センチ秒(0.5秒)かけてこの単語の色を変える」という指示です。これでサビでマイクを向けられても歌詞に追いつける正統派カラオケ表示が出ます。

フォントは Noto Sans JP を指定しています。日本語フォントが入っていない環境では事前に brew install --cask font-noto-sans-cjk-jp 等でインストールしておいてください。


ステップ4:ffmpegで動画化する

背景画像(1920x1080)+ オフボーカル音源 + 字幕焼き込み、をffmpegで合成します。

# step4_make_video.py
import subprocess
from pathlib import Path


def make_karaoke_video(
    instrumental_wav: Path,
    ass_subtitle: Path,
    background_png: Path,
    output_mp4: Path,
) -> Path:
    """
    オフボーカル + 字幕 + 背景 → カラオケmp4
    """
    cmd = [
        "ffmpeg", "-y",
        "-loop", "1", "-i", str(background_png),
        "-i", str(instrumental_wav),
        "-vf", f"ass={ass_subtitle}",
        "-c:v", "libx264", "-tune", "stillimage", "-pix_fmt", "yuv420p",
        "-c:a", "aac", "-b:a", "192k",
        "-shortest",
        str(output_mp4),
    ]
    subprocess.run(cmd, check=True)
    return output_mp4

背景画像は何でもOKですが、真っ黒よりは色味のあるグラデーションのほうが歌詞が読みやすいです。Pillowで自動生成しても良いですし、お気に入りの画像を1枚用意しておくのも手です。


全部つなげる:1コマンドで原曲 → カラオケ動画

# pipeline.py
from pathlib import Path
import json
from step1_separate import split_vocals_and_instrumental
from step2_transcribe import transcribe_lyrics
from step3_make_subtitles import make_ass_subtitles
from step4_make_video import make_karaoke_video


def make_karaoke(input_mp3: str, background: str, out_mp4: str) -> Path:
    print("[1/4] 音源分離中...")
    vocals, instrumental = split_vocals_and_instrumental(input_mp3)

    print("[2/4] 歌詞を文字起こし中(Whisper large-v3)...")
    segments = transcribe_lyrics(vocals)
    Path("lyrics.json").write_text(json.dumps(segments, ensure_ascii=False, indent=2))

    print("[3/4] カラオケ字幕(.ass)を生成中...")
    ass_path = make_ass_subtitles(Path("lyrics.json"), Path("subtitles.ass"))

    print("[4/4] 動画を書き出し中...")
    return make_karaoke_video(
        instrumental, ass_path, Path(background), Path(out_mp4),
    )


if __name__ == "__main__":
    out = make_karaoke("song.mp3", "background.png", "karaoke.mp4")
    print(f"\n完成: {out}")

実行例:

$ python pipeline.py
[1/4] 音源分離中...
[2/4] 歌詞を文字起こし中(Whisper large-v3)...
[3/4] カラオケ字幕(.ass)を生成中...
[4/4] 動画を書き出し中...

完成: karaoke.mp4

実測:4分の楽曲で約8〜12分(M2 Mac、CPU)。GPUがあれば3〜4分で完成します。


ノーコードで済ませたい場合

「コード書く時間も惜しい」「友達のために5本だけサクッと作りたい」場合は、ブラウザだけで完結するStemSplitのカラオケジェネレータを使うのが速いです。同じHTDemucsモデルを内部で使っているので、品質は本記事のローカル版と同等です。実装の細かい仕組みはカラオケ作成のユースケース解説も参考になります。

このパイプラインを「自宅サーバーに置いて、家族が好きな曲を投げると勝手にカラオケmp4が生える」みたいな構成にする場合は、Web UI部分だけStemSplit APIに任せて、ローカルでは管理だけする、みたいな分担もアリです。


ユースケース別の活用パターン

こんなとき やり方
自宅カラオケでマイナー曲を歌いたい このパイプライン1発、テレビにHDMI出力
歌ってみた録音前に歌詞を確認したい 歌ってみた用オフボーカル記事と組み合わせて、練習用にも本番用にも
VTuber配信で歌詞テロップ付きの歌枠をやりたい このカラオケmp4をOBSのMedia Sourceに登録、歌枠記事のセトリ自動切り替えと組み合わせ
歌う前にキー・音域を確認したい 耳コピAI記事でメロディーをMIDI化して音域チェック
忘年会・結婚式の余興用 背景画像を写真にしてエモい仕様にすると盛り上がります

特にVTuberの歌枠で「歌詞テロップ付きの本格カラオケ配信」をやりたい場合、このカラオケmp4 + 歌枠記事の自動切替えの組み合わせで、配信中の操作はチャットコマンドだけになります。


ハマりポイントと対処

症状 原因 対処
歌詞が全然認識されない 楽曲のままWhisperに渡している 必ずDemucsでvocals.wavを抽出してから入力
早口部分の歌詞がズレる Whisperの単語タイムスタンプ精度限界 model="large-v3" 必須、ベンチマーク的に最高
英語歌詞がカタカナで出る language="ja" を強制している バイリンガル曲は language=None(自動判定)にする
同じサビが何回も同じテキストになる condition_on_previous_text がオン 上記コードのように False にする
字幕の日本語が□(豆腐)になる フォント未インストール Noto Sans JP / Source Han Sans を入れる
動画の音がズレる mp4のmoovが先頭にない ffmpeg ... -movflags +faststart を追加

特に「歌詞が認識されない」問題は、ボーカル抽出をスキップして原曲を直接Whisperに渡しているケースがほとんどです。必ず分離 → 認識の順を守ってください。


関連記事


まとめ

  • 音源分離 → Whisper → ASS字幕 → ffmpeg で、原曲から歌詞付きカラオケ動画が全自動で作れます
  • 歌詞認識精度の鍵は「ボーカルだけを取り出してからWhisperに渡す」こと
  • ASSの \k タグを使えばカラオケ機の「歌っている部分が色変わり」が再現できます
  • ノーコードで済ませたい場合はStemSplitのカラオケジェネレータで同じことができます
  • 公開・配信には著作権処理が別途必要です

JOYSOUND/DAMにない曲を諦めていた人にも、自分専用カラオケ動画をコーヒー1杯ぶんの時間で作れる時代になりました。週末プロジェクトにぜひどうぞ。

参考リンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?