k-washi/speaker-emb-ja-ecapa-tdnnを使って似ている歌声ボーカルを探す

初めに

以下の記事で Resemblyzerを使って似ている歌声を探してみました。今回は 別のモデルを使ってみます

ayousanz.hatenadiary.jp

開発環境

環境構築

uv venv .venv -p 3.12
.venv/Scripts/activate  # Windowsの場合
uv pip install speechbrain torch torchaudio librosa numpy scikit-learn gdown
uv pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu121 --forc

音声データの取得

以下の記事を参考にしてください。

流れは以下になります 1. youtube等から音源の取得 2. uvr等でボーカルのみを抽出

ayousanz.hatenadiary.jp

ayousanz.hatenadiary.jp

speaker-emb-ja-ecapa-tdnnを使って音声類似度比較を行う

以下を実行して似ているボーカルを探します

import torchaudio
from pathlib import Path
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
import torch
import torch.nn.functional as F

def get_embedding_speechbrain(audio_path_str, model):
    """指定された音声ファイルからECAPA-TDNNを使って埋め込みを抽出する"""
    fpath = Path(audio_path_str)
    if not fpath.exists():
        print(f"エラー: 音声ファイルが見つかりません - {fpath}")
        return None

    try:
        wave, sr = torchaudio.load(fpath)
        # モデルのサンプルレートにリサンプリング
        if sr != model.sample_rate:
            wave = torchaudio.transforms.Resample(sr, model.sample_rate)(wave)
        # 埋め込み抽出
        with torch.no_grad():
            emb = model.extract_embedding(wave)
            emb = F.normalize(torch.FloatTensor(emb), p=2, dim=1).detach().cpu()
        return emb.squeeze().numpy()
    except Exception as e:
        print(f"エラー: {fpath} の処理中にエラーが発生しました: {e}")
        import traceback
        traceback.print_exc()
        return None

def main():
    try:
        print("ECAPA-TDNNモデルをロード中... (初回は時間がかかることがあります)")
        model = torch.hub.load("k-washi/speaker-emb-ja-ecapa-tdnn", "ecapatdnn_ja_l512_va", trust_repo=True, pretrained=True)
        # モデルを推論モードに設定
        model.model.eval()
        print("モデルのロード完了。")
    except Exception as e:
        print(f"モデルの初期化に失敗しました: {e}")
        print("モデル名が正しいか、インターネット接続、torchのバージョンなどを確認してください。")
        import traceback
        traceback.print_exc()
        return

    # --- 音声ファイルのパスを指定 ---
    target_audio_path = Path("vocal_target.wav")
    candidate_audio_paths = [
        Path("data/vocal_test.wav") ]

    print(f"ターゲット音声: {target_audio_path}")
    print("候補音声リスト:")
    for p in candidate_audio_paths:
        print(f"- {p}")
    print("-" * 30)


    # ターゲット音声の埋め込みを抽出
    target_embedding = get_embedding_speechbrain(target_audio_path, model)

    if target_embedding is None:
        print("ターゲット音声の埋め込みが抽出できませんでした。処理を終了します。")
        return

    # 候補音声の埋め込みを抽出し、類似度を計算
    similarities = []
    for cand_path in candidate_audio_paths:
        print(f"\n候補音声 {cand_path} の処理中...")
        cand_embedding = get_embedding_speechbrain(cand_path, model)
        if cand_embedding is not None:
            similarity = cosine_similarity(target_embedding.reshape(1, -1), cand_embedding.reshape(1, -1))[0][0]
            similarities.append((str(cand_path), similarity))
            print(f"  類似度: {similarity:.4f}")
        else:
            print(f"  {cand_path} の埋め込み抽出に失敗しました。")


    similarities.sort(key=lambda x: x[1], reverse=True)

    print("\n--- 類似度ランキング (k-washi/speaker-emb-ja-ecapa-tdnn) ---")
    for path_str, score in similarities:
        print(f"{path_str}: {score:.4f}")

    if similarities:
        print(f"\n最も近いと思われる音声: {similarities[0][0]} (類似度: {similarities[0][1]:.4f})")
    else:
        print("\n類似度を計算できる候補がありませんでした。")

if __name__ == "__main__":
    main()

実行すると以下になります

最も近いと思われる音声: data\vocal_target.wav (類似度: 0.8525)