kazeia/scripts/generate_tts_wav.py

57 lines
1.8 KiB
Python

#!/usr/bin/env python3
"""Generate a WAV file using Qwen3-TTS with Damien's voice cloning."""
import sys
sys.path.insert(0, "/opt/Kazeia/qnn_venv/lib/python3.10/site-packages")
import warnings; warnings.filterwarnings("ignore")
from qwen_tts import Qwen3TTSModel
import soundfile as sf
import numpy as np
MODEL = "/home/alf/.cache/huggingface/hub/models--Qwen--Qwen3-TTS-12Hz-0.6B-Base/snapshots/5d83992436eae1d760afd27aff78a71d676296fc"
VOICE = "/opt/Kazeia/voix/damien_15s_24k.wav"
TEXT = "Bonjour, je m'appelle Kazeia, je suis encore en phase de développement."
OUTPUT = "/opt/Kazeia/kazeia_damien_pc.wav"
print("Loading model...")
tts = Qwen3TTSModel.from_pretrained(MODEL, local_files_only=True, device_map="cpu")
# Also generate phrase_embeds.bin for tablet use
import torch, struct
tokenizer = tts.processor.tokenizer
talker = tts.model.talker
ids = tokenizer.encode(TEXT, add_special_tokens=False)
print(f"Tokens ({len(ids)}): {ids}")
with torch.no_grad():
raw = talker.model.text_embedding(torch.tensor(ids))
projected = talker.text_projection(raw)
with open("/tmp/phrase_embeds.bin", "wb") as f:
f.write(struct.pack("<i", len(ids)))
for i in range(len(ids)):
f.write(projected[i].numpy().astype(np.float32).tobytes())
print(f"phrase_embeds.bin: {len(ids)} tokens saved")
# Generate speech
print(f"Generating: '{TEXT}'")
print(f"Voice: {VOICE}")
audio_list, sr = tts.generate_voice_clone(
text=TEXT,
ref_audio=VOICE,
language="french",
x_vector_only_mode=True,
non_streaming_mode=True,
)
audio = audio_list[0]
print(f"Audio: {len(audio)} samples, {len(audio)/sr:.2f}s, SR={sr}")
# Check audio is not silent
rms = np.sqrt(np.mean(audio**2))
print(f"RMS: {rms:.4f} (should be > 0.01)")
sf.write(OUTPUT, audio, sr)
print(f"Saved: {OUTPUT}")