Полный проект с нуля: клон голоса на Python

🔊 Клонирование голоса на Python с нуля: в этой статье вы найдете – пошаговый разбор задачи, рабочий код, проверка сходства и защита с водяными знаками. Полное руководство для разработчиков по созданию собственного проекта voice cloning.

Прежде чем начать: клонируйте только свой голос или голос человека, давшего письменное согласие. В примерах ниже я добавляю автоматическую проверку согласия и по умолчанию ставлю водяной знак в сгенерированное аудио.

t.me/pythonl – в моем тг канале сотни разобранных скриптов и статей.

Что строим:

  • Zero-shot TTS на базе XTTS-v2 (нужен 6-секундный эталон и сразу говорим «его» голосом). Hugging Face
  • Метрика «насколько голос похож» через эмбеддинги SpeechBrain ECAPA-TDNNHugging Facespeechbrain.readthedocs.io
  • Опционально: голосовая конверсия (FreeVC) для «переговорить» готовую дорожку чужим тембром. GitHub
  • Водяной знак в аудио (WavMark), чтобы помечать синтез как ИИ-контент. Для production присмотритесь к AudioSeal (Meta). PyPIarXivGitHub

1) Установка и подготовка окружения

Python 3.10–3.12, CUDA (если есть GPU), FFmpeg.

# Базовые зависимости
python -m venv .venv && source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install --upgrade pip

# Core
pip install TTS torch torchaudio librosa soundfile numpy pydub

# Метрика сходства (предобученный спикер-энкодер)
pip install speechbrain

# Водяной знак
pip install wavmark

# Для конвертации и чтения медиа может понадобиться ffmpeg:
# macOS:  brew install ffmpeg
# Ubuntu: sudo apt-get update && sudo apt-get install -y ffmpeg

XTTS-v2 работает на 24 кГц, поддерживает много языков и реально умеет 6-секундный клон. Hugging Face


2) Структура проекта

voice-clone/
  data/
    ref.wav                      # эталонный голос (ваш)
    CONSENT.txt                  # файл-согласие (любой текст)
  out/
  clone_tts.py                   # zero-shot TTS + водяной знак
  check_similarity.py            # метрика похожести (ECAPA)
  vc_convert.py                  # voice conversion (FreeVC)

3) Zero-shot клон (XTTS-v2) с безопасными «перилами»

Код ниже:

  • проверяет файл CONSENT.txt рядом с эталоном (или принимает --authorized),
  • синтезирует фразу в клон-голос,
  • по умолчанию добавляет водяной знак (WavMark).
    API соответствует документации Coqui TTS: модель tts_models/multilingual/multi-dataset/xtts_v2, вызов tts_to_file(...)GitHubHugging Face
# clone_tts.py
import argparse
import os
import tempfile
import numpy as np
import torch
import soundfile as sf
import librosa

from TTS.api import TTS   # XTTS-v2
# Водяной знак
import wavmark

def ensure_mono_16k(y, sr):
    if y.ndim == 2:  # stereo -> mono
        y = np.mean(y, axis=1)
    if sr != 16000:
        y = librosa.resample(y, orig_sr=sr, target_sr=16000)
        sr = 16000
    return y, sr

