magicciv/.project/audio-status.md
Natalie 0d26c271b3 feat(@projects): add audio system integration
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-04-28 16:57:48 -04:00

8.6 KiB
Raw Blame History

Audio status — what's done, what's left

Date: 2026-04-28 Objectives in flight: p2-33 (system extension, done), p2-16 (asset acquisition, in_progress — code/tooling complete, asset curation 65 / 65 done, in-game smoke test pending).

Tally

Layer State
Manifest schema (audio.schema.json) ✓ ships — typed streams[], pitch_jitter, fallback, _silent sentinel
audio_manager.gd extensions ✓ ships — random variant pick, fallback chain, play_for_entity, 12 EventBus wires
Manifest entries (audio.json) ✓ 49 SFX + 8 music tracks (added defeat_stinger + defeat)
Validator (audio-validate.py) ✓ schema, asset coherence, orphan scan
License renderer (audio-licenses-render.py) ✓ renders LICENSES.md, rejects -SA / -NC, allowlist gate
Batch driver (audio-fetch-batch.sh) ✓ idempotent, supports direct URLs + zip#inner paths
./run audio family ✓ status / fetch / fetch:NN / design / validate / render
Design app /audio page ✓ resolution playground + manifest browser + mixer + music list, ▶ synth + ● real buttons
Vite glob hardening server.watch + server.fs.allow so new files HMR cleanly
.gitignore for stray .js outputs ✓ design-app source tree
Real .ogg files on disk 65 / 65 — validator OK, no orphans, no missing
GUT test test_audio_manager.gd ✓ 13 / 13 pass on apricot headless
Design doc + interactive page audio-system.md + React app at /audio

Outstanding work (priority order)

P0 — to close p2-16

  1. In-game audible smoke test on plum/apricot. Boot the live game (not the design app), fire each EventBus signal kind manually: turn_started / turn_ended / city_founded / city_grew / city_starved / tech_researched / culture_researched / combat_started / combat_resolved / unit_destroyed / wonder_built / era_changed / golden_age_started / victory_achieved / player_eliminated (defeat) / wild_creature_spawned / weather_event_applied. Confirm each plays through the right bus (Master / Music / SFX sliders affect them as expected). Why this is non-trivial: requires a live game session with real players and rendering, not headless. Output: a smoke-checklist markdown at .project/screenshots/audio-smoke-2026-XX-XX.md with timestamps + perceived-volume notes.

  2. Apricot deployment — rsync new .ogg files to the run host so the in-game session and any autoplay batches actually have audio. Already covered by the existing tools/deploy.sh rsync (assets are tracked); just run it.

  3. p2-16 close-out: flip status in_progress → done once the smoke test lands + screenshot attached.

P1 — quality-of-life follow-ups

  1. Bespoke melee variants per dwarf unit. Categorical unit.melee.attack covers everything; the play_for_entity ladder already supports <unit_id>.attack if you want to give Spearmen vs Halberdiers different swings later. One-line manifest entry per bespoke voicing, no code change.

  2. Music swap on golden age. audio_manager.gd::_on_golden_age_started switches to the golden_age track via play_music. On golden_age_ended it calls stop_music(). Verify the era track resumes correctly on the next era_changed (or proactively re-queue it). Two-line patch to _on_golden_age_ended.

  3. Pre-roll defeat music as the final fade-out. Right now defeat_stinger and defeat music play simultaneously on player_eliminated. Consider a 0.5 s gap so the stinger is heard cleanly before the music swells. Trivial in _on_player_eliminated.

  4. Loudness audit. All files normalised at loudnorm I=-16 TP=-3 one-pass. For music tracks, two-pass loudnorm gives more accurate integrated LUFS. Run ffmpeg -af loudnorm=…:print_format=json to get measurement, then re-encode. Optional polish.

  5. Music transition crossfade on era change. Already implemented (_crossfade_seconds = 2.0). Verify it sounds right with the actual Junkala tracks — they may need 4-5 s rather than 2.

  6. Credits screen entry. LICENSES.md is the canonical source; the player-facing credits screen (scenes/menus/credits.gd) should pull author lines for CC-BY entries automatically. Currently we have one CC-BY entry (Bogart VGM defeat music). Either render a credits-audio.json from sources.csv or read it directly.

