📊 Jan 8 Audio System Complete Organization + Generation Manifest

 AUDIO REORGANIZATION COMPLETE:

**Files Moved:**
- Moved 2 SFX WAV: footstep_grass.wav, wood_chop.wav → /sfx/
- Moved 1 Music MP3: forest_ambient.mp3 → /music/
- Moved 43 Voiceover WAV: prologue_sl files → /voiceover/
- Existing 20 Voice MP3: Ana, Kai, Mayor, Narrator, Teacher → /voices/

**Folder Structure Created:**
/assets/audio/
├── sfx/
│   ├── farming/ (8 placeholder .txt)
│   ├── combat/ (8 placeholder .txt)
│   ├── building/ (5 placeholder .txt)
│   └── misc/ (4 placeholder .txt)
├── music/ (8 placeholder .txt + 1 MP3 )
├── voices/ (20 MP3 )
└── voiceover/ (43 WAV )

📋 CREATED DOCUMENTATION:

1. **AUDIO_GENERATION_MANIFEST.md**
   - Complete list of 33 missing audio files
   - Detailed specifications (duration, format, mood)
   - SFX: 25 files (farming, combat, building, misc)
   - Music: 8 files (themes, ambients, victory)
   - Generation instructions (AI tools, free libraries)

2. **scripts/convert_audio_to_ogg.py**
   - Auto-remove .txt placeholders
   - Convert MP3/WAV → OGG (ffmpeg)
   - Verify file integrity
   - Generate audioManifest.json for Phaser

 STILL MISSING:
- 25 SFX .ogg files (placeholders only)
- 7 Music .ogg files (placeholders only)

🎯 NEXT STEPS:
1. Generate audio using AI tools (ElevenLabs, Suno, etc.)
2. Run: python3 scripts/convert_audio_to_ogg.py
3. Verify all 33 files present

