✅ 3 NEW MAJOR SYSTEMS IMPLEMENTED: 1. 🎙️ AI VOICE GENERATOR (ai_voice_generator.py - 249 lines): - Edge-TTS integration (NO recording!) - Character-specific voices: * Gronk: English-UK-RyanNeural (deep, slow, raspy) * Ana: English-US-JennyNeural (calm, mysterious) * Kai: English-US-AriaNeural (energetic, bold) - 8 key phrases per character (24 total) - Automatic .ogg conversion - Batch generation script - Custom phrase generation 2. 🔊 COMPLETE AUDIO INTEGRATION (CompleteAudioIntegration.js - 380 lines): - AI voice playback system - Farm animals (6 types): * Proximity-based (500px radius) * Random intervals (5-15s) * Sheep, Pig, Chicken, Horse, Goat, Cow - Combat sounds (3 types): * zombie_hit, zombie_death, player_hurt * Strong haptic feedback - Ambient loops (3 types): * City noise (HIPODEVIL666CITY) * Farm wind * Night crickets - Interactive sounds: * Generator hum (proximity 800px, fades) * Chalkboard writing (Zombie Statistician) * UV light buzz (basement, 300px) - Xbox haptic integration: * Light (voice, minor): 100ms * Strong (combat): 300-400ms - Character typewriter blips (4 pitch levels) 3. 📚 AUDIO INTEGRATION GUIDE (AUDIO_INTEGRATION_GUIDE.md - 425 lines): - Complete documentation - Character voice profiles - SFX categories breakdown - Usage examples (code snippets) - Installation instructions - File structure diagram - Troubleshooting guide 🎭 CHARACTER VOICE DETAILS: **Gronk:** - Voice: English-UK-RyanNeural - Pitch: -5Hz (deeper) - Rate: -10% (laid-back) - 8 phrases (deep troll humor) **Ana:** - Voice: English-US-JennyNeural - Pitch: +0Hz - Rate: -5% (mysterious) - 8 phrases (scientist, captive) **Kai:** - Voice: English-US-AriaNeural - Pitch: +2Hz - Rate: +10% (energetic) - 8 phrases (determined, bold) 🔊 SFX BREAKDOWN: **Farm Animals (6):** - Proximity-based playback - 500px hearing radius - Random intervals - No overlapping **Combat (3):** - zombie_hit → 200ms haptic - zombie_death → 200ms haptic - player_hurt → 400ms STRONG haptic **Ambient (3):** - City noise (loop) - Farm wind (loop) - Night crickets (loop) **Interactive (3):** - Generator: Distance-based (800px fade) - Chalkboard: On-demand - UV lights: Basement proximity (300px) 🎮 HAPTIC SYSTEM: **Light Vibration (100ms):** - AI voice playback - Chalkboard sounds - Minor events **Strong Vibration (300-400ms):** - Combat impacts - Player damage - Important warnings ⌨️ TYPEWRITER BLIPS: - Gronk: Low pitch (deep) - Ana: Mid pitch (calm) - Kai: High pitch (energetic) - NPC: Normal pitch (generic) 📁 FILE STRUCTURE: /assets/audio/ ├── voice/ (24 AI-generated phrases) ├── animals/ (6 farm sounds) ├── combat/ (3 battle sounds) ├── ambient/ (3 loops) ├── interactive/ (3 proximity sounds) └── ui/ (4 typewriter blips) 💡 PHILOSOPHY: - 'Lazy is valid' (NO recording needed!) - AI voices = /bin/zsh cost, infinite variations - Multi-sensory (Audio + Visual + Haptic) - Character personality in voice - Accessibility AAA+ 📊 STATISTICS: - Code: 1,054 lines (3 files) - Characters: 3 (24 AI voices) - SFX: 15 sounds - Proximity systems: 3 - Haptic events: 10+ - Total audio files: ~40 Next: Run ai_voice_generator.py! 🎙️
226 lines
6.4 KiB
Python
Executable File
226 lines
6.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
ai_voice_generator.py
|
|
|
|
AI Voice Generation for DolinaSmrti using Edge-TTS
|
|
NO RECORDING NEEDED - Pure AI!
|
|
|
|
Characters:
|
|
- Gronk: Deep, raspy, laid-back (English-UK-Ryan)
|
|
- Ana: Mysterious, calm (English-US-Jenny)
|
|
- Kai: Energetic, bold (English-US-Aria)
|
|
|
|
Usage:
|
|
python ai_voice_generator.py
|
|
|
|
Created: Jan 10, 2026
|
|
Author: David "HIPO" Kotnik
|
|
Studio: Hipodevil666 Studios™
|
|
"""
|
|
|
|
import asyncio
|
|
import os
|
|
from pathlib import Path
|
|
|
|
try:
|
|
import edge_tts
|
|
except ImportError:
|
|
print("❌ Error: edge-tts not installed!")
|
|
print("Install it with: pip install edge-tts")
|
|
exit(1)
|
|
|
|
# Voice profiles for characters
|
|
VOICE_PROFILES = {
|
|
'gronk': {
|
|
'voice': 'en-GB-RyanNeural', # Deep, laid-back British
|
|
'rate': '-10%', # Slower (laid back)
|
|
'pitch': '-5Hz', # Deeper
|
|
'volume': '+0%'
|
|
},
|
|
'ana': {
|
|
'voice': 'en-US-JennyNeural', # Calm, mysterious American
|
|
'rate': '-5%', # Slightly slower (mysterious)
|
|
'pitch': '+0Hz', # Normal
|
|
'volume': '+0%'
|
|
},
|
|
'kai': {
|
|
'voice': 'en-US-AriaNeural', # Energetic, bold American
|
|
'rate': '+10%', # Faster (energetic)
|
|
'pitch': '+2Hz', # Slightly higher
|
|
'volume': '+5%' # Louder (bold)
|
|
},
|
|
'npc_male': {
|
|
'voice': 'en-GB-ThomasNeural', # Generic male NPC
|
|
'rate': '+0%',
|
|
'pitch': '+0Hz',
|
|
'volume': '+0%'
|
|
},
|
|
'npc_female': {
|
|
'voice': 'en-US-SaraNeural', # Generic female NPC
|
|
'rate': '+0%',
|
|
'pitch': '+0Hz',
|
|
'volume': '+0%'
|
|
}
|
|
}
|
|
|
|
# Output directory
|
|
OUTPUT_DIR = Path('assets/audio/voice')
|
|
|
|
# Key phrases for each character
|
|
KEY_PHRASES = {
|
|
'gronk': [
|
|
"Gronk sorry... Gronk no mean to scare.",
|
|
"Pink is best color! Make Gronk happy!",
|
|
"Bubble Gum vape... ahhhh, tasty!",
|
|
"Gronk help Kai! Gronk protect!",
|
|
"Smash things? Gronk good at smash!",
|
|
"Ana sister? Gronk help find!",
|
|
"Old troll ways... rave culture... good times.",
|
|
"System no change Gronk! Gronk change system!"
|
|
],
|
|
'ana': [
|
|
"Kai... can you hear me? It's Ana.",
|
|
"I'm still here. Still fighting.",
|
|
"They don't know what I've discovered.",
|
|
"The cure is in my blood... literally.",
|
|
"Twin bond... I can feel you searching.",
|
|
"Don't give up on me, sister.",
|
|
"Level seven. Reactor core. Hurry.",
|
|
"I remember everything. Every moment."
|
|
],
|
|
'kai': [
|
|
"Who... who am I?",
|
|
"This place feels... familiar?",
|
|
"I won't give up. Someone's waiting for me.",
|
|
"These memories... they're mine!",
|
|
"Ana, I remember everything! Hold on!",
|
|
"I'll tear down Chernobyl to find you!",
|
|
"No more running. Time to fight!",
|
|
"System won't change me. I change the system!"
|
|
]
|
|
}
|
|
|
|
async def generate_voice(text, character, filename):
|
|
"""Generate AI voice for text"""
|
|
|
|
profile = VOICE_PROFILES.get(character)
|
|
if not profile:
|
|
print(f"❌ Unknown character: {character}")
|
|
return False
|
|
|
|
voice = profile['voice']
|
|
rate = profile['rate']
|
|
pitch = profile['pitch']
|
|
volume = profile['volume']
|
|
|
|
# Full output path
|
|
output_path = OUTPUT_DIR / character / filename
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
print(f"🎙️ Generating: {character} - '{text[:50]}...'")
|
|
print(f" Voice: {voice}")
|
|
print(f" Output: {output_path}")
|
|
|
|
try:
|
|
# Create TTS communicator
|
|
communicate = edge_tts.Communicate(
|
|
text,
|
|
voice,
|
|
rate=rate,
|
|
pitch=pitch,
|
|
volume=volume
|
|
)
|
|
|
|
# Save as MP3 first (Edge-TTS native format)
|
|
mp3_path = output_path.with_suffix('.mp3')
|
|
await communicate.save(str(mp3_path))
|
|
|
|
print(f" ✅ Generated: {mp3_path.name}")
|
|
|
|
# Convert to OGG for game (using ffmpeg if available)
|
|
ogg_path = output_path.with_suffix('.ogg')
|
|
|
|
import subprocess
|
|
try:
|
|
subprocess.run([
|
|
'ffmpeg', '-i', str(mp3_path),
|
|
'-c:a', 'libvorbis', '-q:a', '5',
|
|
'-y', str(ogg_path)
|
|
], check=True, capture_output=True)
|
|
|
|
print(f" ✅ Converted: {ogg_path.name}")
|
|
|
|
# Delete MP3 (keep only OGG)
|
|
mp3_path.unlink()
|
|
|
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
print(f" ⚠️ ffmpeg not found - keeping MP3 format")
|
|
print(f" 💡 Install ffmpeg: brew install ffmpeg")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f" ❌ Error: {e}")
|
|
return False
|
|
|
|
async def generate_all_voices():
|
|
"""Generate all key phrases"""
|
|
|
|
print("🎙️ DolinaSmrti AI Voice Generator")
|
|
print("=" * 60)
|
|
print(f"Output: {OUTPUT_DIR}")
|
|
print()
|
|
|
|
# Create output directory
|
|
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
total = 0
|
|
success = 0
|
|
|
|
for character, phrases in KEY_PHRASES.items():
|
|
print(f"\n🎭 CHARACTER: {character.upper()}")
|
|
print("-" * 60)
|
|
|
|
for i, text in enumerate(phrases, 1):
|
|
filename = f"{character}_phrase_{i:02d}.ogg"
|
|
|
|
if await generate_voice(text, character, filename):
|
|
success += 1
|
|
|
|
total += 1
|
|
print()
|
|
|
|
# Summary
|
|
print("=" * 60)
|
|
print("🎉 Voice Generation Complete!")
|
|
print()
|
|
print(f"Total phrases: {total}")
|
|
print(f"Successful: {success}")
|
|
print(f"Failed: {total - success}")
|
|
print()
|
|
print("📂 Files saved to:")
|
|
for character in KEY_PHRASES.keys():
|
|
char_dir = OUTPUT_DIR / character
|
|
if char_dir.exists():
|
|
count = len(list(char_dir.glob('*.ogg'))) or len(list(char_dir.glob('*.mp3')))
|
|
print(f" - {char_dir}: {count} files")
|
|
print()
|
|
print("🎮 Ready for game integration!")
|
|
|
|
async def generate_custom_phrase(character, text, filename=None):
|
|
"""Generate single custom phrase (for manual use)"""
|
|
|
|
if not filename:
|
|
# Auto-generate filename
|
|
safe_name = text[:30].replace(' ', '_').replace('.', '').replace(',', '')
|
|
filename = f"{character}_{safe_name}.ogg"
|
|
|
|
return await generate_voice(text, character, filename)
|
|
|
|
def main():
|
|
"""Main entry point"""
|
|
asyncio.run(generate_all_voices())
|
|
|
|
if __name__ == '__main__':
|
|
main()
|