P2 — wider integrations

  1. HUD entry points for tech tree + culture tree (deferred from the tree work). Currently the proof scenes open them directly; the live HUD has no button or hotkey. Two scene edits + EventBus tech_tree_opened / culture_tree_opened. About 30 LOC each.

  2. Per-instance pitch jitter on unit_moved. The throttle keeps it tame, but every footstep is identical. Adding pitch_jitter: 0.05 to that manifest entry would diversify it. One-line change.

  3. Mod overridesuser://overrides/audio.json merged over the theme manifest at AudioManager.load_theme(…). Hook reserved in p2-33 notes; ~10 LOC.

  4. Game 2 / Game 3 packs. When Kzzykt and Elves ship, public/games/<theme>/data/audio.json + assets/audio/** follow the same shape. The runtime (audio_manager.gd) is already theme-agnostic; the validator + renderer accept --theme so re-running them on each pack works.

  5. Per-unit voice lines (post-EA exploration). "Aye!" / "For the Mountain!" — out of scope for launch but the per-entity resolution ladder already supports <unit_id>.select and <unit_id>.command keys when authoring catches up.

P3 — polish / not blocking launch

  1. Design app: download .ogg from page. Right-click → save works already, but a small "download" link next to each ● button would let designers grab files for tweaking outside the pipeline. Optional.

  2. Per-pillar weather mood. Weather-event SFX is currently one-shot. A persistent ambient layer that fades up when a storm is over the camera would be richer; needs a new bus or stem layering in audio_manager.gd. Out of scope for launch.

Source-of-truth files

File Purpose
public/games/age-of-dwarves/data/audio.json Canonical manifest (49 SFX + 8 music)
public/games/age-of-dwarves/assets/audio/sources.csv Provenance ledger — 65 rows, all licences verified
public/games/age-of-dwarves/assets/audio/LICENSES.md Auto-rendered from sources.csv; CI fails on hand-edit
public/games/age-of-dwarves/assets/audio/{sfx,music}/**.ogg The shipped pack
tools/audio-batch-{01..05}-*.tsv Per-pack mappings, idempotent
tools/audio-fetch-batch.sh / audio-validate.py / audio-licenses-render.py Pipeline tools
scripts/run/audio.sh ./run audio:* family
src/game/engine/src/autoloads/audio_manager.gd The engine (~430 LOC)
src/game/engine/tests/unit/test_audio_manager.gd GUT cases (13 / 13 pass)
.project/designs/audio-system.md Static design doc
.project/designs/app/src/pages/AudioSystem.tsx Interactive design page
.project/audio-sourcing-checklist.md Per-row punch list (now 100% closed for the 65-file pack)

Source provenance summary

Pack Licence Files Source page
Calinou/kenney-interface-sounds (GitHub) CC0-1.0 11 https://github.com/Calinou/kenney-interface-sounds
Kenney Impact Sounds CC0-1.0 26 https://kenney.nl/assets/impact-sounds
Junkala JRPG Pack 2 (Towns) CC0-1.0 4 https://opengameart.org/content/jrpg-pack-2-towns
Junkala JRPG Pack 5 (Action) CC0-1.0 3 https://opengameart.org/content/jrpg-pack-5-action
rubberduck "30 CC0 SFX loops" CC0-1.0 6 https://opengameart.org/content/30-cc0-sfx-loops
rubberduck "80 CC0 creature SFX" CC0-1.0 12 https://opengameart.org/content/80-cc0-creature-sfx
Bogart VGM "Game over man" CC-BY-4.0 (1 attr line) 1 https://opengameart.org/content/gb-victory-stinger-and-game-over-man
Calinou kenney-interface-sounds (extras) CC0-1.0 2 https://github.com/Calinou/kenney-interface-sounds

Licence breakdown: 64 × CC0-1.0 + 1 × CC-BY-4.0 (Bogart VGM, attribution recorded in sources.csv and rendered to LICENSES.md).