Every line in the game is voiced. The script is the source of truth: a line
lives in a Dialogue Manager file with a static [ID:…], and that ID names
its audio file, its manifest entry, its lock entry and its translation key.
Nothing is generated twice for the same text.
Proven in Past:
extract,diff,report,post, the envelope table and the runtime (Voice autoload, Say holding lines, stall fallback, browser click). Not proven yet: casting, generation (voice) and review (check). Past's plan 003 slice 2 waits on the ElevenLabs plan and credit ceiling. Those steps are written in references/designed-process.md as the designed process, not a measured one. Run them on a small first batch and measure before any volume.
Read RULES.md before generating anything. Commands are in WORKFLOW.md.
The loop
script ([ID:], [#vo=]) -> extract -> manifest -> diff vs lock -> voice (new + changed only)
|
game line + envelope <- post <- masters <- check + operator listen <-+
- Lines first. Every spoken line has a static
[ID:…]and, where it matters, direction as a tag:[#vo=whispers]. Direction never goes into the spoken text.tools/test.sh's lint fails on a missing or duplicate ID. - Extract.
bun tools/audio/run.ts extractreadscontent/dialogue/**intoaudio-source/voice/manifest.json: ID, speaker, text, direction,file:lineand a text hash. Responses (- text) are spoken by the hero, named intools/audio/config.jsonasplayer. - Diff.
bun tools/audio/run.ts difflists new, changed and orphaned lines againstaudio-source/voice/lock.json. Only those cost credits. - Cast and generate (designed, not proven): one Voice Design voice per character, one model per character, one request per line, cached by hash.
- Review (designed): word match against the script, then the operator listens. Claude cannot hear.
- Post.
bun tools/audio/run.ts posttrims each master (80 ms lead, 200 ms tail), sets one gain to -18 LUFS capped at -1 dBTP, writes a 24 kHz mono WAV toassets/audio/voice/en/<ID>.wav, and writes the 30-a-second envelope tableassets/audio/voice/en/envelopes.gd. Only changed masters are posted again. - Import and play.
tools/test.sh(or the editor) imports the WAVs as QOA.Voice.play(id)streams the line on the Voice bus;Sayholds the subtitle for it; the hero's body eases towardVoice.level(). - Prove it in both builds with the
voiceQA scenario (see ship-check).
Quick start
bun tools/audio/run.ts extract # manifest from content/dialogue
bun tools/audio/run.ts diff # what would cost credits
bun tools/audio/run.ts report # credits used of the limit (needs ELEVENLABS_API_KEY)
# masters land in audio-source/voice/masters/<ID>.wav (generation: designed-process.md)
bun tools/audio/run.ts post # game lines + envelopes.gd
tools/test.sh # imports as QOA, runs GUT and the lint
Per-batch checklist
- Every new line has an
[ID:…], a speaker that matches an actor'sspeaker_nameor one of itsspeaker_aliases, and[#vo=…]on turning points. extractruns clean (a repeated ID is an error) anddiffshows only the lines you meant to change.reportbefore and after the batch, and spend stays under 80% of the operator's credit ceiling.- Masters are kept in
audio-source/voice/masters/and never overwritten without a reason recorded in the lock. postreports each line's seconds and gain; no line hits the -1 dBTP cap by surprise.tools/test.shpasses, and--qa=voicepasses in the exported desktop app and in Chrome.
Do not
- Do not put emotion in the spoken text. It is read aloud. Use tags.
- Do not change an ID to regenerate a line. Change the text; the hash marks it.
- Do not use Voice Library voices for the cast. Owners can withdraw them.
- Do not mix models within one character.
- Do not request Opus. Godot cannot import it.
- Do not ship WAVs or loose audio files. The export carries QOA inside the pack.
- Do not analyse audio at runtime. Envelopes are computed offline.
- Do not print or commit
ELEVENLABS_API_KEY. It lives in.env.
Next: ship-check proves the voiced lines in both builds.