0
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音源分離・2026年版】

0
Posted at

「歌ってみたを録りたいけど、公式のオフボーカル音源が配布されていない」――一度はぶつかる壁だと思います。

ピアプロやニコニ・コモンズに上がっていれば最高ですが、現実には原曲しか手に入らないケースのほうが多いです。最近はAIによる音源分離(source separation)の精度が一気に上がり、自宅のPCでも数分で「ほぼ違和感のないオフボーカル」が作れるようになりました。

この記事では、Pythonを使って原曲からオフボーカル(カラオケ音源)を自作する方法を、ローカル実行・API利用の両方で解説します。

著作権について:自分で歌ってみた動画を投稿する場合、原盤権・著作権の処理(JASRACへの申請、ニコニコ動画のクリエイター奨励プログラム等)は別途必要です。本記事はあくまで「自分の練習用、または正規にライセンス取得済みの楽曲」を前提としています。

この記事でわかること

  • ✅ Demucs(無料・ローカル)でオフボーカル音源を作る最短手順
  • ✅ GPUがないPCでも実用速度で動かす方法
  • ✅ 商用APIでバッチ処理を回す方法(StemSplit)
  • ✅ 「サビでうっすらボーカルが残る」問題の対処法
  • ✅ 歌ってみた向けに音圧・EQを整える後処理スクリプト

前提条件

  • Python 3.10以上
  • ffmpeg(mp3/m4a対応のため必須)
  • 8GB以上のRAM(GPU無しでも動きます)
# macOS
brew install ffmpeg
# Ubuntu
sudo apt install ffmpeg
# Windows
winget install ffmpeg

Pythonライブラリをまとめてインストールします。

pip install demucs torch torchaudio soundfile pydub requests python-dotenv

方法1:Demucsでローカル分離(無料・オフライン)

Demucs はMeta(旧Facebook AI Research)が公開している音源分離モデルで、現在の事実上のスタンダードです。htdemucs_ft という派生モデルが特にボーカル分離精度が高く、歌ってみた用途には十分すぎる品質が出ます。

最短コード

# separate.py
import subprocess
from pathlib import Path


def make_off_vocal(input_path: str, output_dir: str = "out") -> Path:
    """
    Demucsで原曲からオフボーカル(no_vocals.wav)を作成する。
    """
    Path(output_dir).mkdir(exist_ok=True)
    subprocess.run(
        [
            "demucs",
            "--two-stems", "vocals",  # ボーカル vs それ以外、の2分割
            "-n", "htdemucs_ft",       # 高精度モデル
            "-o", output_dir,
            input_path,
        ],
        check=True,
    )
    stem = Path(input_path).stem
    return Path(output_dir) / "htdemucs_ft" / stem / "no_vocals.wav"


if __name__ == "__main__":
    off_vocal = make_off_vocal("song.mp3")
    print(f"オフボーカル生成完了: {off_vocal}")

--two-stems vocals を指定すると、ドラム・ベース・その他に細かく分けず**「ボーカル」と「それ以外」の2つだけ**を出力します。歌ってみた用途ならこれで十分で、処理時間も半分以下になります。

CPU only で実行する場合の体感速度

マシン 4分の楽曲 備考
MacBook Air M2 約45秒 MPS自動利用
Intel Core i7(GPU無し) 約3分30秒 CPU推論
RTX 3060 (12GB) 約12秒 CUDA

iPhoneアプリのように「秒で終わる」までは行きませんが、寝る前に10曲投げて朝起きたら全部できているくらいの速度感です。

サビで微妙にボーカルが残る場合

歌ってみた界隈で一番嫌われる「うっすら原曲ボーカルが聴こえる」現象、Demucsでも完全には消えません。htdemucs_ft で出ない場合は、シフト推論(shifts) を増やすと改善することが多いです。

subprocess.run([
    "demucs",
    "--two-stems", "vocals",
    "-n", "htdemucs_ft",
    "--shifts", "5",   # 推論を5回行って平均化(時間は約5倍)
    "-o", "out",
    input_path,
])

--shifts 5 で約3〜5dBほどS/Nが改善する印象です。サビのような密度の高いセクションで効果が出やすいです。


方法2:StemSplit APIで分離(高速・GPU不要)

ローカルで毎回数分待つのは正直しんどいので、量産するならAPIに投げてしまうのが楽です。私はStemSplitのAIボーカルリムーバーを使っています。HTDemucsベースで、無料枠が5分ぶん(カード登録不要)あるので動作確認には十分です。

# stemsplit_client.py
import os
import time
import requests
from pathlib import Path
from dotenv import load_dotenv

load_dotenv()
API_BASE = "https://api.stemsplit.io/v1"
HEADERS  = {"Authorization": f"Bearer {os.environ['STEMSPLIT_API_KEY']}"}


def upload_and_separate(file_path: str) -> str:
    """ファイルをアップロードしてjob_idを返す。"""
    with open(file_path, "rb") as f:
        res = requests.post(
            f"{API_BASE}/jobs",
            headers=HEADERS,
            files={"file": f},
            data={"stems": "vocals"},  # 2-stem mode
        )
    res.raise_for_status()
    return res.json()["job_id"]


def wait_for_completion(job_id: str, timeout: int = 300) -> dict:
    """ジョブが完了するまでポーリング。"""
    start = time.time()
    while time.time() - start < timeout:
        res = requests.get(f"{API_BASE}/jobs/{job_id}", headers=HEADERS)
        res.raise_for_status()
        data = res.json()
        if data["status"] == "completed":
            return data
        if data["status"] == "failed":
            raise RuntimeError(f"分離に失敗しました: {data.get('error')}")
        time.sleep(3)
    raise TimeoutError("タイムアウトしました")