def add_watermark(in_wav_path, out_wav_path, payload_bits=16):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model = wavmark.load_model().to(device)

    # читаем исходный WAV, приводим к моно 16кГц (формат, ожидаемый wavmark)
    y, sr = sf.read(in_wav_path, always_2d=False)
    y16, _ = ensure_mono_16k(y, sr)

    # 16-битный пэйлоад (можно заменить на свой ID)
    payload = np.random.choice([0, 1], size=payload_bits)

    # кодирование водяного знака
    y_wm, _ = wavmark.encode_watermark(model, y16, payload, show_progress=False)

    # сохраняем сэмплинг 16кГц (для корректного детектирования)
    sf.write(out_wav_path, y_wm, 16000)

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--ref", required=True, help="Путь к эталону голоса (wav)")
    ap.add_argument("--text", required=True, help="Текст для синтеза")
    ap.add_argument("--lang", default="ru", help="Язык (например, ru, en, de ...)")
    ap.add_argument("--out", default="out/clone.wav", help="Файл вывода")
    ap.add_argument("--authorized", action="store_true",
                    help="Подтверждаю, что имею право клонировать этот голос")
    ap.add_argument("--no-watermark", action="store_true",
                    help="Не добавлять водяной знак (не рекомендуется)")
    args = ap.parse_args()

    # простая проверка согласия
    consent_file = os.path.join(os.path.dirname(args.ref), "CONSENT.txt")
    if not args.authorized and not os.path.exists(consent_file):
        raise SystemExit(
            "❌ Нет подтверждения. Добавьте data/CONSENT.txt рядом с эталоном "
            "или запустите с флагом --authorized."
        )

    os.makedirs(os.path.dirname(args.out), exist_ok=True)

    device = "cuda" if torch.cuda.is_available() else "cpu"
    tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device)

    # временный файл без водяного знака
    with tempfile.TemporaryDirectory() as td:
        tmp = os.path.join(td, "tmp.wav")
        tts.tts_to_file(
            text=args.text,
            speaker_wav=args.ref,
            language=args.lang,
            file_path=tmp
        )

        if args.no_watermark:
            # сохраняем как есть (24кГц от XTTS-v2)
            y, sr = sf.read(tmp)
            sf.write(args.out, y, sr)
        else:
            # добавляем водяной знак (итог будет 16кГц)
            add_watermark(tmp, args.out)

    print(f"✅ Готово: {args.out}")

if __name__ == "__main__":
    main()

Пример запуска:

python clone_tts.py --ref data/ref.wav --text "Это тест клонирования моего голоса." --lang ru --out out/clone_ru.wav

4) Оценка сходства голосов (сколько «похоже»)

Извлечём эмбеддинги спикера через SpeechBrain ECAPA и посчитаем косинусное сходство. Значения ~0.75–0.9 обычно выглядят «похожими», но порог зависит от данных. Hugging Facespeechbrain.readthedocs.io

# check_similarity.py
import argparse
import torch
import numpy as np
import torchaudio
from speechbrain.pretrained import EncoderClassifier
import torch.nn.functional as F

def load_wav_mono16k(path):
    wav, sr = torchaudio.load(path)
    if wav.shape[0] > 1:  # stereo -> mono
        wav = torch.mean(wav, dim=0, keepdim=True)
    if sr != 16000:
        wav = torchaudio.functional.resample(wav, orig_freq=sr, new_freq=16000)
        sr = 16000
    return wav, sr

def spk_embed(model, path):
    wav, _ = load_wav_mono16k(path)
    with torch.no_grad():
        emb = model.encode_batch(wav)
        emb = torch.mean(emb, dim=1)  # [1, D]
    return emb.squeeze(0)  # [D]

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--ref", required=True)
    ap.add_argument("--test", required=True)
    args = ap.parse_args()

    model = EncoderClassifier.from_hparams(
        source="speechbrain/spkrec-ecapa-voxceleb",
        run_opts={"device": "cuda" if torch.cuda.is_available() else "cpu"},
        savedir="pretrained_models/spkrec-ecapa-voxceleb"
    )

    ref_emb = spk_embed(model, args.ref)
    test_emb = spk_embed(model, args.test)

    sim = F.cosine_similarity(ref_emb, test_emb, dim=0).item()
    print(f"Cosine similarity: {sim:.4f}")

if __name__ == "__main__":
    main()

Пример:

python check_similarity.py --ref data/ref.wav --test out/clone_ru.wav
# Cosine similarity: 0.82

5) Voice Conversion (FreeVC): «переговорить» готовый файл в нужный тембр

Если у вас есть исходная речь (source.wav) и эталон голоса (target.wav), можно поменять только тембр. В Coqui TTS это делается одной строкой через модель voice_conversion_models/multilingual/vctk/freevc24GitHub

