Huggingface_Hack / test_modules /test_qwen3_tts_clone.py
minhahwang's picture
feat: implement real voice cloning with Qwen3-TTS (sprint step 3)
215fff0
Raw
History Blame Contribute Delete
3.74 kB
"""
Test Qwen3-TTS voice cloning: load model, extract voice from reference audio, synthesize.
Usage: python test_modules/test_qwen3_tts_clone.py
"""
import sys
import time
import numpy as np
import soundfile as sf
print("=" * 60)
print("Testing Qwen3-TTS Voice Cloning")
print("=" * 60)
# Generate a synthetic reference audio (sine wave simulating speech duration)
# In production this would be the parent's recorded voice
print("Creating synthetic reference audio for testing...")
sr = 16000
duration = 5 # 5 seconds
t = np.linspace(0, duration, sr * duration, dtype=np.float32)
# Simple multi-frequency signal (not real speech, but tests the pipeline)
ref_audio = 0.3 * np.sin(2 * np.pi * 200 * t) + 0.2 * np.sin(2 * np.pi * 400 * t)
ref_audio_path = "sample_sounds/test_ref_audio.wav"
sf.write(ref_audio_path, ref_audio, sr)
print(f"[OK] Reference audio created: {ref_audio_path} ({duration}s)")
print()
print("Loading Qwen3-TTS model (this downloads ~1.7GB on first run)...")
start = time.time()
try:
import torch
from qwen_tts import Qwen3TTSModel
model = Qwen3TTSModel.from_pretrained(
"Qwen/Qwen3-TTS-12Hz-1.7B-Base",
device_map="cuda" if torch.cuda.is_available() else "cpu",
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
)
elapsed = time.time() - start
print(f"[OK] Qwen3-TTS loaded in {elapsed:.1f}s")
except Exception as e:
print(f"[FAIL] Model loading failed: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
# Test 1: Voice cloning with x_vector_only_mode (speaker embedding only, no ref_text needed)
print()
print("Test 1: Voice clone with x_vector_only_mode=True...")
try:
start = time.time()
audio_list, sample_rate = model.generate_voice_clone(
text="Hello! I am reading a bedtime story for you tonight.",
language="en",
ref_audio=ref_audio_path,
x_vector_only_mode=True,
)
elapsed = time.time() - start
total_samples = sum(len(a) for a in audio_list)
duration_out = total_samples / sample_rate
print(f"[OK] Voice clone (x_vector) in {elapsed:.1f}s, output: {duration_out:.1f}s at {sample_rate}Hz")
# Save output
output = np.concatenate(audio_list)
sf.write("sample_sounds/test_clone_xvector.wav", output, sample_rate)
print(f"[OK] Saved to sample_sounds/test_clone_xvector.wav")
except Exception as e:
print(f"[FAIL] x_vector clone failed: {e}")
import traceback
traceback.print_exc()
# Test 2: Create and reuse voice clone prompt (for caching)
print()
print("Test 2: Create reusable voice clone prompt...")
try:
start = time.time()
prompt_items = model.create_voice_clone_prompt(
ref_audio=ref_audio_path,
x_vector_only_mode=True,
)
elapsed = time.time() - start
print(f"[OK] Voice clone prompt created in {elapsed:.1f}s")
# Reuse cached prompt for synthesis
start = time.time()
audio_list2, sr2 = model.generate_voice_clone(
text="Once upon a time, there was a little rabbit named Peter.",
language="en",
voice_clone_prompt=prompt_items,
)
elapsed = time.time() - start
total_samples2 = sum(len(a) for a in audio_list2)
duration_out2 = total_samples2 / sr2
print(f"[OK] Synthesis from cached prompt in {elapsed:.1f}s, output: {duration_out2:.1f}s")
output2 = np.concatenate(audio_list2)
sf.write("sample_sounds/test_clone_cached.wav", output2, sr2)
print(f"[OK] Saved to sample_sounds/test_clone_cached.wav")
except Exception as e:
print(f"[FAIL] Cached prompt failed: {e}")
import traceback
traceback.print_exc()
print()
print("=" * 60)
print("[OK] Qwen3-TTS voice cloning tests complete")
print("=" * 60)