#!/usr/bin/env python3 """ Test script for Qwen3-TTS voice synthesis. Tests the TTS server with character voice prompts. """ import sys import time from pathlib import Path # Add parent directory to path sys.path.insert(0, str(Path(__file__).parent.parent)) from umbra_audio.core.tts_client import QwenTTSClient def test_tts_connection(): """Test connection to TTS server.""" print("=" * 80) print("TESTING TTS CONNECTION") print("=" * 80) client = QwenTTSClient() try: # Test with a simple text print("\n1. Testing basic synthesis...") start_time = time.time() audio_data = client.synthesize_speech( text="Hello, this is a test.", voice_prompt="A clear, neutral voice." ) elapsed = time.time() - start_time print(f" Success! Audio data: {len(audio_data)} bytes") print(f" Time taken: {elapsed:.2f} seconds") # Save the test audio output_path = Path(__file__).parent.parent / "test_audio_basic.wav" with open(output_path, 'wb') as f: f.write(audio_data) print(f" Saved to: {output_path}") return True except Exception as e: print(f" Error: {e}") return False finally: client.close() def test_character_voices(): """Test synthesis with character voice prompts.""" print("\n" + "=" * 80) print("TESTING CHARACTER VOICES") print("=" * 80) client = QwenTTSClient() # Test characters with voice prompts test_characters = [ { "name": "Princess Kainda", "voice_prompt": "A young adult female in her early 20s with a clear, bright mid-range voice. Speaks with a curious, confident cadence and a refined, royal accent.", "test_line": "I will not be trapped in my cabin any longer. I want to see what's out there." }, { "name": "Captain Hunter", "voice_prompt": "A middle-aged male in his 40s with a gravelly, low-pitched baritone. Speaks with a slow, deliberate pace, rough texture, and authoritative delivery.", "test_line": "Orders, Captain. We need to move fast before the salvagers arrive." }, { "name": "King Kabir", "voice_prompt": "A senior male in his 60s with a resonant, regal baritone. Speaks with a slow, dignified pace and an authoritative, paternal tone.", "test_line": "My daughter has always been headstrong. Perhaps that is what we need in these troubled times." }, { "name": "Nanny (Robot)", "voice_prompt": "A synthesized female voice with a smooth, neutral tone. Speaks with a steady, measured cadence and precise, matter-of-fact delivery.", "test_line": "Princess, please put this on quickly. We are under attack by pirates." }, { "name": "Aunt Vicky", "voice_prompt": "A mature female in her 50s with a warm, concerned alto. Speaks with a gentle, maternal cadence and a worried, caring tone.", "test_line": "Who is the captain of this ship? I need to speak with him immediately." } ] results = [] for i, char in enumerate(test_characters): print(f"\n{i+1}. Testing {char['name']}...") try: start_time = time.time() audio_data = client.synthesize_speech( text=char['test_line'], voice_prompt=char['voice_prompt'] ) elapsed = time.time() - start_time # Save audio safe_name = char['name'].replace(' ', '_').replace('(', '').replace(')', '') output_path = Path(__file__).parent.parent / f"test_audio_{safe_name}.wav" with open(output_path, 'wb') as f: f.write(audio_data) results.append({ "name": char['name'], "success": True, "audio_size": len(audio_data), "time_seconds": elapsed, "output_path": str(output_path) }) print(f" Success! Audio: {len(audio_data)} bytes, Time: {elapsed:.2f}s") print(f" Saved to: {output_path}") except Exception as e: results.append({ "name": char['name'], "success": False, "error": str(e) }) print(f" Error: {e}") client.close() # Summary print("\n" + "=" * 80) print("CHARACTER VOICE TEST SUMMARY") print("=" * 80) successful = sum(1 for r in results if r['success']) print(f"Successful: {successful}/{len(results)}") for r in results: status = "PASS" if r['success'] else "FAIL" print(f" {r['name']}: {status}") if r['success']: print(f" Size: {r['audio_size']} bytes, Time: {r['time_seconds']:.2f}s") else: print(f" Error: {r.get('error', 'Unknown')}") return successful == len(results) def test_dialogue_extraction(): """Test synthesis with actual dialogue from the manuscript.""" print("\n" + "=" * 80) print("TESTING DIALOGUE SYNTHESIS") print("=" * 80) client = QwenTTSClient() # Real dialogue from DealHunterShip dialogues = [ { "speaker": "Kainda", "text": "Where is the ship that brought me? Did you fire on that ship?", "voice_prompt": "A young adult female in her early 20s with a clear, bright mid-range voice. Speaks with a curious, confident cadence and a refined, royal accent." }, { "speaker": "Hunter", "text": "Orders, Captain.", "voice_prompt": "A middle-aged male in his 40s with a gravelly, low-pitched baritone. Speaks with a slow, deliberate pace, rough texture, and authoritative delivery." }, { "speaker": "Kabir", "text": "Or I can let her run loose and watch the fun.", "voice_prompt": "A senior male in his 60s with a resonant, regal baritone. Speaks with a slow, dignified pace and an authoritative, paternal tone." } ] results = [] for i, d in enumerate(dialogues): print(f"\n{i+1}. Synthesizing {d['speaker']}'s dialogue...") try: start_time = time.time() audio_data = client.synthesize_speech( text=d['text'], voice_prompt=d['voice_prompt'] ) elapsed = time.time() - start_time # Save audio output_path = Path(__file__).parent.parent / f"test_dialogue_{d['speaker'].lower()}.wav" with open(output_path, 'wb') as f: f.write(audio_data) results.append({ "speaker": d['speaker'], "success": True, "audio_size": len(audio_data), "time_seconds": elapsed }) print(f" Success! Audio: {len(audio_data)} bytes, Time: {elapsed:.2f}s") except Exception as e: results.append({ "speaker": d['speaker'], "success": False, "error": str(e) }) print(f" Error: {e}") client.close() # Summary print("\n" + "=" * 80) print("DIALOGUE SYNTHESIS SUMMARY") print("=" * 80) successful = sum(1 for r in results if r['success']) print(f"Successful: {successful}/{len(results)}") for r in results: status = "PASS" if r['success'] else "FAIL" print(f" {r['speaker']}: {status}") return successful == len(results) def test_voice_prompt_validation(): """Test voice prompt validation.""" print("\n" + "=" * 80) print("TESTING VOICE PROMPT VALIDATION") print("=" * 80) client = QwenTTSClient() test_prompts = [ "A young adult female in her early 20s with a clear, bright mid-range voice. Speaks with a curious, confident cadence and a refined, royal accent.", "A middle-aged male with a gravelly, low-pitched baritone.", "This is a very long prompt that exceeds the forty word limit and should fail validation because it contains too many words for the voice synthesis system to handle properly.", "A voice that sounds like a mysterious philosopher from ancient times." ] for i, prompt in enumerate(test_prompts): print(f"\n{i+1}. Validating prompt: {prompt[:50]}...") result = client.validate_voice_prompt(prompt) print(f" Valid: {result['valid']}") print(f" Word count: {result['word_count']}") if result['issues']: print(f" Issues: {result['issues']}") client.close() return True def main(): """Run all TTS tests.""" print("UmbraAudio Voice Synthesis Tests") print("=" * 80) results = {} try: results['connection'] = test_tts_connection() except Exception as e: print(f"ERROR in connection test: {e}") results['connection'] = False try: results['character_voices'] = test_character_voices() except Exception as e: print(f"ERROR in character voice test: {e}") results['character_voices'] = False try: results['dialogue'] = test_dialogue_extraction() except Exception as e: print(f"ERROR in dialogue test: {e}") results['dialogue'] = False try: results['validation'] = test_voice_prompt_validation() except Exception as e: print(f"ERROR in validation test: {e}") results['validation'] = False # Final summary print("\n" + "=" * 80) print("FINAL TEST SUMMARY") print("=" * 80) all_passed = True for test_name, passed in results.items(): status = "PASS" if passed else "FAIL" print(f" {test_name}: {status}") if not passed: all_passed = False print("\n" + "=" * 80) if all_passed: print("ALL TESTS PASSED!") else: print("SOME TESTS FAILED!") print("=" * 80) return 0 if all_passed else 1 if __name__ == "__main__": sys.exit(main())