PROCITY/docs/LANES/LANE_C_AUDIO.md
m3ultra 89f278b603 Lane C: round-11 audio contract — room.audio on buildInterior
buildInterior returns room.audio = { musicKey, toneKey }, seeded per shop: keys into Lane E's manifest.audio (interior music bed + room-tone). Lane F's interior_mode plays it, B's audio.js resolves keys->files. Keys not files: buildInterior stays synchronous + asset-free; silence on missing/mute/noassets (house audio law).

Maps mirror the manifest per-type types arrays: record/milkbar/video/dept/arcade always play their bed; other types room-tone-only except a seeded ~1-in-3 general radio (the 'general'-tagged milk-bar bed).

Verified 9 types x 8 seeds: 0 bad keys, 0 nondeterminism, all keys resolve + match manifest type membership, deterministic per shop. Contract + F wiring: docs/LANES/LANE_C_AUDIO.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:46:58 +10:00

51 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# LANE C — audio contract (round 11) → for Lane F + Lane B
*PROCITY-C, 2026-07-15. Answers the R11 §Lane C question: "where should the interior bed + room-tone
attach?" **Confirmed**`buildInterior` now returns `room.audio = { musicKey, toneKey }`, seeded per
shop. Lane F's interior_mode just plays it; Lane B's audio.js resolves the keys → files.*
## The contract
```js
room.audio = {
musicKey, // string | null — an interior MUSIC bed key in manifest.audio.music, or null (no music)
toneKey, // string — an interior ROOM-TONE key in manifest.audio.ambience (scope:'interior')
}
```
- **Keys, not files.** `buildInterior` stays synchronous and asset-free — it only *names* what should
play. Resolve `musicKey``manifest.audio.music[musicKey]`, `toneKey`
`manifest.audio.ambience[toneKey]` (the `.ogg` + `.m4a` fallback + `gain`/`loop` are on those entries).
- **Silence is legal** (house audio law): missing key / `?mute=1` / `?noassets=1` → play nothing, no error.
`musicKey` is frequently `null` — treat "no music, room-tone only" as the common case.
- **Seeded per shop, stable per revisit.** Derived from `shop.seed` on its own sub-stream, so the same
shop is silent-or-playing identically every visit (no per-frame or per-enter randomness).
## What each type resolves to (mirrors manifest.audio `types` arrays — one source of truth)
| type | toneKey | musicKey |
|---|---|---|
| record | roomtone-retail | **record-shop** (always — "record shop plays music") |
| milkbar | roomtone-milkbar | **milkbar** (radio, always) |
| video | roomtone-video | **video-synth** (always) |
| dept, arcade | roomtone-video | **arcade** (always) |
| opshop, book, toy, pawn, stall | roomtone-retail | **null**, or **milkbar** for a seeded ~1-in-3 (a general radio playing — the milk-bar bed is `types:[…,'general']`) |
Verified (9 types × 8 seeds): 0 bad keys, 0 nondeterminism, every key resolves + matches its manifest
`types` membership; dedicated-music types are stable, non-music types vary on/off by seed.
## Lane F wiring (interior_mode)
```
on enter(room):
if room.audio.toneKey: playLoop(ambience[room.audio.toneKey], fadeIn) // room-tone always
if room.audio.musicKey: playLoop(music[room.audio.musicKey], fadeIn) // may be null → skip
on exit / dispose(room):
fadeOut + stop both, release AudioNodes // your enter/exit leak smoke checks this
```
Nothing before the first gesture; `?mute=1`/`?noassets=1` short-circuit before any fetch. `room.audio`
is plain data on the return — no new lifecycle, no dispose hook needed from Lane C's side.
## Optional (deferred to v2.2, per the round scope)
A `places`-tagged visual source ("the music comes from *there*") — e.g. a radio/hifi mesh with
`userData.audioEmitter = musicKey` so Lane B can position/spatialise the bed. Only the record shop has a
fitting for it today (`listeningCorner`); milk-bar/video would need a new radio prop. Not in this round —
proposing the `userData.audioEmitter` hook + the two props as a tidy v2.2 item. v2.1 plays the bed
non-spatially (room-filling), which is the right default for an interior anyway.