8.6 KiB
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
-
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.mdwith timestamps + perceived-volume notes. -
Apricot deployment — rsync new
.oggfiles to the run host so the in-game session and any autoplay batches actually have audio. Already covered by the existingtools/deploy.shrsync (assets are tracked); just run it. -
p2-16close-out: flip statusin_progress → doneonce the smoke test lands + screenshot attached.
P1 — quality-of-life follow-ups
-
Bespoke melee variants per dwarf unit. Categorical
unit.melee.attackcovers everything; theplay_for_entityladder already supports<unit_id>.attackif you want to give Spearmen vs Halberdiers different swings later. One-line manifest entry per bespoke voicing, no code change. -
Music swap on golden age.
audio_manager.gd::_on_golden_age_startedswitches to thegolden_agetrack viaplay_music. Ongolden_age_endedit callsstop_music(). Verify the era track resumes correctly on the nextera_changed(or proactively re-queue it). Two-line patch to_on_golden_age_ended. -
Pre-roll defeat music as the final fade-out. Right now
defeat_stingeranddefeatmusic play simultaneously onplayer_eliminated. Consider a 0.5 s gap so the stinger is heard cleanly before the music swells. Trivial in_on_player_eliminated. -
Loudness audit. All files normalised at
loudnorm I=-16 TP=-3one-pass. For music tracks, two-pass loudnorm gives more accurate integrated LUFS. Runffmpeg -af loudnorm=…:print_format=jsonto get measurement, then re-encode. Optional polish. -
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. -
Credits screen entry.
LICENSES.mdis 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 acredits-audio.jsonfromsources.csvor read it directly.
P2 — wider integrations
-
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. -
Per-instance pitch jitter on
unit_moved. The throttle keeps it tame, but every footstep is identical. Addingpitch_jitter: 0.05to that manifest entry would diversify it. One-line change. -
Mod overrides —
user://overrides/audio.jsonmerged over the theme manifest atAudioManager.load_theme(…). Hook reserved in p2-33 notes; ~10 LOC. -
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--themeso re-running them on each pack works. -
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>.selectand<unit_id>.commandkeys when authoring catches up.
P3 — polish / not blocking launch
-
Design app: download
.oggfrom 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. -
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).