**Current Audio Status:** 66/99 files (67% complete)
This commit is contained in:
2026-01-08 15:50:30 +01:00
parent f309374ff5
commit 5b07de56da
50 changed files with 4937 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
# 🎵 **AUDIO GENERATION MANIFEST - JAN 8, 2026**
**COMPLETE LIST OF ALL MISSING AUDIO FILES**
---
## 📊 **SUMMARY:**
- **SFX Needed:** 25 files (.ogg format)
- **Music Needed:** 8 files (.ogg format)
- **Total:** 33 audio files to generate
---
## 🔊 **1. SOUND EFFECTS (SFX) - 25 FILES**
### **FARMING SOUNDS (8 files)** 🌾
**Path:** `/assets/audio/sfx/farming/`
| Filename | Description | Duration | Notes |
|----------|-------------|----------|-------|
| `cow_moo.ogg` | Cow mooing sound | 2-3s | Friendly, farm animal |
| `dig.ogg` | Digging/hoeing soil | 1s | Shovel into dirt |
| `harvest.ogg` | Crop harvest/pickup | 0.5s | Satisfying pop/snap |
| `plant_seed.ogg` | Planting seed in soil | 0.5s | Soft thud |
| `scythe_swing.ogg` | Scythe swinging through air | 0.8s | Whoosh sound |
| `stone_mine.ogg` | Pickaxe hitting stone | 1s | Clink/chip sound |
| `tree_chop.ogg` | Axe chopping wood | 1s | Thunk/chop |
| `water_crop.ogg` | Watering can pouring | 1.5s | Water splash/trickle |
---
### **COMBAT SOUNDS (8 files)** ⚔️
**Path:** `/assets/audio/sfx/combat/`
| Filename | Description | Duration | Notes |
|----------|-------------|----------|-------|
| `bow_release.ogg` | Arrow release from bow | 0.3s | Twang sound |
| `explosion.ogg` | Explosion/bomb | 2s | Boom + debris |
| `player_hurt.ogg` | Player damage grunt | 0.5s | Oof/ugh |
| `raider_attack.ogg` | Enemy attack yell | 1s | Aggressive shout |
| `shield_block.ogg` | Shield blocking hit | 0.5s | Metallic clang |
| `sword_slash.ogg` | Sword swing | 0.5s | Whoosh + metal |
| `zombie_death.ogg` | Zombie dies | 1.5s | Groan + thud |
| `zombie_hit.ogg` | Zombie takes damage | 0.5s | Hurt groan |
---
### **BUILDING SOUNDS (5 files)** 🏗️
**Path:** `/assets/audio/sfx/building/`
| Filename | Description | Duration | Notes |
|----------|-------------|----------|-------|
| `chest_open.ogg` | Chest opening | 1s | Creaky wood |
| `door_close.ogg` | Door closing | 0.8s | Wood door slam |
| `door_open.ogg` | Door opening | 0.8s | Creaky hinges |
| `hammer_nail.ogg` | Hammering nail | 0.5s | Metallic bang |
| `repair.ogg` | Building repair | 1.5s | Construction sounds |
---
### **MISC SOUNDS (4 files)** ✨
**Path:** `/assets/audio/sfx/misc/`
| Filename | Description | Duration | Notes |
|----------|-------------|----------|-------|
| `coin_collect.ogg` | Picking up coin | 0.3s | Bright ching! |
| `footstep_grass.ogg` | Footstep on grass | 0.3s | Soft rustle (HAVE .wav, convert!) |
| `footstep_stone.ogg` | Footstep on stone | 0.3s | Hard tap |
| `level_up.ogg` | Level up/achievement | 2s | Triumphant chime |
---
## 🎶 **2. MUSIC TRACKS - 8 FILES**
### **BACKGROUND MUSIC (.ogg format)**
**Path:** `/assets/audio/music/`
| Filename | Description | Duration | Loop | BPM | Mood |
|----------|-------------|----------|------|-----|------|
| `forest_ambient.mp3` | **✅ HAVE!** Forest sounds | - | Yes | - | Peaceful |
| `main_theme.ogg` | Main menu theme | 2-3min | Yes | 90-110 | Epic/Adventure |
| `farm_ambient.ogg` | Farm/grassland loop | 2-3min | Yes | 70-90 | Calm/Peaceful |
| `town_theme.ogg` | Town restoration theme | 2min | Yes | 100-120 | Hopeful/Uplifting |
| `combat_theme.ogg` | Battle music | 2min | Yes | 130-150 | Intense/Action |
| `night_theme.ogg` | Nighttime ambient | 3min | Yes | 60-80 | Mysterious/Calm |
| `victory_theme.ogg` | Quest complete | 30s | No | 120 | Triumphant |
| `raid_warning.ogg` | Raid approaching | 1min | No | 140-160 | Tense/Urgent |
| `ana_theme.ogg` | Ana's memory theme | 2min | No | 80 | Emotional/Sad |
---
## 🛠️ **GENERATION INSTRUCTIONS:**
### **Option 1: AI Sound Generation (Recommended)**
Use services like:
- **ElevenLabs Sound Effects** - AI SFX generation
- **Suno AI** or **Udio** - Music generation
- **Soundraw** - Royalty-free music generator
### **Option 2: Free Sound Libraries**
Download from:
- **Freesound.org** - Community sound library
- **OpenGameArt.org** - Game audio assets
- **Incompetech** - Royalty-free music (Kevin MacLeod)
### **Option 3: Script Generation (Placeholder)**
Use `/scripts/generate_placeholder_audio.py` to create:
- Simple tone beeps (SFX placeholders)
- White noise loops (ambient placeholders)
---
## 📋 **CONVERSION CHECKLIST:**
After generating, run:
```bash
python3 /Users/davidkotnik/repos/novafarma/scripts/convert_audio_to_ogg.py
```
This will:
1. Convert all .mp3/.wav to .ogg
2. Remove .txt placeholders
3. Verify file sizes
4. Generate audio manifest
---
## ✅ **COMPLETION CRITERIA:**
- [ ] All 25 SFX .ogg files present
- [ ] All 8 music .ogg files present
- [ ] Each file is 5KB+ (not empty)
- [ ] Audio plays correctly in Phaser 3
- [ ] Volume normalized (-14 LUFS)
---
**Status:** 📝 Manifest ready, awaiting audio generation
**Last Updated:** 2026-01-08 15:48 CET

View File

@@ -0,0 +1,35 @@
# 🔍 **COMPLETE ASSET & SYSTEMS CHECK - JAN 8, 2026 (15:41 CET)**
**SYSTEMATIČNI PREGLED OD ZAČETKA DO KONCA**
---
## 📋 **METODOLOGIJA:**
1. ✅ Pregledam DEMO_FAZA1_FAZA2_OVERVIEW.md (kaj MORA bit)
2. ✅ Preverim vse /assets/references/ folders (kaj IMO)
3. ✅ Primerjam dokumentacijo vs realnost
4. ✅ Naredim seznam manjkajočih elementov
5. ✅ Prioritiziram kaj dodat
---
## 📊 **CATEGORY 1: CHARACTER ANIMATIONS**
### **Kaj MORA bit (iz docs):**
- Kai: idle (5), walk (6), dig (5), swing (5) = 21 ✅
- Ana: idle (4), walk (6) = 10 ✅
- Gronk: idle (4), walk (6) = 10 ✅
- Susi: idle (4), run (6), bark (2) = 12 ✅
**TOTAL NEEDS:** 53 sprites
### **Kaj IMO v /references:**
46
**STATUS:** ✅ CHECKING...
---
## 📊 **RUNNING SYSTEMATIC CHECK...**

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

