488067004c
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
488067004c |
Lane C R28: audioEmitter — the till rings from the counter; the panner was inert
R28 ledger #3. My own idea, parked since R7. Brief stated acceptance only, so I scoped it. THE TRAP MY OWN v3.0 NOTE SET: it said "userData.audioEmitter = musicKey so Lane B can position/spatialise the bed". Both halves wrong. (1) Shape: the engine holds no reference to the room group and would traverse a scene it cannot reach — emitters belong on room.audio as data, riding the EXISTING playInterior() call (no lifecycle, no traverse, no dispose hook). (2) Method: "spatialise" invites a PannerNode. NOTHING in this tree sets ctx.listener (zero hits), so a panner resolves against a listener frozen at (0,0,0) — which, because rooms are built room-local at the origin, is the room's own CENTRE, forever. Measured, OfflineAudioContext, real pub stage bed: panner, listener never set (the engine as it SHIPS): RMS 0.106905 door / 0.106905 stage -> ratio 1.0000, bit-identical panner + listener tracking the player: RMS 0.055665 / 0.500000 -> 8.98x It plays, throws nothing, warns nothing, and never changes by one sample as you cross the room — while failing at the only thing it exists to do. "Routed through a panner?" yes. "Audible in a venue?" yes. "In a shop?" yes. All green, all worthless. -> ASSERT THE DELTA (gain at door != gain at stage), NEVER THE PRESENCE. Vacuous-gate law, 4th application — and this one had no author to blame but me. The house idiom was already right: tram/spill/gig-spill are all hand-rolled distance-gains vs PROCITY.camera.position. No listener, no panner. SHIPPED (C's files only): - room.audio.emitters {stage?, counter?} — room-local, 3dp, 18/18 same-seed deep-equal. `r` (reach) is the ROOM DIAGONAL, not a constant: measured diagonals 9.81-15.63m, and scaling to the room holds door->stage proximity in a 0.42-0.59 band from a pokey band_room to a hall pub. `floor` is the room-filling residue, never 0 — a PA fills a pub; what changes as you walk in is the BALANCE, not the presence. Swing 2.65-4.52 dB (John R20: subtle changes are great). - THE TILL RINGS FROM THE COUNTER — audible positioning in a normal shop with NO ENGINE CHANGE, via B's already-public playSfx(key,{gain}): C does its own distance math, hands B a scalar. Measured through the real BUY button, real record shop: 0.9489 @0.60m vs 0.6436 @4.19m = 3.37 dB; negative control (no emitters) fires at exactly 1.0. sfx-till.ogg has been on disk since 15 Jul and had NEVER ONCE PLAYED. Routing, not content — as the brief said. - ?mute=1 now actually silences the crate riffle (below). WHY THE VENUE HALF IS AN ASK: a one-shot resolves its gain once, at fire time, from a position C already knows. A bed must be re-gained every frame inside the engine's update() loop — B's file. Structural, not political. B has a ~12-line paste-ready diff in the house idiom; audio.js untouched. The letter of "consulted not modified" could have been met by reaching through B's exposed state.layers diag to rewire its graph from outside — that backdoor is strictly worse than the edit it avoids. Refused. TWO BUGS FOUND: - F DROPS EMITTERS ON THE ONE NIGHT THEY MATTER. interior_mode.js:289 rebuilds the spec on a gig night ({musicKey: ra.gigKey, toneKey: WALLA_KEY}) and every unnamed field dies. Measured on a real gig-night pub: quietNight_emittersSurvive TRUE, gigNight_emittersSurvive FALSE. Silent, fail-soft, no error. -> F: don't just spread `ra`; pass emitters DELIBERATELY, gig-night only — on a quiet night the radio + room-tone genuinely ARE room-filling. - ?mute=1 NEVER SILENCED THE RIFFLE (R5->R28). dig.js opened a SECOND, private AudioContext straight to destination, breaking both stated house laws (ONE AudioContext; mute forces silence). It survived three epochs because NO GATE ASSERTS MUTE => SILENCE: the engine returns its silent surface and looks honest from its own side, while the sound came from somewhere it never knew existed. A gate that asks the engine whether it is muted can only ever answer yes. Fixed via B's PUBLIC .muted getter (consulted, not modified), URL fallback when standalone. Measured with a positive control so the probe provably CAN see sound: live 1 ctx / 1 source; muted 0 / 0. Blips stay procedural on purpose — zero-fetch, deterministic, still work under ?noassets=1 where a sampled riffle cannot. Only mute was broken; only mute was fixed. -> F: assert SILENCE, not muted-state. HARNESS HONESTY: the first till rig reported the same clean 3.37 dB — from a CONTAMINATED rig (createDig appends a panel per call, close() doesn't remove it, so querySelector clicked trial 1's stale button every time). Tell: panelUp false on all three, and the NEGATIVE CONTROL returning 0.6436 where it must return 1.0. The gains matched the formula exactly; I refused them anyway and re-ran isolated (panelCount 1, panelUp true, control 1.0). Never report a number from a rig you have watched lie — even when you like the number. QA GREEN 6/0/0/0. Docs: LANE_C_AUDIO.md -> v3.1 (contract + both asks + every measurement). |
||
|
|
62e38c5df8 |
Lane C R14 (v3.0 release): venue audit + docs freeze + guitar_amp ruling
Light release round — C interior code unchanged since R13 (venue geometry stable by construction); this is the audit, the amp ruling, and the v3.0-FROZEN docs. - Re-verified vs the current tree (D's continuity + RY-fix + real instrument GLBs landed): contract byte-identical (caps 8/8/12, deckY/riserY, gigKeys, determinism), drawSweep <=350 (worst 161), gig-night draws pub 60 / band_room 52 / rsl 71 <=350. Release-law confirmed: D's rng namespace is disjoint from C's ctx.stream; no golden can move. - guitar_amp ruling (LANE_C_PUB §3): NOT blessed as-is. D's pose (stage.z+0.15, down-stage) interpenetrates the bass rig by 0.29 m; move it up-stage-right ~ (stage.w*0.36, deckY, stage.z - stage.d*0.22), clear of the bass + C's primitive ampStacks. Full amp unification = v3.1. Cosmetic + ?gigs opt-in -> not a tag blocker. C->D handshake. - Docs freeze: LANE_C_PUB.md + LANE_C_AUDIO.md marked v3.0-FROZEN; RY §0 reframed past-tense (the +Z fleet was fixed at source in D's buildFigure, R13 - no per-consumer flips); budgets updated to R14; in-flight handoffs removed. - Comment-only code (zero runtime/golden/rng change): fixed stale R12 inline return-shape comments (watchPoints 6..8 -> 8/8/12; stage bandPoses[3] -> [4] + riserY) in interiors.js + layout.js; documented the standalone gigKey hand-build. theme.js untouched. Note for E/F: settled gig-night interior is ~182k tris (E instruments 14k each x5); F's smoke reports ~34k measuring before the async GLBs load. Interior draw gate passes; v3.1 LOD candidate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
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>
|