def download(url: str, out_path: str) -> Path:
    res = requests.get(url, stream=True)
    res.raise_for_status()
    out = Path(out_path)
    with open(out, "wb") as f:
        for chunk in res.iter_content(8192):
            f.write(chunk)
    return out


if __name__ == "__main__":
    job_id = upload_and_separate("song.mp3")
    print(f"job_id={job_id}")
    result = wait_for_completion(job_id)
    download(result["stems"]["instrumental"], "off_vocal.wav")
    print("完了: off_vocal.wav")

実測で4分の楽曲が40秒前後で返ってきます。Demucsをローカルで回すよりも約4〜5倍速い計算です。CIやServerlessから呼ぶ場合はGPUを用意しなくていいのが大きいです。


歌ってみた用の後処理:音圧とEQを整える

「分離したオフボーカルをそのまま使うと音が小さい/こもって聞こえる」――これは歌ってみた界隈でよくある相談です。AI分離は副作用として音圧が下がるので、pydub で軽くマスタリング寄りの調整をかけるとぐっと聴きやすくなります。

# postprocess.py
from pydub import AudioSegment
from pydub.effects import normalize, low_pass_filter, high_pass_filter


def polish_off_vocal(input_path: str, output_path: str, target_dbfs: float = -14.0) -> None:
    """
    歌ってみた向けにオフボーカルを整える:
    1. ハイパスで分離残響の低音ノイズをカット
    2. ローパスで超高域のシュワシュワ感を抑制
    3. 配信向けのラウドネス(-14 LUFSをざっくり再現)にノーマライズ
    """
    audio = AudioSegment.from_file(input_path)
    audio = high_pass_filter(audio, cutoff=40)
    audio = low_pass_filter(audio, cutoff=18000)
    audio = normalize(audio)

    gain = target_dbfs - audio.dBFS
    audio = audio.apply_gain(gain)

    audio.export(output_path, format="wav")


if __name__ == "__main__":
    polish_off_vocal("off_vocal.wav", "off_vocal_polished.wav")

ニコニコ動画やYouTubeに投稿する場合のラウドネスターゲットは -14 LUFS あたりが標準です。厳密なLUFS計測がしたい場合は pyloudnorm を使ってください。


バッチ処理:プレイリスト一括変換

ライブ前にカバー曲を10曲練習したい、というときのために並列処理も置いておきます。Demucsはモデル読み込みが重いので、API側を並列化したほうが効率的です。

# batch.py
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from stemsplit_client import upload_and_separate, wait_for_completion, download


def process_one(mp3_path: Path) -> Path:
    job_id = upload_and_separate(str(mp3_path))
    result = wait_for_completion(job_id)
    out = mp3_path.with_name(f"{mp3_path.stem}_off_vocal.wav")
    return download(result["stems"]["instrumental"], str(out))


def batch_convert(folder: str, max_workers: int = 4) -> list[Path]:
    files = list(Path(folder).glob("*.mp3"))
    results = []
    with ThreadPoolExecutor(max_workers=max_workers) as ex:
        futures = {ex.submit(process_one, f): f for f in files}
        for fut in as_completed(futures):
            try:
                results.append(fut.result())
                print(f"{futures[fut].name}")
            except Exception as e:
                print(f"{futures[fut].name}: {e}")
    return results


if __name__ == "__main__":
    batch_convert("./songs", max_workers=4)

max_workers=4 程度に抑えておくのが無難です。APIにレートリミットがかかると逆に遅くなります。


よくあるトラブルと対処

症状 原因 対処
ffmpeg not found で落ちる mp3デコーダが無い ffmpegをOSにインストール
サビでボーカルが残る モデルが密度の高い音に弱い --shifts 5 を付ける/htdemucs_ftを使う
出力がモノラルになる 入力がモノラル 元ファイルがモノなら避けられない
音が小さい 分離による音圧低下 上記の polish_off_vocal でノーマライズ
24bit WAVが読めない torchaudioのバックエンド問題 soundfile をインストール

特に「サビで残る」現象は、原曲のミックスがステレオ広がりに頼っている曲(最近のJ-POP・アニソンに多い)で起きやすいです。--shifts を増やすか、後段でセンターチャンネルの-3dBカットを入れると目立たなくなります。


どっちを選ぶべきか

ざっくりした使い分けの目安です。

用途 おすすめ
自分用に1〜2曲、たまに作る Demucs(ローカル)
毎週新曲をカバーする・練習用に量産 StemSplit API
サークル/コミュニティで共有サービスを立てたい API + Flask等でラップ
GPUを持っていない・CIから呼びたい API一択

私自身は普段はAPI、こだわって追い込みたい曲だけDemucsで --shifts 10 を回す、という使い分けをしています。


まとめ

  • AI音源分離のおかげで、原曲からオフボーカルを作るのは5分かからない作業になりました
  • Demucsはローカルで完結するが、量産するならAPIのほうが圧倒的に楽
  • 後処理(ハイパス・ノーマライズ)まで含めて自動化すると「ファイルを投げるだけで歌える」状態が作れます
  • 投稿用途では必ずJASRAC等の権利処理を別途行ってください

歌ってみたの「音源探しに時間を取られて練習時間が削れる」問題、ぜひ解消してみてください。コードは全部コピペで動くようにしてあります。

参考リンク

0
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
0
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?