221
scripts/complete_asset_audit.py Executable file
View File

@@ -0,0 +1,221 @@
#!/usr/bin/env python3
"""
Complete Asset Audit Script
Systematically checks ALL assets vs documentation
"""
import os
from pathlib import Path
from collections import defaultdict
# Base paths
REPO_ROOT = Path("/Users/davidkotnik/repos/novafarma")
ASSETS_REF = REPO_ROOT / "assets" / "references"
# Expected counts from DEMO_FAZA1_FAZA2_OVERVIEW.md
EXPECTED = {
"characters": {
"kai": 21, # idle(5) + walk(6) + dig(5) + swing(5)
"ana": 10, # idle(4) + walk(6)
"gronk": 10, # idle(4) + walk(6)
},
"companions": {
"susi": 12, # idle(4) + run(6) + bark(2)
},
"zombies": 45, # 3 types × 15 frames each
"crops": {
"wheat": 5,
"carrot": 5,
"tomato": 5,
"potato": 5,
"corn": 5,
},
"tools": 8,
"ui": 28,
"grassland": 27,
}
def count_pngs(directory):
"""Count PNG files in directory recursively"""
if not directory.exists():
return 0
return len(list(directory.rglob("*.png")))
def audit_characters():
"""Audit main characters"""
print("\n## 1. MAIN CHARACTERS")
print("=" * 60)
char_path = ASSETS_REF / "main_characters"
results = {}
for char in ["kai", "ana", "gronk"]:
char_dir = char_path / char
actual = count_pngs(char_dir)
expected = EXPECTED["characters"][char]
status = "" if actual >= expected else ""
gap = actual - expected
results[char] = {
"expected": expected,
"actual": actual,
"status": status,
"gap": gap
}
print(f"\n{char.upper()}:")
print(f" Expected: {expected}")
print(f" Actual: {actual}")
print(f" Status: {status} ({'+' if gap >= 0 else ''}{gap})")
return results
def audit_companions():
"""Audit companion animals"""
print("\n## 2. COMPANIONS")
print("=" * 60)
comp_path = ASSETS_REF / "companions"
results = {}
for comp in ["susi"]:
comp_dir = comp_path / comp
actual = count_pngs(comp_dir)
expected = EXPECTED["companions"][comp]
status = "" if actual >= expected else ""
gap = actual - expected
results[comp] = {
"expected": expected,
"actual": actual,
"status": status,
"gap": gap
}
print(f"\n{comp.upper()}:")
print(f" Expected: {expected}")
print(f" Actual: {actual}")
print(f" Status: {status} ({'+' if gap >= 0 else ''}{gap})")
return results
def audit_crops():
"""Audit crop growth stages"""
print("\n## 3. CROPS")
print("=" * 60)
crops_path = ASSETS_REF / "crops"
results = {}
for crop in ["wheat", "carrot", "tomato", "potato", "corn"]:
crop_dir = crops_path / crop / "growth_stages"
actual = count_pngs(crop_dir)
expected = EXPECTED["crops"][crop]
status = "" if actual >= expected else ""
gap = actual - expected
results[crop] = {
"expected": expected,
"actual": actual,
"status": status,
"gap": gap
}
print(f"\n{crop.upper()}:")
print(f" Expected: {expected}")
print(f" Actual: {actual}")
print(f" Status: {status} ({'+' if gap >= 0 else ''}{gap})")
return results
def audit_audio():
"""Audit audio files"""
print("\n## 4. AUDIO")
print("=" * 60)
audio_path = ASSETS_REF.parent / "audio"
# Count voice files
voices = audio_path / "voices"
voice_count = len(list(voices.rglob("*.mp3"))) if voices.exists() else 0
# Count sound effects
sfx = audio_path / "sfx"
sfx_count = len(list(sfx.rglob("*.wav"))) if sfx.exists() else 0
# Count music
music_path = REPO_ROOT / "music"
music_count = len(list(music_path.rglob("*.mp3"))) if music_path.exists() else 0
music_count += len(list(music_path.rglob("*.wav"))) if music_path.exists() else 0
music_count += len(list(music_path.rglob("*.ogg"))) if music_path.exists() else 0
print(f"\nVOICES (MP3): {voice_count}")
print(f"SOUND EFFECTS (WAV): {sfx_count}")
print(f"MUSIC: {music_count} {'❌ MISSING!' if music_count == 0 else ''}")
return {
"voices": voice_count,
"sfx": sfx_count,
"music": music_count
}
def generate_report():
"""Generate complete audit report"""
print("\n" + "="*60)
print("🔍 COMPLETE ASSET AUDIT - JAN 8, 2026")
print("="*60)
# Run all audits
chars = audit_characters()
comps = audit_companions()
crops = audit_crops()
audio = audit_audio()
# Summary
print("\n" + "="*60)
print("📊 SUMMARY")
print("="*60)
total_expected = sum(EXPECTED["characters"].values())
total_expected += sum(EXPECTED["companions"].values())
total_expected += sum(EXPECTED["crops"].values())
total_expected += EXPECTED["zombies"]
total_expected += EXPECTED["tools"]
total_expected += EXPECTED["ui"]
total_actual = count_pngs(ASSETS_REF)
print(f"\nTOTAL PNG FILES: {total_actual}")
print(f"AUDIO FILES: {audio['voices'] + audio['sfx'] + audio['music']}")
print(f" - Voices: {audio['voices']}")
print(f" - SFX: {audio['sfx']}")
print(f" - Music: {audio['music']} {'' if audio['music'] == 0 else ''}")
# Missing items
print("\n" + "="*60)
print("❌ MISSING / NEEDS ATTENTION")
print("="*60)
missing = []
for char, data in chars.items():
if data['gap'] < 0:
missing.append(f" - {char.upper()}: {abs(data['gap'])} sprites short")
for crop, data in crops.items():
if data['gap'] < 0:
missing.append(f" - {crop.upper()}: {abs(data['gap'])} sprites short")
if audio['music'] == 0:
missing.append(f" - MUSIC: Need 3+ background tracks")
if missing:
for item in missing:
print(item)
else:
print(" ✅ ALL ASSETS COMPLETE!")
print("\n" + "="*60)
if __name__ == "__main__":
generate_report()