# vc_convert.py
import argparse, os, torch
from TTS.api import TTS

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--source", required=True, help="Оригинальная речь (wav)")
    ap.add_argument("--target", required=True, help="Эталонный голос (wav)")
    ap.add_argument("--out", default="out/vc.wav")
    args = ap.parse_args()

    os.makedirs(os.path.dirname(args.out), exist_ok=True)

    tts = TTS("voice_conversion_models/multilingual/vctk/freevc24").to(
        "cuda" if torch.cuda.is_available() else "cpu"
    )
    tts.voice_conversion_to_file(
        source_wav=args.source,
        target_wav=args.target,
        file_path=args.out
    )
    print(f"✅ VC готово: {args.out}")

if __name__ == "__main__":
    main()

6) Подготовка эталона и данных

Практические советы:

  • Эталон 6–20 сек: чистая речь без музыки, шумов, эха, лучше 24 кГц под XTTS-v2. Hugging Face
  • Для стабильности сделайте 2–3 эталона (разный темп/эмоция) и выберите лучший на слух.
  • Для длинных текстов делайте разбиение на фразы 6–12 сек.
  • Хотите ещё точнее — fine-tuning на ваших данных в Coqui TTS (есть рецепты и обсуждения, zero-shot ≠ идеал). GitHub+1

7) Транскрибация чужих записей для «пересказа» вашим голосом

Иногда нужно взять длинный ролик, получить текст, потом начитать «вашим» тембром. Для транскрибации удобно использовать faster-whisper (в 3–4 раза быстрее оригинального Whisper при той же точности). GitHub

Мини-пример:

pip install faster-whisper
# transcribe.py
from faster_whisper import WhisperModel

model = WhisperModel("large-v3")  # автоматически скачает веса
segments, info = model.transcribe("input.wav", beam_size=5, vad_filter=True)
with open("out/transcript.txt","w", encoding="utf-8") as f:
    for seg in segments:
        f.write(seg.text.strip() + "\n")

Далее прочитайте transcript.txt по абзацам и подайте строки в clone_tts.py.


8) Параметры качества и производительности

  • GPU: желательно (FP16), CPU тоже работает, но медленнее.
  • Язык: --lang должен совпадать с целевым (список языков поддерживается XTTS-v2). Hugging Face
  • Детекция сходства: не гонитесь за «1.0». Важнее субъективное восприятие и стабильность произношения.
  • Водяной знак: я сохраняю итог в 16 кГц для корректного детекта WavMark. В проде можно держать две версии: «мастер-24кГц» и «дистрибутив-16кГц с WM». Для продвинутых задач изучите AudioSeal от Meta (локализуемый детект). PyPIarXiv

9) Частые проблемы

  • «Шипение/металличность»: проверьте чистоту эталона, попробуйте другой клип.
  • «Слова на другом языке звучат хуже»: укажите корректный --lang, разбейте текст на более короткие фразы. Hugging Face
  • «На Windows не ставится ffmpeg/torch»: установите Visual C++ Redistributable, поставьте ffmpeg из официального сборщика или через пакетный менеджер.

10) Этика и право

  • Получайте явное согласие и храните его вместе с эталонным клипом (CONSENT.txt).
  • Помечайте результат водяным знаком и/или метаданными. Некоторые юрисдикции уже требуют маркировку ИИ-контента (см. обзоры по правовому регулированию). arXiv
  • Не используйте клон-голос для введения в заблуждение, фишинга, имперсонации и т. п.

Полезные источники

  • Coqui XTTS-v2: модель + код примеров, 6-сек. клон, 24 кГц. Hugging Face
  • Coqui TTS API (Python-примеры XTTS и FreeVC): GitHub
  • SpeechBrain ECAPA (эмбеддинги спикера): Hugging Facespeechbrain.readthedocs.io
  • WavMark (водяной знак, Python/PyPI + пример кода): PyPI
  • AudioSeal (локализуемый детект ИИ-аудио, Meta, статья/репо): arXivGitHub
  • faster-whisper (быстрая транскрибация): GitHub

+1
0
+1
5
+1
1
+1
0
+1
1

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *