🎬🎵 Jan 8 COMPLETE AUDIO PRODUCTION - Intro + Music + SFX
✅ FULL AUDIO SYSTEM READY (104 FILES): **🎙️ INTRO CUTSCENE VOICES (4 NEW MP3):** Generated via Edge TTS: 1. 01_narrator_flyover.mp3 (73KB) - 'They say the world didn't die...' 2. 02_kai_awakening.mp3 (28KB) - 'My head... it hurts. Where am I?' 3. 03_kai_truth_part1.mp3 (67KB) - 'Kai Marković. 14 years old...' 4. 04_kai_truth_part2.mp3 (51KB) - 'I'm coming to find you... Ana.' **Story Structure:** - Part 1: The Flyover (0:00-0:45) - Narrator - Part 2: The Awakening (0:45-1:10) - Kai confused - Part 3: The Truth (1:10-2:00) - Kai determined **🎵 MUSIC PLACEHOLDERS (7 WAV - 60MB):** Simple ambient loops for testing: 1. main_theme.wav (90s) - Menu music 2. farm_ambient.wav (120s) - Farming 3. town_theme.wav (90s) - Town restoration 4. combat_theme.wav (60s) - Battle 5. night_theme.wav (180s) - Nighttime 6. victory_theme.wav (30s) - Quest complete 7. ana_theme.wav (120s) - Emotional/flashback **🔊 SFX PLACEHOLDERS (23 WAV - 1.5MB):** Farming (8): plant_seed, water_crop, harvest, dig, scythe, mine, chop, cow Combat (8): sword, bow, zombie_hit, zombie_death, hurt, shield, explosion, raider Building (5): chest, door_open, door_close, hammer, repair Misc (2): coin_collect, level_up **📊 TOTAL AUDIO INVENTORY:** - Voice Files: 28 MP3 (24 existing + 4 new intro) - Voiceover: 43 WAV (prologue cutscenes) - Sound Effects: 25 WAV (2 existing + 23 new placeholders) - Music: 8 tracks (1 existing + 7 new placeholders) - **TOTAL: 104 audio files!** **🎮 INTEGRATION:** - Updated PreloadScene with intro voice loading - All audio keys ready for use in intro cutscene - BiomeMusicSystem ready for 7-track cross-fade - AudioTriggerSystem ready for all 23 SFX **📝 SCRIPTS CREATED:** 1. scripts/generate_intro_cutscene.py - Intro dialogue generation 2. scripts/generate_audio_placeholders.py - Music + SFX placeholders **🎯 USAGE:** Intro voices: 'intro_flyover', 'intro_awakening', 'intro_truth_1', 'intro_truth_2' Music: Load from assets/audio/music/*.wav SFX: Load from assets/audio/sfx/[category]/*.wav **⚠️ NOTE:** Music/SFX are PLACEHOLDERS (simple tones) Replace with real audio from Freesound.org or AI generators later! 🎉 AUDIO SYSTEM 100% FUNCTIONAL FOR TESTING!
This commit is contained in:
BIN
assets/audio/music/ana_theme.wav
Normal file
BIN
assets/audio/music/ana_theme.wav
Normal file
Binary file not shown.
BIN
assets/audio/music/combat_theme.wav
Normal file
BIN
assets/audio/music/combat_theme.wav
Normal file
Binary file not shown.
BIN
assets/audio/music/farm_ambient.wav
Normal file
BIN
assets/audio/music/farm_ambient.wav
Normal file
Binary file not shown.
BIN
assets/audio/music/main_theme.wav
Normal file
BIN
assets/audio/music/main_theme.wav
Normal file
Binary file not shown.
BIN
assets/audio/music/night_theme.wav
Normal file
BIN
assets/audio/music/night_theme.wav
Normal file
Binary file not shown.
BIN
assets/audio/music/town_theme.wav
Normal file
BIN
assets/audio/music/town_theme.wav
Normal file
Binary file not shown.
BIN
assets/audio/music/victory_theme.wav
Normal file
BIN
assets/audio/music/victory_theme.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/building/chest_open.wav
Normal file
BIN
assets/audio/sfx/building/chest_open.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/building/door_close.wav
Normal file
BIN
assets/audio/sfx/building/door_close.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/building/door_open.wav
Normal file
BIN
assets/audio/sfx/building/door_open.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/building/hammer_nail.wav
Normal file
BIN
assets/audio/sfx/building/hammer_nail.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/building/repair.wav
Normal file
BIN
assets/audio/sfx/building/repair.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/combat/bow_release.wav
Normal file
BIN
assets/audio/sfx/combat/bow_release.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/combat/explosion.wav
Normal file
BIN
assets/audio/sfx/combat/explosion.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/combat/player_hurt.wav
Normal file
BIN
assets/audio/sfx/combat/player_hurt.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/combat/raider_attack.wav
Normal file
BIN
assets/audio/sfx/combat/raider_attack.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/combat/shield_block.wav
Normal file
BIN
assets/audio/sfx/combat/shield_block.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/combat/sword_slash.wav
Normal file
BIN
assets/audio/sfx/combat/sword_slash.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/combat/zombie_death.wav
Normal file
BIN
assets/audio/sfx/combat/zombie_death.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/combat/zombie_hit.wav
Normal file
BIN
assets/audio/sfx/combat/zombie_hit.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/farming/cow_moo.wav
Normal file
BIN
assets/audio/sfx/farming/cow_moo.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/farming/dig.wav
Normal file
BIN
assets/audio/sfx/farming/dig.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/farming/harvest.wav
Normal file
BIN
assets/audio/sfx/farming/harvest.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/farming/plant_seed.wav
Normal file
BIN
assets/audio/sfx/farming/plant_seed.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/farming/scythe_swing.wav
Normal file
BIN
assets/audio/sfx/farming/scythe_swing.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/farming/stone_mine.wav
Normal file
BIN
assets/audio/sfx/farming/stone_mine.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/farming/tree_chop.wav
Normal file
BIN
assets/audio/sfx/farming/tree_chop.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/farming/water_crop.wav
Normal file
BIN
assets/audio/sfx/farming/water_crop.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/misc/coin_collect.wav
Normal file
BIN
assets/audio/sfx/misc/coin_collect.wav
Normal file
Binary file not shown.
BIN
assets/audio/sfx/misc/level_up.wav
Normal file
BIN
assets/audio/sfx/misc/level_up.wav
Normal file
Binary file not shown.
BIN
assets/audio/voiceover/intro/01_narrator_flyover.mp3
Normal file
BIN
assets/audio/voiceover/intro/01_narrator_flyover.mp3
Normal file
Binary file not shown.
BIN
assets/audio/voiceover/intro/02_kai_awakening.mp3
Normal file
BIN
assets/audio/voiceover/intro/02_kai_awakening.mp3
Normal file
Binary file not shown.
BIN
assets/audio/voiceover/intro/03_kai_truth_part1.mp3
Normal file
BIN
assets/audio/voiceover/intro/03_kai_truth_part1.mp3
Normal file
Binary file not shown.
BIN
assets/audio/voiceover/intro/04_kai_truth_part2.mp3
Normal file
BIN
assets/audio/voiceover/intro/04_kai_truth_part2.mp3
Normal file
Binary file not shown.
158
scripts/generate_audio_placeholders.py
Executable file
158
scripts/generate_audio_placeholders.py
Executable file
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Audio Placeholder Generator
|
||||
Creates simple placeholder audio files for testing
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from scipy.io import wavfile
|
||||
from pathlib import Path
|
||||
|
||||
SAMPLE_RATE = 44100 # CD quality
|
||||
OUTPUT_DIR_MUSIC = Path("/Users/davidkotnik/repos/novafarma/assets/audio/music")
|
||||
OUTPUT_DIR_SFX = Path("/Users/davidkotnik/repos/novafarma/assets/audio/sfx")
|
||||
|
||||
def generate_tone(frequency, duration, sample_rate=SAMPLE_RATE):
|
||||
"""Generate a simple sine wave tone"""
|
||||
t = np.linspace(0, duration, int(sample_rate * duration))
|
||||
tone = np.sin(2 * np.pi * frequency * t)
|
||||
# Convert to 16-bit PCM
|
||||
tone = (tone * 32767).astype(np.int16)
|
||||
return tone
|
||||
|
||||
def generate_noise(duration, sample_rate=SAMPLE_RATE):
|
||||
"""Generate white noise"""
|
||||
noise = np.random.normal(0, 0.1, int(sample_rate * duration))
|
||||
noise = (noise * 32767).astype(np.int16)
|
||||
return noise
|
||||
|
||||
def generate_ambient_loop(duration=60, base_freq=220):
|
||||
"""Generate simple ambient loop"""
|
||||
t = np.linspace(0, duration, int(SAMPLE_RATE * duration))
|
||||
|
||||
# Multiple sine waves for ambient feel
|
||||
ambient = (
|
||||
0.3 * np.sin(2 * np.pi * base_freq * t) +
|
||||
0.2 * np.sin(2 * np.pi * (base_freq * 1.5) * t) +
|
||||
0.15 * np.sin(2 * np.pi * (base_freq * 0.75) * t) +
|
||||
0.05 * np.random.normal(0, 0.1, len(t)) # Subtle noise
|
||||
)
|
||||
|
||||
# Fade in/out for seamless loop
|
||||
fade_samples = int(SAMPLE_RATE * 2) # 2 second fade
|
||||
fade_in = np.linspace(0, 1, fade_samples)
|
||||
fade_out = np.linspace(1, 0, fade_samples)
|
||||
|
||||
ambient[:fade_samples] *= fade_in
|
||||
ambient[-fade_samples:] *= fade_out
|
||||
|
||||
ambient = (ambient * 32767 * 0.5).astype(np.int16)
|
||||
return ambient
|
||||
|
||||
def create_music_placeholders():
|
||||
"""Create 7 music track placeholders"""
|
||||
print("\n🎵 CREATING MUSIC PLACEHOLDERS...")
|
||||
print("="*60)
|
||||
|
||||
OUTPUT_DIR_MUSIC.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
music_tracks = {
|
||||
"main_theme": (440, 90), # A note, 1.5 min
|
||||
"farm_ambient": (220, 120), # A lower, 2 min
|
||||
"town_theme": (330, 90), # E note
|
||||
"combat_theme": (523, 60), # C note, 1 min
|
||||
"night_theme": (196, 180), # G note, 3 min
|
||||
"victory_theme": (659, 30), # E note, 30 sec
|
||||
"ana_theme": (247, 120), # B note, 2 min
|
||||
}
|
||||
|
||||
for name, (freq, duration) in music_tracks.items():
|
||||
print(f"\n🎶 Generating: {name}.wav")
|
||||
print(f" Frequency: {freq}Hz, Duration: {duration}s")
|
||||
|
||||
audio = generate_ambient_loop(duration, freq)
|
||||
output_path = OUTPUT_DIR_MUSIC / f"{name}.wav"
|
||||
wavfile.write(output_path, SAMPLE_RATE, audio)
|
||||
|
||||
size = output_path.stat().st_size
|
||||
print(f" ✅ Saved: {size:,} bytes")
|
||||
|
||||
print(f"\n✅ Created {len(music_tracks)} music placeholders")
|
||||
|
||||
def create_sfx_placeholders():
|
||||
"""Create 23 SFX placeholders"""
|
||||
print("\n\n🔊 CREATING SFX PLACEHOLDERS...")
|
||||
print("="*60)
|
||||
|
||||
sfx_sounds = {
|
||||
# Farming (8)
|
||||
"farming/plant_seed": (880, 0.3),
|
||||
"farming/water_crop": (440, 1.0),
|
||||
"farming/harvest": (1320, 0.5),
|
||||
"farming/dig": (220, 0.8),
|
||||
"farming/scythe_swing": (660, 0.6),
|
||||
"farming/stone_mine": (330, 0.7),
|
||||
"farming/tree_chop": (165, 0.8),
|
||||
"farming/cow_moo": (110, 1.5),
|
||||
|
||||
# Combat (8)
|
||||
"combat/sword_slash": (1100, 0.4),
|
||||
"combat/bow_release": (880, 0.3),
|
||||
"combat/zombie_hit": (220, 0.5),
|
||||
"combat/zombie_death": (110, 1.0),
|
||||
"combat/player_hurt": (330, 0.5),
|
||||
"combat/shield_block": (440, 0.4),
|
||||
"combat/explosion": (55, 1.5),
|
||||
"combat/raider_attack": (165, 0.8),
|
||||
|
||||
# Building (5)
|
||||
"building/chest_open": (660, 0.8),
|
||||
"building/door_open": (440, 0.6),
|
||||
"building/door_close": (330, 0.6),
|
||||
"building/hammer_nail": (1100, 0.3),
|
||||
"building/repair": (880, 1.0),
|
||||
|
||||
# Misc (2)
|
||||
"misc/coin_collect": (1760, 0.3),
|
||||
"misc/level_up": (1320, 1.5),
|
||||
}
|
||||
|
||||
for name, (freq, duration) in sfx_sounds.items():
|
||||
# Create subfolder if needed
|
||||
sfx_path = OUTPUT_DIR_SFX / name.split('/')[0]
|
||||
sfx_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
filename = name.split('/')[1] + ".wav"
|
||||
output_path = sfx_path / filename
|
||||
|
||||
print(f"\n🔊 Generating: {name}.wav")
|
||||
print(f" Frequency: {freq}Hz, Duration: {duration}s")
|
||||
|
||||
audio = generate_tone(freq, duration)
|
||||
wavfile.write(output_path, SAMPLE_RATE, audio)
|
||||
|
||||
size = output_path.stat().st_size
|
||||
print(f" ✅ Saved: {size:,} bytes")
|
||||
|
||||
print(f"\n✅ Created {len(sfx_sounds)} SFX placeholders")
|
||||
|
||||
def main():
|
||||
"""Generate all placeholders"""
|
||||
print("="*60)
|
||||
print("🎨 AUDIO PLACEHOLDER GENERATOR")
|
||||
print("="*60)
|
||||
print("\nGenerating simple tone-based placeholders for testing...")
|
||||
print("Replace these with real audio later!")
|
||||
|
||||
create_music_placeholders()
|
||||
create_sfx_placeholders()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("✅ ALL PLACEHOLDERS GENERATED!")
|
||||
print("="*60)
|
||||
print("\nTotal files created: 30 (7 music + 23 SFX)")
|
||||
print("\n⚠️ NOTE: These are simple BEEP PLACEHOLDERS for testing!")
|
||||
print("Replace with real audio from Freesound.org or AI generators.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
123
scripts/generate_intro_cutscene.py
Executable file
123
scripts/generate_intro_cutscene.py
Executable file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Complete Intro Cutscene Voice Generation
|
||||
Generates all dialogue for the intro sequence
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import edge_tts
|
||||
from pathlib import Path
|
||||
|
||||
OUTPUT_DIR = Path("/Users/davidkotnik/repos/novafarma/assets/audio/voiceover/intro")
|
||||
|
||||
# Voice configurations
|
||||
NARRATOR_VOICE = "en-US-GuyNeural" # Deep, mysterious
|
||||
KAI_VOICE = "en-US-AvaNeural" # Young female
|
||||
|
||||
async def generate_intro_voices():
|
||||
"""Generate all intro cutscene dialogue"""
|
||||
|
||||
# Create output directory
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print("🎬 GENERATING INTRO CUTSCENE VOICES...")
|
||||
print("="*60)
|
||||
|
||||
# ========================================
|
||||
# PART 1: THE FLYOVER (Narrator)
|
||||
# ========================================
|
||||
print("\n📍 Part 1: The Flyover (Narrator)")
|
||||
|
||||
narrator_flyover = (
|
||||
"They say the world didn't die with a bang... "
|
||||
"but with a quiet whisper. "
|
||||
"The Valley of Death is not just a place. "
|
||||
"It's a memory that no one wants to have anymore."
|
||||
)
|
||||
|
||||
await generate_voice(
|
||||
text=narrator_flyover,
|
||||
voice=NARRATOR_VOICE,
|
||||
output_path=OUTPUT_DIR / "01_narrator_flyover.mp3",
|
||||
rate="-10%", # Slower, dramatic
|
||||
pitch="-5Hz" # Deeper
|
||||
)
|
||||
|
||||
# ========================================
|
||||
# PART 2: THE AWAKENING (Kai)
|
||||
# ========================================
|
||||
print("\n📍 Part 2: The Awakening (Kai)")
|
||||
|
||||
kai_awakening = (
|
||||
"My head... it hurts. Where am I? Who am I...?"
|
||||
)
|
||||
|
||||
await generate_voice(
|
||||
text=kai_awakening,
|
||||
voice=KAI_VOICE,
|
||||
output_path=OUTPUT_DIR / "02_kai_awakening.mp3",
|
||||
rate="-15%", # Slower, confused
|
||||
pitch="-3Hz" # Slightly lower
|
||||
)
|
||||
|
||||
# ========================================
|
||||
# PART 3: THE TRUTH (Kai - multiple lines)
|
||||
# ========================================
|
||||
print("\n📍 Part 3: The Truth (Kai)")
|
||||
|
||||
# Line 1: Reading ID card
|
||||
kai_truth_1 = (
|
||||
"Kai Marković. 14 years old. That's me. "
|
||||
"But this other girl... why do I feel so... empty when I see her? "
|
||||
"Like I'm missing half of my heart."
|
||||
)
|
||||
|
||||
await generate_voice(
|
||||
text=kai_truth_1,
|
||||
voice=KAI_VOICE,
|
||||
output_path=OUTPUT_DIR / "03_kai_truth_part1.mp3",
|
||||
rate="-5%", # Normal pace, emotional
|
||||
pitch="+0Hz"
|
||||
)
|
||||
|
||||
# Line 2: Final determination
|
||||
kai_truth_2 = (
|
||||
"Someone is waiting for me out there. "
|
||||
"I can't remember the face, but I feel the promise. "
|
||||
"I'm coming to find you... Ana."
|
||||
)
|
||||
|
||||
await generate_voice(
|
||||
text=kai_truth_2,
|
||||
voice=KAI_VOICE,
|
||||
output_path=OUTPUT_DIR / "04_kai_truth_part2.mp3",
|
||||
rate="+0%", # Normal pace, determined
|
||||
pitch="+2Hz" # Slightly higher, hopeful
|
||||
)
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("✅ ALL INTRO VOICES GENERATED!")
|
||||
print("="*60)
|
||||
print(f"\nOutput directory: {OUTPUT_DIR}")
|
||||
print("\nGenerated files:")
|
||||
print(" 1. 01_narrator_flyover.mp3 (Narrator - The Flyover)")
|
||||
print(" 2. 02_kai_awakening.mp3 (Kai - Awakening)")
|
||||
print(" 3. 03_kai_truth_part1.mp3 (Kai - Reading ID)")
|
||||
print(" 4. 04_kai_truth_part2.mp3 (Kai - Determination)")
|
||||
|
||||
|
||||
async def generate_voice(text, voice, output_path, rate="+0%", pitch="+0Hz"):
|
||||
"""Generate single voice line"""
|
||||
print(f"\n🎙️ Generating: {output_path.name}")
|
||||
print(f" Voice: {voice}")
|
||||
print(f" Text: {text[:60]}...")
|
||||
|
||||
communicate = edge_tts.Communicate(text, voice, rate=rate, pitch=pitch)
|
||||
await communicate.save(str(output_path))
|
||||
|
||||
size = output_path.stat().st_size
|
||||
print(f" ✅ Saved: {size:,} bytes")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(generate_intro_voices())
|
||||
@@ -61,6 +61,12 @@ class PreloadScene extends Phaser.Scene {
|
||||
|
||||
const basePath = 'assets/audio/voices/';
|
||||
|
||||
// 🎬 INTRO CUTSCENE VOICES (NEW!)
|
||||
this.loadAudioSafe('intro_flyover', 'assets/audio/voiceover/intro/01_narrator_flyover.mp3');
|
||||
this.loadAudioSafe('intro_awakening', 'assets/audio/voiceover/intro/02_kai_awakening.mp3');
|
||||
this.loadAudioSafe('intro_truth_1', 'assets/audio/voiceover/intro/03_kai_truth_part1.mp3');
|
||||
this.loadAudioSafe('intro_truth_2', 'assets/audio/voiceover/intro/04_kai_truth_part2.mp3');
|
||||
|
||||
// Narrator (cinematic)
|
||||
this.loadAudioSafe('narrator_intro', basePath + 'narrator/intro_cutscene.mp3');
|
||||
this.loadAudioSafe('narrator_memory', basePath + 'narrator/kai_memory_ana.mp3');
|
||||
|
||||
Reference in New Issue
Block a user