203
scripts/convert_audio_to_ogg.py Executable file
View File

@@ -0,0 +1,203 @@
#!/usr/bin/env python3
"""
Audio Conversion & Cleanup Script
Converts audio files to .ogg, removes placeholders, verifies integrity
"""
import os
import subprocess
from pathlib import Path
REPO_ROOT = Path("/Users/davidkotnik/repos/novafarma")
AUDIO_ROOT = REPO_ROOT / "assets" / "audio"
def remove_txt_placeholders():
"""Remove all .txt placeholder files"""
print("\n🗑️ Removing .txt placeholders...")
txt_files = list(AUDIO_ROOT.rglob("*.txt"))
for txt_file in txt_files:
txt_file.unlink()
print(f" ✅ Removed: {txt_file.name}")
print(f"\n✅ Removed {len(txt_files)} placeholder files")
def convert_to_ogg():
"""Convert all MP3/WAV to OGG using ffmpeg"""
print("\n🔄 Converting audio files to .ogg...")
# Find all MP3 and WAV files
audio_files = []
audio_files.extend(AUDIO_ROOT.rglob("*.mp3"))
audio_files.extend(AUDIO_ROOT.rglob("*.wav"))
converted = 0
for audio_file in audio_files:
ogg_file = audio_file.with_suffix(".ogg")
# Skip if .ogg already exists
if ogg_file.exists():
print(f" ⏭️ Skip: {ogg_file.name} (exists)")
continue
# Convert using ffmpeg
try:
cmd = [
"ffmpeg",
"-i", str(audio_file),
"-c:a", "libvorbis",
"-q:a", "6", # Quality 6 (good balance)
str(ogg_file)
]
result = subprocess.run(
cmd,
capture_output=True,
text=True
)
if result.returncode == 0:
print(f" ✅ Converted: {audio_file.name}{ogg_file.name}")
converted += 1
else:
print(f" ❌ Failed: {audio_file.name}")
print(f" Error: {result.stderr[:100]}")
except FileNotFoundError:
print("\n❌ ERROR: ffmpeg not found!")
print("Install with: brew install ffmpeg")
return False
print(f"\n✅ Converted {converted} files to .ogg")
return True
def verify_audio():
"""Verify all audio files exist and are valid"""
print("\n🔍 Verifying audio files...")
# Expected files from manifest
expected = {
"sfx/farming": ["cow_moo", "dig", "harvest", "plant_seed", "scythe_swing", "stone_mine", "tree_chop", "water_crop"],
"sfx/combat": ["bow_release", "explosion", "player_hurt", "raider_attack", "shield_block", "sword_slash", "zombie_death", "zombie_hit"],
"sfx/building": ["chest_open", "door_close", "door_open", "hammer_nail", "repair"],
"sfx/misc": ["coin_collect", "footstep_grass", "footstep_stone", "level_up"],
"music": ["main_theme", "farm_ambient", "town_theme", "combat_theme", "night_theme", "victory_theme", "raid_warning", "ana_theme", "forest_ambient"],
}
missing = []
found = []
for category, files in expected.items():
category_path = AUDIO_ROOT / category
for filename in files:
# Check both .ogg and original formats
ogg_file = category_path / f"{filename}.ogg"
mp3_file = category_path / f"{filename}.mp3"
wav_file = category_path / f"{filename}.wav"
if ogg_file.exists():
size = ogg_file.stat().st_size
if size > 1000: # At least 1KB
found.append(str(ogg_file.relative_to(AUDIO_ROOT)))
else:
missing.append(f"{category}/{filename}.ogg (too small: {size}B)")
elif mp3_file.exists() or wav_file.exists():
found.append(f"{category}/{filename} (needs conversion)")
else:
missing.append(f"{category}/{filename}.ogg")
print(f"\n✅ Found: {len(found)} files")
print(f"❌ Missing: {len(missing)} files")
if missing:
print("\n❌ MISSING FILES:")
for file in missing[:10]: # Show first 10
print(f" - {file}")
if len(missing) > 10:
print(f" ... and {len(missing) - 10} more")
return len(missing) == 0
def generate_manifest():
"""Generate audio file manifest for Phaser preload"""
print("\n📝 Generating audio manifest...")
manifest = {
"sfx": {},
"music": {},
"voices": {}
}
# Scan SFX
sfx_path = AUDIO_ROOT / "sfx"
for ogg_file in sfx_path.rglob("*.ogg"):
category = ogg_file.parent.name
filename = ogg_file.stem
if category not in manifest["sfx"]:
manifest["sfx"][category] = []
manifest["sfx"][category].append({
"key": f"{category}_{filename}",
"path": str(ogg_file.relative_to(AUDIO_ROOT.parent))
})
# Scan Music
music_path = AUDIO_ROOT / "music"
for audio_file in music_path.glob("*"):
if audio_file.suffix in [".ogg", ".mp3"]:
manifest["music"][audio_file.stem] = {
"key": audio_file.stem,
"path": str(audio_file.relative_to(AUDIO_ROOT.parent))
}
# Scan Voices
voices_path = AUDIO_ROOT / "voices"
for char_dir in voices_path.iterdir():
if char_dir.is_dir():
manifest["voices"][char_dir.name] = []
for mp3_file in char_dir.glob("*.mp3"):
manifest["voices"][char_dir.name].append({
"key": f"{char_dir.name}_{mp3_file.stem}",
"path": str(mp3_file.relative_to(AUDIO_ROOT.parent))
})
# Write manifest
manifest_file = REPO_ROOT / "src" / "data" / "audioManifest.json"
import json
manifest_file.parent.mkdir(parents=True, exist_ok=True)
with open(manifest_file, 'w') as f:
json.dump(manifest, f, indent=2)
print(f"✅ Manifest written to: {manifest_file}")
print(f" - SFX categories: {len(manifest['sfx'])}")
print(f" - Music tracks: {len(manifest['music'])}")
print(f" - Voice characters: {len(manifest['voices'])}")
def main():
"""Run all audio processing tasks"""
print("="*60)
print("🎵 AUDIO CONVERSION & CLEANUP")
print("="*60)
# Step 1: Remove placeholders
remove_txt_placeholders()
# Step 2: Convert to OGG
if not convert_to_ogg():
return
# Step 3: Verify
verify_audio()
# Step 4: Generate manifest
generate_manifest()
print("\n" + "="*60)
print("✅ AUDIO PROCESSING COMPLETE!")
print("="*60)
if __name__ == "__main__":
main()