268 lines
7.8 KiB
Python
268 lines
7.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
AUDIO PRODUCTION AUTOMATION SCRIPT
|
|
Generates placeholder audio files and TTS voices for DolinaSmrti
|
|
Uses edge-tts for voice synthesis (FREE Microsoft Azure TTS)
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import asyncio
|
|
from pathlib import Path
|
|
|
|
# Check if edge-tts is installed
|
|
try:
|
|
import edge_tts
|
|
EDGE_TTS_AVAILABLE = True
|
|
except ImportError:
|
|
EDGE_TTS_AVAILABLE = False
|
|
print("⚠️ edge-tts not installed. Install with: pip install edge-tts")
|
|
|
|
# Audio output directory
|
|
AUDIO_DIR = Path(__file__).parent.parent / "assets" / "audio"
|
|
|
|
# Audio manifest
|
|
AUDIO_MANIFEST = {
|
|
"music": [
|
|
"main_theme.ogg",
|
|
"farm_ambient.ogg",
|
|
"combat_theme.ogg",
|
|
"raid_warning.ogg",
|
|
"town_theme.ogg",
|
|
"night_theme.ogg",
|
|
"victory_theme.ogg",
|
|
"ana_theme.ogg"
|
|
],
|
|
"ambience": [
|
|
"wind_soft.ogg",
|
|
"wind_strong.ogg",
|
|
"rain_light.ogg",
|
|
"rain_heavy.ogg",
|
|
"crickets.ogg",
|
|
"birds_chirping.ogg",
|
|
"fire_crackling.ogg",
|
|
"water_stream.ogg",
|
|
"zombie_moans_distant.ogg",
|
|
"town_bustle.ogg",
|
|
"workshop_ambient.ogg",
|
|
"forest_ambient.ogg"
|
|
],
|
|
"sfx": {
|
|
"farming": [
|
|
"dig.ogg",
|
|
"plant_seed.ogg",
|
|
"harvest.ogg",
|
|
"water_crop.ogg",
|
|
"tree_chop.ogg",
|
|
"stone_mine.ogg",
|
|
"scythe_swing.ogg",
|
|
"cow_moo.ogg"
|
|
],
|
|
"combat": [
|
|
"sword_slash.ogg",
|
|
"zombie_hit.ogg",
|
|
"zombie_death.ogg",
|
|
"player_hurt.ogg",
|
|
"raider_attack.ogg",
|
|
"shield_block.ogg",
|
|
"bow_release.ogg",
|
|
"explosion.ogg"
|
|
],
|
|
"building": [
|
|
"hammer_nail.ogg",
|
|
"door_open.ogg",
|
|
"door_close.ogg",
|
|
"chest_open.ogg",
|
|
"repair.ogg"
|
|
],
|
|
"misc": [
|
|
"footstep_grass.ogg",
|
|
"footstep_stone.ogg",
|
|
"coin_collect.ogg",
|
|
"level_up.ogg"
|
|
]
|
|
},
|
|
"ui": [
|
|
"button_click.ogg",
|
|
"button_hover.ogg",
|
|
"notification.ogg",
|
|
"quest_complete.ogg",
|
|
"error.ogg"
|
|
]
|
|
}
|
|
|
|
# NPC voice lines
|
|
NPC_VOICES = {
|
|
"kai": {
|
|
"voice": "sl-SI-PetraNeural", # Slovenian female (can use for young male)
|
|
"lines": [
|
|
"Živjo! Sem Kai.",
|
|
"Moramo najti Ano.",
|
|
"Zombiji prihajajo!",
|
|
"Hvala za pomoč.",
|
|
"To je nevarno..."
|
|
]
|
|
},
|
|
"ana": {
|
|
"voice": "sl-SI-RokNeural", # Slovenian male (soft)
|
|
"lines": [
|
|
"Kai... kje si?",
|
|
"Spomin mi uhaja...",
|
|
"Pomagaj mi, prosim.",
|
|
"Hvala ti."
|
|
]
|
|
},
|
|
"teacher": {
|
|
"voice": "sl-SI-PetraNeural",
|
|
"lines": [
|
|
"Dobrodošel v šoli!",
|
|
"Želiš se kaj naučiti?",
|
|
"Odlično delo!",
|
|
"Še ena lekcija?"
|
|
]
|
|
},
|
|
"mayor": {
|
|
"voice": "sl-SI-RokNeural",
|
|
"lines": [
|
|
"Dobrodošel, meščan!",
|
|
"Mesto potrebuje tvojo pomoč.",
|
|
"Odlično! Mesto cveti.",
|
|
"Hvala za vašo službo!"
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
async def generate_voice_line(text, voice, output_path):
|
|
"""Generate a single voice line using edge-tts"""
|
|
if not EDGE_TTS_AVAILABLE:
|
|
print(f"⚠️ Skipping {output_path.name} - edge-tts not available")
|
|
return False
|
|
|
|
try:
|
|
communicate = edge_tts.Communicate(text, voice)
|
|
await communicate.save(str(output_path))
|
|
print(f"✅ Generated: {output_path.name}")
|
|
return True
|
|
except Exception as e:
|
|
print(f"❌ Error generating {output_path.name}: {e}")
|
|
return False
|
|
|
|
|
|
async def generate_all_npc_voices():
|
|
"""Generate all NPC voice lines"""
|
|
voices_dir = AUDIO_DIR / "voices"
|
|
voices_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
total = 0
|
|
successful = 0
|
|
|
|
for npc_name, npc_data in NPC_VOICES.items():
|
|
npc_dir = voices_dir / npc_name
|
|
npc_dir.mkdir(exist_ok=True)
|
|
|
|
for i, line in enumerate(npc_data["lines"], 1):
|
|
output_file = npc_dir / f"{npc_name}_{i:02d}.mp3"
|
|
total += 1
|
|
|
|
if await generate_voice_line(line, npc_data["voice"], output_file):
|
|
successful += 1
|
|
|
|
print(f"\n🎤 Voice Generation: {successful}/{total} successful")
|
|
|
|
|
|
def create_placeholder_files():
|
|
"""Create placeholder text files for all audio"""
|
|
print("\n📝 Creating placeholder files...\n")
|
|
|
|
# Music
|
|
music_dir = AUDIO_DIR / "music"
|
|
music_dir.mkdir(parents=True, exist_ok=True)
|
|
for track in AUDIO_MANIFEST["music"]:
|
|
placeholder = music_dir / f"{track}.txt"
|
|
placeholder.write_text(f"PLACEHOLDER: {track}\nDuration: 2-3 minutes\nStyle: Dark folk/post-apocalyptic\nLoop: Yes")
|
|
print(f"📄 {track}.txt")
|
|
|
|
# Ambience
|
|
ambience_dir = AUDIO_DIR / "ambience"
|
|
ambience_dir.mkdir(parents=True, exist_ok=True)
|
|
for amb in AUDIO_MANIFEST["ambience"]:
|
|
placeholder = ambience_dir / f"{amb}.txt"
|
|
placeholder.write_text(f"PLACEHOLDER: {amb}\nDuration: 30-60 seconds\nLoop: Seamless\nSource: Freesound.org")
|
|
print(f"📄 {amb}.txt")
|
|
|
|
# SFX
|
|
for category, sounds in AUDIO_MANIFEST["sfx"].items():
|
|
sfx_dir = AUDIO_DIR / "sfx" / category
|
|
sfx_dir.mkdir(parents=True, exist_ok=True)
|
|
for sfx in sounds:
|
|
placeholder = sfx_dir / f"{sfx}.txt"
|
|
placeholder.write_text(f"PLACEHOLDER: {sfx}\nCategory: {category}\nDuration: 0.1-2 seconds\nFormat: OGG Vorbis")
|
|
print(f"📄 {sfx}.txt")
|
|
|
|
# UI
|
|
ui_dir = AUDIO_DIR / "ui"
|
|
ui_dir.mkdir(parents=True, exist_ok=True)
|
|
for ui_sound in AUDIO_MANIFEST["ui"]:
|
|
placeholder = ui_dir / f"{ui_sound}.txt"
|
|
placeholder.write_text(f"PLACEHOLDER: {ui_sound}\nDuration: 0.1-0.5 seconds\nFormat: OGG Vorbis\nCrisp, clean sound")
|
|
print(f"📄 {ui_sound}.txt")
|
|
|
|
print(f"\n✅ Created {61} placeholder files!")
|
|
|
|
|
|
def generate_audio_manifest_json():
|
|
"""Generate audio_manifest.json for loading"""
|
|
manifest_path = AUDIO_DIR / "audio_manifest.json"
|
|
|
|
manifest_data = {
|
|
"version": "1.0",
|
|
"total_files": 61,
|
|
"categories": {
|
|
"music": len(AUDIO_MANIFEST["music"]),
|
|
"ambience": len(AUDIO_MANIFEST["ambience"]),
|
|
"sfx": sum(len(sounds) for sounds in AUDIO_MANIFEST["sfx"].values()),
|
|
"ui": len(AUDIO_MANIFEST["ui"]),
|
|
"voices": sum(len(npc["lines"]) for npc in NPC_VOICES.values())
|
|
},
|
|
"files": AUDIO_MANIFEST
|
|
}
|
|
|
|
manifest_path.write_text(json.dumps(manifest_data, indent=2))
|
|
print(f"\n✅ Generated audio_manifest.json")
|
|
|
|
|
|
async def main():
|
|
"""Main execution"""
|
|
print("🎵 DolinaSmrti - Audio Production Automation")
|
|
print("=" * 50)
|
|
|
|
# Create directories
|
|
AUDIO_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Step 1: Create placeholder files
|
|
create_placeholder_files()
|
|
|
|
# Step 2: Generate manifest
|
|
generate_audio_manifest_json()
|
|
|
|
# Step 3: Generate NPC voices (if edge-tts available)
|
|
if EDGE_TTS_AVAILABLE:
|
|
print("\n🎤 Generating NPC voice lines...")
|
|
await generate_all_npc_voices()
|
|
else:
|
|
print("\n⚠️ Install edge-tts to generate NPC voices:")
|
|
print(" pip install edge-tts")
|
|
|
|
print("\n" + "=" * 50)
|
|
print("✅ AUDIO PRODUCTION SETUP COMPLETE!")
|
|
print("\nNext steps:")
|
|
print("1. Install edge-tts: pip install edge-tts")
|
|
print("2. Run this script again to generate voices")
|
|
print("3. Source music/SFX from Freesound.org or AI music generators")
|
|
print("4. Convert all files to OGG Vorbis format")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|