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

🔊 Клонирование голоса на Python с нуля: в этой статье вы найдете – пошаговый разбор задачи, рабочий код, проверка сходства и защита с водяными знаками. Полное руководство для разработчиков по созданию собственного проекта voice cloning.
Прежде чем начать: клонируйте только свой голос или голос человека, давшего письменное согласие. В примерах ниже я добавляю автоматическую проверку согласия и по умолчанию ставлю водяной знак в сгенерированное аудио.
t.me/pythonl – в моем тг канале сотни разобранных скриптов и статей.
Что строим:
- Zero-shot TTS на базе XTTS-v2 (нужен 6-секундный эталон и сразу говорим «его» голосом). Hugging Face
- Метрика «насколько голос похож» через эмбеддинги SpeechBrain ECAPA-TDNN. Hugging 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/freevc24. GitHub
# 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



