From 820815e1a55b6509c3a0ba94af9322a5d1241b08 Mon Sep 17 00:00:00 2001 From: David Kotnik Date: Thu, 8 Jan 2026 15:55:16 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=99=EF=B8=8F=20Jan=208=20Edge=20TTS=20?= =?UTF-8?q?Voice=20Generator=20-=20AI=20Voice=20Synthesis=20Working!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit โœ… VOICE GENERATION SYSTEM COMPLETE: **Script Created:** scripts/generate_voices_edge_tts.py - Async voice generation using Microsoft Edge TTS - Multiple character voices configured - English + Slovenian support - Adjustable rate and pitch **Voice Configurations:** - Kai (EN): en-US-AvaNeural (young female) - Kai (SL): sl-SI-PetraNeural - Ana (EN): en-US-JennyNeural (warm, friendly) - Narrator (EN): en-US-GuyNeural (deep, storytelling) **Test Generation SUCCESS:** โœ… Generated: kai_test_01.mp3 (17,280 bytes) Text: 'My name is Kai, and I will find my sister.' Voice: en-US-AvaNeural Quality: High-quality AI voice synthesis **Features:** - Automatic MP3 generation - Organized output to /assets/audio/voices/[character]/ - Configurable speech rate (-50% to +100%) - Configurable pitch (-50Hz to +50Hz) - Batch generation functions ready **Usage:** python3 scripts/generate_voices_edge_tts.py **Next Steps:** 1. Uncomment generate_kai_voices() for full Kai dialogue 2. Generate Ana, Narrator voices 3. Add sound effects using similar approach (or freesound.org) 4. Generate background music (use AI music tools) ๐ŸŽฏ Audio Status: 67/99 files (68% complete + voice generator ready) --- assets/audio/voices/kai/kai_test_01.mp3 | Bin 0 -> 17280 bytes scripts/generate_voices_edge_tts.py | 139 ++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 assets/audio/voices/kai/kai_test_01.mp3 create mode 100755 scripts/generate_voices_edge_tts.py diff --git a/assets/audio/voices/kai/kai_test_01.mp3 b/assets/audio/voices/kai/kai_test_01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f3f4772ee1cad1d0f12fc3ab226bf6d65bd50919 GIT binary patch literal 17280 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 IfBfWs0Kjaet^fc4 literal 0 HcmV?d00001 diff --git a/scripts/generate_voices_edge_tts.py b/scripts/generate_voices_edge_tts.py new file mode 100755 index 000000000..b587ccfd0 --- /dev/null +++ b/scripts/generate_voices_edge_tts.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +""" +Edge TTS Voice Generator +Generate voice-over audio using Microsoft Edge TTS +""" + +import asyncio +import edge_tts +from pathlib import Path + +# Output directory +OUTPUT_DIR = Path("/Users/davidkotnik/repos/novafarma/assets/audio/voices") + +# Voice configurations +VOICES = { + "kai_en": "en-US-AvaNeural", # English - female, young + "kai_sl": "sl-SI-PetraNeural", # Slovenian + "ana_en": "en-US-JennyNeural", # English - warm, friendly + "ana_sl": "sl-SI-PetraNeural", + "narrator_en": "en-US-GuyNeural", # English - deep, storytelling + "narrator_sl": "sl-SI-RokNeural", # Slovenian male +} + +async def generate_voice(text: str, voice: str, output_path: Path, rate: str = "+0%", pitch: str = "+0Hz"): + """ + Generate voice audio from text + + Args: + text: Text to convert to speech + voice: Voice ID (e.g. "en-US-AvaNeural") + output_path: Where to save the MP3 + rate: Speech rate (-50% to +100%) + pitch: Speech pitch (-50Hz to +50Hz) + """ + print(f"๐ŸŽ™๏ธ Generating: {output_path.name}") + print(f" Voice: {voice}") + print(f" Text: {text[:50]}...") + + # Create output directory + output_path.parent.mkdir(parents=True, exist_ok=True) + + # Generate speech + communicate = edge_tts.Communicate(text, voice, rate=rate, pitch=pitch) + await communicate.save(str(output_path)) + + # Check file size + size = output_path.stat().st_size + print(f" โœ… Saved: {size:,} bytes\n") + +async def generate_test(): + """Generate test voice for Kai""" + text = "My name is Kai, and I will find my sister." + output = OUTPUT_DIR / "kai" / "kai_test_01.mp3" + + await generate_voice( + text=text, + voice=VOICES["kai_en"], + output_path=output + ) + +async def generate_kai_voices(): + """Generate Kai's voice lines""" + lines = [ + "My name is Kai, and I will find my sister.", + "Ana, where are you? I won't give up.", + "This farm... it reminds me of home.", + "I need to keep farming. For Ana.", + "Another day, another harvest. But I won't forget." + ] + + for i, text in enumerate(lines, 1): + output = OUTPUT_DIR / "kai" / f"kai_{i:02d}.mp3" + await generate_voice(text, VOICES["kai_en"], output) + +async def generate_ana_voices(): + """Generate Ana's voice lines (memories)""" + lines = [ + "Kai... can you hear me?", + "Remember the farm... remember our home.", + "I'm still here, Kai. Don't forget me.", + "The valley holds secrets... find them." + ] + + for i, text in enumerate(lines, 1): + output = OUTPUT_DIR / "ana" / f"ana_{i:02d}.mp3" + await generate_voice(text, VOICES["ana_en"], output, rate="-10%", pitch="-5Hz") + +async def generate_narrator_voices(): + """Generate narrator voice lines""" + lines = [ + "In the Valley of Death, a young farmer searches for answers.", + "Long ago, this valley was green and full of life.", + "But the dead walk now, and the living must survive." + ] + + for i, text in enumerate(lines, 1): + output = OUTPUT_DIR / "narrator" / f"narrator_{i:02d}.mp3" + await generate_voice(text, VOICES["narrator_en"], output, rate="-5%") + +async def list_available_voices(): + """List all available Edge TTS voices""" + print("\n๐Ÿ“‹ Available Edge TTS Voices:\n") + + voices = await edge_tts.list_voices() + + # Filter to relevant languages + relevant = [v for v in voices if v["Locale"].startswith(("en-", "sl-"))] + + for voice in relevant[:20]: # Show first 20 + print(f" {voice['ShortName']}") + print(f" Language: {voice['Locale']}") + print(f" Gender: {voice['Gender']}") + print() + +async def main(): + """Main execution""" + print("="*60) + print("๐ŸŽ™๏ธ EDGE TTS VOICE GENERATOR") + print("="*60) + + # Generate test first + print("\n๐Ÿงช GENERATING TEST VOICE:\n") + await generate_test() + + print("\n" + "="*60) + print("โœ… TEST COMPLETE! Check: assets/audio/voices/kai/kai_test_01.mp3") + print("="*60) + + # Ask if user wants to continue + print("\n๐Ÿ“ To generate all voices, uncomment the function calls below.") + +if __name__ == "__main__": + # Run async main + asyncio.run(main()) + + # UNCOMMENT BELOW TO GENERATE ALL VOICES: + # asyncio.run(generate_kai_voices()) + # asyncio.run(generate_ana_voices()) + # asyncio.run(generate_narrator_voices())