Skip to content

Latest commit

 

History

History
99 lines (82 loc) · 5.68 KB

File metadata and controls

99 lines (82 loc) · 5.68 KB

Echo - architecture

This document is not a module map (that lives in README.md). It is the record of Echo's permanent commitments and the reasoning behind each one. Every section answers a "why" that is meant to survive refactors, new stages, and new renderers. If a future change contradicts one of these, the change is wrong or this document must be renegotiated first - the commitments do not bend to convenience.


Why the scene graph is the product, and the WAV is always derived

Echo's output is not an audio file. It is the sound scene graph - an explicit, typed, inspectable description of what is in the scene, where each source is over time, how the ambience wraps, and under what render policy the scene should be voiced. Every audio artifact (binaural, ambisonics, object metadata, a device-specific mix) is derived from that graph and is therefore disposable: it can be regenerated byte-for-byte, re-inspected, re-scored, and re-rendered under a different policy at any time.

This is the load-bearing decision of the whole project. A finished mix is opaque

  • you cannot ask it why a voice sits where it does, you cannot move that voice without artifacts, and you cannot audit whether its cues are internally consistent. A scene graph can be diffed, validated, scored, and versioned. It makes the pipeline debuggable, makes determinism meaningful, and makes the render policy a first-class knob rather than a destructive edit. Treating the WAV as the product would collapse all of that into an artifact you can only listen to and never interrogate. So: the graph is the source of truth; the WAV is a projection of it.

Why intent is typed, never prompts

Intent enters Echo only as five named psychoacoustic scalars with declared ranges - dialogue_distance_m, ambience_wrap, stage_width, bass_body, fatigue_guard - wrapped in a frozen policy value object with a small catalogue of named presets. Echo never parses "make it wider" free text into a render.

Two reasons. First, determinism and auditability: a typed scalar with a range is reproducible, clampable, diffable, and explainable; a natural-language prompt is none of those and smuggles a non-deterministic interpreter into the render path. Second, honesty of control surface: a prompt implies the engine understands intent it cannot actually honor. Five typed scalars say exactly what Echo can and cannot be asked to do, and out-of-range values are silently clamped so intent is always total - every input yields a renderable policy. The intent layer is a contract, not a chat box.

Why the core is deterministic and ML is opt-in

The render and verify paths are pure, RNG-free, fixed-FFT, float64-compute numerical code depending only on NumPy/SciPy/soundfile. Anything heavy or learned - source separation (Demucs), visual analysis (OpenCV) - lives behind extras, is off by default, and is marked EXPERIMENTAL until verified on labeled data. The only seeded randomness in the whole system lives in calibration.

The reason is that evidence requires reproducibility. An artifact SHA-256 is only meaningful if the same input always yields the same bytes; the moment a non-deterministic model sits in the render path, the audit trail in AUDIT.md becomes noise. Keeping the perception-recovery stages (learned, fallible, non-deterministic) strictly upstream of a deterministic core means Echo can be wrong about what is in a scene while still being exactly reproducible about how it renders the scene it believes in. The two failure modes stay separable, which is what makes each one debuggable.

Why the gate fails closed

The gate (echo/gate.py) turns the scene score into a single PASS/BLOCK decision with a deliberately boring rule: PASS iff overall ≥ 70 and zero criticals, BLOCK otherwise. Crucially, any malformed score record resolves to BLOCK, never to an exception and never to PASS - a missing field, a non-finite overall, a criticals value with no length: all BLOCK.

A verifier that cannot prove a scene is clean must not let it through. The expensive failure in spatial audio is the confident wrong result - a clipped buffer, a determinism mismatch, an ITD/ILD sign inversion (the "mirror bug"), non-finite geometry - shipped as if it were fine. Fail-open hides exactly those. Fail-closed guarantees that the only way past the gate is to be demonstrably clean, so a green verdict is worth something. The cost is that ambiguity blocks; that is the correct trade for an engine whose credibility depends on never overclaiming.

Why there is no accepted externalization metric - and what that buys us

There is no field-agreed, listener-validated scalar for externalization, envelopment, presence, or fatigue. Echo does not pretend otherwise. It ships proxies - interaural coherence, spectral-notch presence, cue consistency - that are designed to track those felt outcomes, and it labels every one of them as a signal statistic rather than a listener report.

This is a deliberate stance, not a gap we are hiding. Inventing a number and calling it "externalization achieved" would be the single fastest way to lose credibility, because no such measurement exists to back it. By keeping felt outcomes behind the hypothesis firewall (see CLAIMS.md) - always "designed for", never "achieves" - Echo stays honest about the one thing it cannot yet measure: what a human actually hears. The EchoBench methodology is designed to eventually connect proxies to listener data; until that data exists, perceptual honesty is the architecture. The proxy tells you the render is built the way externalization is thought to require; it does not tell you the listener felt it, and Echo never says it does.