Spaces:
Paused
Paused
| """ | |
| 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) | |