|
|
|
@@ -2,7 +2,7 @@
|
|
|
|
|
|
|
|
|
|
**XZBT format version:** 0.1
|
|
|
|
|
**Document revision:** 0.4
|
|
|
|
|
**Status:** Normative specification for shared contracts (document, types, ValueSpec, ConditionSpec, resolution, bindings, and transitions); the Audio subsystem authoring contract (Phase 3a sources/control sources, Phase 3b processing/routing/components/buses) is complete; audio automation precedence, lifecycle, and master protection (Phase 3c) and the remaining subsystem contracts are in progress
|
|
|
|
|
**Status:** Normative specification for shared contracts (document, types, ValueSpec, ConditionSpec, resolution, bindings, and transitions); the Audio subsystem contract is complete (Phase 3a sources/control sources, Phase 3b processing/routing/components/buses, Phase 3c automation/lifecycle/protection); master-protection values remain provisional pending GC6 measurement, and the remaining subsystem contracts are in progress
|
|
|
|
|
**Related resources:** [PRD](../XZBT_0-1_MVP_Product_Requirements_Document.md), [decisions](XZBT_0-1_Gap_Closure_Decisions.md), [verification](XZBT_0-1_Verification_Gates.md)
|
|
|
|
|
|
|
|
|
|
This document defines normative syntax and runtime semantics for XZBT 0.1 exhibits. An exhibit is a UTF-8 JSON document that configures generic procedural visual, audio, cadence, and orchestration primitives. It does not contain executable JavaScript.
|
|
|
|
@@ -287,6 +287,12 @@ To ensure consistent error reporting between structural schema validation, seman
|
|
|
|
|
| `ERR_NO_AUDIBLE_PATH` | Semantic | A sound's expanded audio graph has no chain of audio routes from a non-control source to `output`. |
|
|
|
|
|
| `ERR_COMPONENT_RECURSION` | Semantic | An audio component instantiates itself transitively, or component nesting exceeds 8 levels. |
|
|
|
|
|
| `WARN_AUDIO_RATE_CLAMP` | Runtime | A frequency field was clamped to the device's `audioMaxFrequency` at node instantiation. |
|
|
|
|
|
| `ERR_AUTOMATION_CONFLICT` | Semantic | More than one automation track directly controls one property in a recipe instance. |
|
|
|
|
|
| `ERR_INDETERMINATE_ONESHOT` | Semantic | A `oneshot` recipe has no computable finite ending; its audible path begins at an unbounded source. |
|
|
|
|
|
| `WARN_AUTOMATION_FALLBACK` | Runtime | An `exponential` automation segment had a zero or sign-crossing endpoint and fell back to linear interpolation. |
|
|
|
|
|
| `WARN_VOICE_LIMIT` | Runtime | A voice ceiling was reached; an instance was evicted or a request refused. |
|
|
|
|
|
| `WARN_AUDIO_NONFINITE` | Runtime | A non-finite sample reached the master chain and the containing block was muted. |
|
|
|
|
|
| `WARN_AUDIO_UNAVAILABLE` | Runtime | No audio device is available; the performance continues silently. |
|
|
|
|
|
|
|
|
|
|
## 8. Shared value resolution, bindings, and transitions
|
|
|
|
|
|
|
|
|
@@ -505,7 +511,7 @@ Complete shared contracts before implementing dependent subsystems. Use PRD sect
|
|
|
|
|
| Values and conditions | 15-21, 33 | Operator arity/table, division-by-zero protection, sampling timing boundaries, edge-trigger re-arming | **Complete (Rev 0.2)** |
|
|
|
|
|
| References and bindings | 13, 17, 31-32 | Target-capability table, instance/input scope, evaluation order, cycles, disabled bindings, exact smoothing | **Complete (Rev 0.3 / GC3)** |
|
|
|
|
|
| Actions and transitions | 22-30 | Shared `set`/`override` fields and defaults, override target matrix, interrupted transitions, instance IDs; subsystem action matrices remain with their subsystems | **Shared contract complete (Rev 0.3 / GC3)** |
|
|
|
|
|
| Audio | 34-60 | Complete recipe/component shapes, node defaults, automation timing, clamp implementation, one-shot endings and tails, unlock behavior, master protection contract | **Authoring contract complete (Rev 0.4 / Phase 3a-3b):** units, graph objects, all sixteen node types, routing, modulation, graph legality, authoring limits, components, sounds/recipes, and buses. Automation precedence (54), lifecycle states (57), unlock behavior, and runtime safety/master protection (58) remain (Phase 3c) |
|
|
|
|
|
| Audio | 34-60 | Complete recipe/component shapes, node defaults, automation timing, clamp implementation, one-shot endings and tails, unlock behavior, master protection contract | **Complete (Rev 0.4 / Phase 3a-3c):** units, graph objects, all sixteen node types, routing, modulation, graph legality, authoring limits, components, sounds/recipes, buses, automation tracks and precedence, lifecycle states and release, determinable one-shot endings, voice ceilings, unlock and pause behavior, and the master-protection contract shape. Master-protection *values* (peak ceiling, tolerance, release behavior) are provisional pending GC6 measurement |
|
|
|
|
|
| Cadence | 61-68 | Exact selection/cooldown/overlap fields, clocks, pool collisions, pending audio, manual sampling isolation and continuous-sample termination | Subsystem contract (Phase 5) |
|
|
|
|
|
| Visuals | 69-89 | Primitive/system/behavior fields, angle and motion units, transform order, depth projection, field behavior, morph compatibility, effect approximation and limits | Subsystem contract (Phase 4) |
|
|
|
|
|
| Events/scenarios | 90-102 | Shared ownership, hooks/failure ordering, condition rearming, deferred ordering/expiry, dispatch limits; full trigger/timeline shapes remain for Phase 6 | **Shared lifecycle contract complete (Rev 0.3 / GC5)** |
|
|
|
|
@@ -553,7 +559,7 @@ Structural JSON Schema does not replace semantic validation. The internal schema
|
|
|
|
|
|
|
|
|
|
This section opens the Audio contract required by section 11 (PRD 34-60). It covers the audio pipeline overview, canonical units, the shared audio-graph object shape, node-field resolution scope, the audio frequency ceiling, audio reproducibility scope, and the six source and control-source node types (PRD 36-42).
|
|
|
|
|
|
|
|
|
|
Processing nodes, routing and modulation, graph legality, components, sound definitions, and buses (PRD 43-53, 55, 56, 59, 60) are specified in section 15 (Phase 3b). Audio automation precedence (PRD 54), the full runtime lifecycle state machine (PRD 57), and the consolidated safety-limit and master-protection contract (PRD 58) are **Not yet specified** and arrive in Phase 3c.
|
|
|
|
|
Processing nodes, routing and modulation, graph legality, components, sound definitions, and buses (PRD 43-53, 55, 56, 59, 60) are specified in section 15 (Phase 3b). Automation tracks and precedence (PRD 54), the runtime lifecycle state machine and release (PRD 57), determinable one-shot endings, voice ceilings, master protection (PRD 58), and unlock and pause behavior (PRD 117-118) are specified in section 16 (Phase 3c).
|
|
|
|
|
|
|
|
|
|
### 14.1 Pipeline and scope
|
|
|
|
|
|
|
|
|
@@ -596,6 +602,7 @@ An **audio graph object** is the shared container for every audio node network i
|
|
|
|
|
| --- | --- | :---: | --- |
|
|
|
|
|
| `nodes` | object keyed by node ID | Yes | Each value is a node object (14.4). An empty `nodes` object is `ERR_SCHEMA_VALIDATION`. |
|
|
|
|
|
| `routes` | array | No (defaults to `[]`) | Route objects, specified in 15.12-15.13. A graph object with no route reaching `output` is `ERR_NO_AUDIBLE_PATH` unless it is a component graph, which terminates at its declared component output instead (15.15). |
|
|
|
|
|
| `automation` | array | No (defaults to `[]`) | Automation tracks, specified in 16.1. Declared on the graph object, never on a node. |
|
|
|
|
|
|
|
|
|
|
Audio graph objects are embedded at exactly three places, whose surrounding fields are specified in Phase 3b:
|
|
|
|
|
|
|
|
|
@@ -624,7 +631,7 @@ Node fields are **not** added to the section 8.1 target-capability table. A `Bin
|
|
|
|
|
|
|
|
|
|
This restriction governs **external** targeting only. Graph-internal modulation — a route whose `to` addresses `node.property` (15.13, PRD 52-53) — is the supported mechanism for varying a node field over time and is fully specified in Phase 3b. Section 15.13 lists which properties accept modulation; a modulation route to any other property is `ERR_UNSUPPORTED_TARGET`.
|
|
|
|
|
|
|
|
|
|
Automation tracks (PRD 54) are a separate Phase 3c stage and are **Not yet specified**; an `automation` field on any node or recipe is `ERR_UNKNOWN_FIELD` until that contract lands.
|
|
|
|
|
Automation tracks (PRD 54) are specified in sections 16.1 and 16.2. A track is declared on the graph object alongside `nodes` and `routes`, never on a node, so an `automation` field on a *node object* remains `ERR_UNKNOWN_FIELD`. Automation does not widen the external surface of 14.4: a track is authored inside the graph that owns the property, not in the document's binding list.
|
|
|
|
|
|
|
|
|
|
### 14.5 Audio frequency ceiling and validation staging
|
|
|
|
|
|
|
|
|
@@ -839,7 +846,7 @@ This section completes the authoring surface of the audio subsystem: the process
|
|
|
|
|
|
|
|
|
|
It builds directly on section 14: every node object declared here lives in a `nodes` map inside an audio graph object (14.3), its numeric fields follow the resolve-once scope of 14.4, its frequency fields follow the two-tier ceiling of 14.5, and it is externally unaddressable except through the modulation routes defined in 15.13.
|
|
|
|
|
|
|
|
|
|
Audio automation precedence (PRD 54), the runtime lifecycle state machine (PRD 57), and the runtime half of the safety-limit and master-protection contract (PRD 58 voice ceilings, peak ceiling, numerical tolerance, release behavior, finite-sample handling) remain **Not yet specified** and arrive in Phase 3c.
|
|
|
|
|
Automation tracks and precedence (PRD 54), the runtime lifecycle state machine and release (PRD 57), and the runtime half of the safety-limit and master-protection contract (PRD 58 voice ceilings, peak ceiling, numerical tolerance, release behavior, finite-sample handling) are specified in section 16 (Phase 3c). The master-protection *values* there remain provisional pending GC6 measurement.
|
|
|
|
|
|
|
|
|
|
### 15.1 Processing and routing node set
|
|
|
|
|
|
|
|
|
@@ -1088,7 +1095,7 @@ value = clamp(b + sum(s_i x d_i), property minimum, property maximum)
|
|
|
|
|
|
|
|
|
|
Multiple legal modulation routes are summed (PRD 53). The clamp uses the property's declared range from its node contract and is the audio application of the safety-clamp stage of section 8.1; it is not a separate precedence system. A control source's output is its own value in its own units before scaling: an `lfo` with `amplitude: 1` and `polarity: "bipolar"` contributes `[-d, +d]` to its target.
|
|
|
|
|
|
|
|
|
|
Automation (PRD 54) sits between binding and override in the shared pipeline and is **Not yet specified**; in Phase 3b the resolution of a node property is exactly base, then modulation sum, then safety clamp.
|
|
|
|
|
Automation (PRD 54) sits between binding and override in the shared pipeline and is specified in sections 16.1 and 16.2. With no automation track on a property, its resolution is exactly base, then modulation sum, then safety clamp.
|
|
|
|
|
|
|
|
|
|
### 15.14 Graph legality and authoring limits
|
|
|
|
|
|
|
|
|
@@ -1242,3 +1249,245 @@ The following codes are added to the section 7 table, which remains the single a
|
|
|
|
|
5. Two modulation routes onto one property sum and then clamp to the property's declared range.
|
|
|
|
|
6. A graph exceeding `128` expanded nodes or `256` routes after component expansion is `ERR_NODE_LIMIT_EXCEEDED`, while a graph at exactly those counts passes.
|
|
|
|
|
7. Expansion and validation run with no `AudioContext`, confirming the 14.5 separation between semantic validation and instantiation.
|
|
|
|
|
|
|
|
|
|
## 16. Audio Subsystem Contract — Automation, Lifecycle, and Protection (Phase 3c)
|
|
|
|
|
|
|
|
|
|
This section closes the audio subsystem. It covers automation tracks and their place in the shared resolution pipeline (PRD 54), the runtime lifecycle state machine and release behavior (PRD 57), determinable one-shot endings, voice ceilings and eviction, master output protection (PRD 58, 120), and unlock and pause behavior for audio (PRD 117-118).
|
|
|
|
|
|
|
|
|
|
Sections 14 and 15 define what an exhibit may *declare*. This section defines what the runtime *does* with it over time. Where an earlier section deferred a rule to "Phase 3c", this section is the referent.
|
|
|
|
|
|
|
|
|
|
**Implementation status.** This section is the Phase 3c *contract*; the runtime does not yet implement it. Until slices 3c-2 and 3c-3 land, a document declaring `automation` or `release` is rejected with `ERR_UNKNOWN_FIELD` by the current validator, sound instances have no lifecycle state machine, and no voice ceiling is enforced. The implementation plan records the slice order.
|
|
|
|
|
|
|
|
|
|
### 16.1 Automation tracks
|
|
|
|
|
|
|
|
|
|
An automation track drives one node property along an authored curve measured from the owning sound instance's start. Tracks are declared in the `automation` array of a graph object (14.3), alongside `nodes` and `routes`, and are therefore available in recipe graphs and component graphs alike:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"automation": [
|
|
|
|
|
{
|
|
|
|
|
"target": "level.gain",
|
|
|
|
|
"mode": "absolute",
|
|
|
|
|
"interpolation": "smooth",
|
|
|
|
|
"points": [
|
|
|
|
|
{ "at": "0ms", "value": 0 },
|
|
|
|
|
{ "at": "800ms", "value": 1 },
|
|
|
|
|
{ "at": "4s", "value": 0.6 }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
| Field | Type | Required | Notes |
|
|
|
|
|
| --- | --- | :---: | --- |
|
|
|
|
|
| `target` | string | Yes | `<node-key>.<property>`, resolved in the same graph. Follows the encapsulation rules of 15.15: a component's internals are unreachable, and a component instance exposes only its declared parameters. |
|
|
|
|
|
| `mode` | enum | No (default `absolute`) | `absolute`, `offset`, or `scale`. |
|
|
|
|
|
| `interpolation` | enum | No (default `linear`) | `step`, `linear`, `exponential`, or `smooth`. |
|
|
|
|
|
| `points` | array | Yes | `2` to `256` breakpoints, each `{ "at": DurationSpec, "value": ValueSpec<number> }`. |
|
|
|
|
|
|
|
|
|
|
`automation` is an array rather than a keyed map because a track has no identity an author needs to reference. It defaults to `[]`.
|
|
|
|
|
|
|
|
|
|
**Targets.** A track may address any property in the 15.13 modulatable registry, and no other. A track targeting a property absent from that registry is `ERR_UNSUPPORTED_TARGET`; the registry is deliberately the same one modulation uses, so a property is either time-varying or it is not, and the two mechanisms never disagree about which. A `target` naming an undeclared node is `ERR_INVALID_REFERENCE`.
|
|
|
|
|
|
|
|
|
|
**Exclusivity.** Only one automation track may directly control a property in a recipe instance (PRD 54). Two tracks addressing the same expanded target are `ERR_AUTOMATION_CONFLICT`. This is the automation counterpart of the `ERR_CONFLICTING_BINDING` rule in section 8.2, and for the same reason: two writers to one scalar has no defined answer. Modulation is unaffected — multiple modulation routes onto one property still sum (15.13), because summation *is* their defined answer.
|
|
|
|
|
|
|
|
|
|
**Points.** `at` is measured from the node instance's start and is sampled once at instantiation (14.4), like every other DurationSpec in this contract. Points must be in strictly increasing `at` order; equal or decreasing times are `ERR_INVALID_RANGE_ORDER`. Fewer than two points is `ERR_SCHEMA_VALIDATION` — a single point is a constant and belongs in the property's base value. Before the first point the track holds the first point's value; after the last point it holds the last point's value and does not loop.
|
|
|
|
|
|
|
|
|
|
**Modes.** Given the property's resolved base value `b` and the track's current curve value `a`, the track contributes:
|
|
|
|
|
|
|
|
|
|
| `mode` | Contribution | Use |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| `absolute` | `a` | The curve *is* the value; the base is ignored. |
|
|
|
|
|
| `offset` | `b + a` | The curve displaces the authored base in the property's own unit. |
|
|
|
|
|
| `scale` | `b x a` | The curve multiplies the authored base. |
|
|
|
|
|
|
|
|
|
|
**Interpolation.** Between two points with values `v0` and `v1` and normalized progress `t` in `[0, 1)`:
|
|
|
|
|
|
|
|
|
|
| `interpolation` | Value | Notes |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| `step` | `v0` | Holds until the next point, then jumps. |
|
|
|
|
|
| `linear` | `v0 + (v1 - v0) x t` | — |
|
|
|
|
|
| `exponential` | `v0 x (v1 / v0)^t` | Requires both endpoints strictly positive and same-signed; a segment with a zero or sign-crossing endpoint falls back to `linear` and raises `WARN_AUTOMATION_FALLBACK` once per track. A fallback is preferable to failing, because a legal base value can resolve to zero at instantiation. |
|
|
|
|
|
| `smooth` | `v0 + (v1 - v0) x (3t^2 - 2t^3)` | Smoothstep; zero first derivative at both endpoints. |
|
|
|
|
|
|
|
|
|
|
**Limits.** At most `64` automation tracks and `256` total automation points per expanded sound (PRD 58). Exceeding either is `ERR_NODE_LIMIT_EXCEEDED`.
|
|
|
|
|
|
|
|
|
|
### 16.2 Audio automation precedence
|
|
|
|
|
|
|
|
|
|
Section 8.1 fixes the shared resolution pipeline. This section states its audio application; it is not a competing system (PRD 54). For a node property:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
base ValueSpec -> binding (where supported) -> automation -> winning override -> modulation sum -> safety clamp -> engine parameter
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Three consequences follow, and they are the whole content of this subsection.
|
|
|
|
|
|
|
|
|
|
**Binding remains unsupported for node properties.** Section 14.4 stands: an external `BindingSpec` targeting a node field is `ERR_UNSUPPORTED_TARGET`. Automation does not change this, because a track is declared *inside* the graph that owns the property, not in the document's binding list. Adding automation to a subsystem never widens that subsystem's external surface. `audio.buses.<id>.gain` remains the one audio target in the shared registry, and it takes binding, automation, override, and modulation as section 8.1 already states.
|
|
|
|
|
|
|
|
|
|
**Underlying automation continues while overridden** (PRD 54). An override masks the automation stage's output for as long as it wins; it does not pause, reset, or rewind the track. When the override releases, the property returns to whatever the track has reached by then, not to the value it held when the override took hold. This mirrors the parameter-masking rule of section 8.1, where releasing an override returns toward the current stored value rather than an obsolete snapshot.
|
|
|
|
|
|
|
|
|
|
**Every stage a target does not expose is absent, not an identity hook.** A property outside the 15.13 registry has no automation stage and no modulation stage. Being numeric grants nothing.
|
|
|
|
|
|
|
|
|
|
### 16.3 Lifecycle states
|
|
|
|
|
|
|
|
|
|
A sound instance occupies exactly one state (PRD 57):
|
|
|
|
|
|
|
|
|
|
| State | Meaning | Audio produced |
|
|
|
|
|
| --- | --- | :---: |
|
|
|
|
|
| `CREATED` | Graph expanded, values resolved, nodes constructed, nothing connected to a bus. | No |
|
|
|
|
|
| `SCHEDULED` | Start time fixed on the audio clock; awaiting it. | No |
|
|
|
|
|
| `ACTIVE` | Running and audible. | Yes |
|
|
|
|
|
| `RELEASING` | Release envelope running; no new work accepted. | Yes, decaying |
|
|
|
|
|
| `FINISHED` | Release complete; output silent; resources not yet reclaimed. | No |
|
|
|
|
|
| `DISPOSED` | Every node, connection, automation track, buffer, and subscription released. Terminal. | No |
|
|
|
|
|
| `FAILED` | Construction or activation raised; resources reclaimed as for `DISPOSED`. Terminal. | No |
|
|
|
|
|
|
|
|
|
|
Permitted transitions, and nothing else:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
CREATED -> SCHEDULED | FAILED
|
|
|
|
|
SCHEDULED -> ACTIVE | RELEASING | FAILED
|
|
|
|
|
ACTIVE -> RELEASING | FINISHED | FAILED
|
|
|
|
|
RELEASING -> FINISHED | FAILED
|
|
|
|
|
FINISHED -> DISPOSED
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A stop request in `SCHEDULED` goes to `RELEASING` rather than straight to `FINISHED`, so a single code path handles teardown whether or not the voice ever sounded. `ACTIVE -> FINISHED` without a release is reserved for a one-shot that has reached its determinable ending, where the envelope has already returned to zero and a further release would be redundant. `DISPOSED` and `FAILED` are terminal; a second stop on a disposed instance is a no-op, never an error.
|
|
|
|
|
|
|
|
|
|
Every state change is observable to the runtime's own diagnostics, but state names are not exposed to exhibits. An exhibit describes desired behavior, not runtime bookkeeping.
|
|
|
|
|
|
|
|
|
|
### 16.4 Release and the internal release gain
|
|
|
|
|
|
|
|
|
|
The runtime provides an internal release gain for every sound instance, whether or not the exhibit authored one (PRD 57). It is the last node before the instance's bus, it is engine-owned, and no route can bypass or address it.
|
|
|
|
|
|
|
|
|
|
The default release is `50ms`. A recipe graph object (15.16) may declare one further field, `release`, a DurationSpec in `0ms` to `10s`; `0ms` is permitted and means an immediate cut, which is an author's explicit choice rather than a default. `release` is a recipe field, not a graph-object field: a component graph does not declare it, because release belongs to the sound instance and not to any part of it. Entering `RELEASING` ramps the internal release gain linearly from its current value to zero over the release duration, and `FINISHED` follows at the end of that ramp.
|
|
|
|
|
|
|
|
|
|
Release is unconditional. It runs on exhibit deactivation, on voice eviction, on pause-induced teardown, and on failure, so no path exists that stops a voice by disconnecting an already-running source. This is what keeps the click-and-pop surface to a single, testable code path.
|
|
|
|
|
|
|
|
|
|
Disposal must release nodes, connections, automation tracks, buffers, and subscriptions (PRD 57). A `FINISHED` instance that has not reached `DISPOSED` still holds resources and still counts against the voice ceilings of 16.6.
|
|
|
|
|
|
|
|
|
|
### 16.5 Determinable one-shot endings
|
|
|
|
|
|
|
|
|
|
A `oneshot` recipe must have a determinable ending (PRD 57). "Determinable" means the runtime can compute a finite upper bound on the instance's audible duration at instantiation, from the resolved graph alone, without observing output.
|
|
|
|
|
|
|
|
|
|
The bound is computed as the longest path from any source to `output`, where each node contributes:
|
|
|
|
|
|
|
|
|
|
| Node | Contribution to the bound |
|
|
|
|
|
| --- | --- |
|
|
|
|
|
| `impulse` | its resolved `duration` |
|
|
|
|
|
| `oscillator`, `noise` | **unbounded** — these run until stopped |
|
|
|
|
|
| `constant`, `lfo`, `sample-hold` | zero; control sources are not on an audible path (15.14 rule 5) |
|
|
|
|
|
| `delay` | `time x ceil(log(1/1000) / log(feedback))` for `feedback > 0`, else `time`; the time for the internal feedback path to fall `60` dB |
|
|
|
|
|
| `reverb` | `predelay + decay` |
|
|
|
|
|
| `resonator` | the longest `decay` among its retained modes |
|
|
|
|
|
| every other node | zero; they colour a signal without extending it |
|
|
|
|
|
|
|
|
|
|
The instance's ending is the maximum over all source-to-`output` paths of the sum of contributions along that path, plus the release duration of 16.4.
|
|
|
|
|
|
|
|
|
|
A `oneshot` recipe whose bound is unbounded — that is, one whose audible path begins at an `oscillator` or `noise` source — is `ERR_INDETERMINATE_ONESHOT` at validation. The author's remedies are to declare `mode: "continuous"` and stop the sound explicitly, or to gate the source through an `impulse`-driven path. This is a semantic error rather than a runtime one because it is decidable from the document, and catching it at import is the difference between a rejected exhibit and a voice that never frees itself.
|
|
|
|
|
|
|
|
|
|
A `continuous` recipe has no ending requirement and runs until explicitly stopped.
|
|
|
|
|
|
|
|
|
|
### 16.6 Voice ceilings and eviction
|
|
|
|
|
|
|
|
|
|
Approximate ceilings (PRD 58):
|
|
|
|
|
|
|
|
|
|
| Ceiling | Value |
|
|
|
|
|
| --- | --- |
|
|
|
|
|
| One-shot voices | `64` |
|
|
|
|
|
| Continuous sounds | `16` |
|
|
|
|
|
|
|
|
|
|
These are approximate and may be lowered by the runtime on a weaker device (PRD 58, 120). They are never raised above these values by a document; an exhibit cannot request a larger budget. A voice counts against its ceiling from `CREATED` until `DISPOSED`, so instances lingering in `FINISHED` still occupy budget — which is why disposal is part of the contract rather than an optimization.
|
|
|
|
|
|
|
|
|
|
When a new instance would exceed its ceiling, the runtime applies this policy in order and stops at the first candidate:
|
|
|
|
|
|
|
|
|
|
1. Dispose the oldest instance already in `FINISHED`.
|
|
|
|
|
2. Evict the oldest instance in `RELEASING`.
|
|
|
|
|
3. For a one-shot request only: evict the oldest `ACTIVE` one-shot by starting its release.
|
|
|
|
|
4. Otherwise refuse the new instance.
|
|
|
|
|
|
|
|
|
|
Eviction always releases (16.4); it never hard-stops. A one-shot request never evicts a continuous sound, and a continuous request never evicts a one-shot — the two budgets are independent, because a bed of ambience and a burst of transients fail differently and stealing across the boundary produces the worse failure in both directions.
|
|
|
|
|
|
|
|
|
|
Every eviction and every refusal raises `WARN_VOICE_LIMIT` once, naming the sound and the ceiling. A refusal is not an error: an exhibit that asks for a sixty-fifth simultaneous transient is behaving legally, and the runtime's job is to stay stable and say so.
|
|
|
|
|
|
|
|
|
|
Resource ceilings are centralized rather than scattered through subsystems (PRD 120). Every limit in sections 14-16 lives in one runtime table.
|
|
|
|
|
|
|
|
|
|
### 16.7 Master output protection
|
|
|
|
|
|
|
|
|
|
Every audible signal passes through engine-controlled master protection, and an exhibit cannot bypass it (PRD 58). The chain is engine-owned, is the only path from any bus to the output device, and is not addressable from a document: no route, binding, override, automation, or action can reach it. `audio.master` remains absent from a 0.1 document (15.17).
|
|
|
|
|
|
|
|
|
|
The protection contract has four parts. Their *shape* is normative now; their *values* are provisional and are confirmed by measurement under GC6 before audio acceptance (PRD 58).
|
|
|
|
|
|
|
|
|
|
| Part | Contract | Status |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| Digital output peak ceiling | The absolute sample value at the output device never exceeds the ceiling. Provisional ceiling `-1.0` dBFS (`0.891` linear). | Provisional; pending GC6 |
|
|
|
|
|
| Numerical tolerance | Measured peak may exceed the ceiling by no more than the tolerance across a full measurement window. Provisional tolerance `0.1` dB. | Provisional; pending GC6 |
|
|
|
|
|
| Release behavior | Protection gain reduction recovers smoothly and never produces an audible pump on a sustained bed or a click on a transient. Verified by listening, not by a number alone. | Provisional; pending GC6 |
|
|
|
|
|
| Finite-sample handling | A non-finite sample (`NaN` or infinity) reaching the master chain is replaced with silence for that sample, the containing block is muted, and `WARN_AUDIO_NONFINITE` is raised once per instance. The count of affected blocks is recorded for the acceptance run. | Normative |
|
|
|
|
|
|
|
|
|
|
Only finite-sample handling is settled, because it is a correctness rule rather than a measured threshold: a `NaN` in the output buffer is never acceptable at any ceiling, and silencing it is strictly better than propagating it. The other three require real output on real hardware.
|
|
|
|
|
|
|
|
|
|
**The presence of a compressor does not establish that this contract passes** (PRD 58). A runtime may implement the chain with a limiter, a compressor, a soft clipper, or any combination; what it may not do is claim the contract on the strength of having built one. Acceptance requires the measured peak, the measured tolerance across worst-case overlapping recipes, and listening observations for clicks, clipping, and pumping.
|
|
|
|
|
|
|
|
|
|
### 16.8 Unlock behavior
|
|
|
|
|
|
|
|
|
|
Browsers may require user interaction before producing audio (PRD 118). The runtime treats the first user interaction that engages the exhibit as the point at which audio is initialized or resumed. Visual rendering may begin before audio authorization, and the UI must clearly represent muted or unavailable audio state.
|
|
|
|
|
|
|
|
|
|
When audio unlocks after visual startup, it aligns to the *current* logical position rather than replaying the interval it missed:
|
|
|
|
|
|
|
|
|
|
- **Expired one-shot invocations are not replayed.** Every one-shot whose invocation time has already passed is discarded, and `INFO_AUDIO_UNLOCK_SKIP` is raised once for the whole batch, carrying the count. One diagnostic for the batch, not one per skipped sound: a long pre-unlock interval would otherwise flood the panel with entries describing a single condition.
|
|
|
|
|
- **Continuous sounds start partially elapsed.** A continuous instance that should have begun at logical time `t0` and unlocks at `t1` starts at `ACTIVE` with its automation tracks and sample-hold ticks advanced to `t1 - t0`, not from zero. Its procedural draws are consumed in order to reach that position, so the seeded sequence is identical to an unbroken run — reproducibility does not depend on when the listener clicked.
|
|
|
|
|
- **The logical clock is not rewound.** Audio never causes the performance to replay elapsed time.
|
|
|
|
|
|
|
|
|
|
If audio is unavailable entirely — no `AudioContext`, or unlock refused — the performance continues silently. `WARN_AUDIO_UNAVAILABLE` is raised once, the UI shows the muted state, and no sound instance is created. A missing audio device degrades the exhibit; it does not fail it.
|
|
|
|
|
|
|
|
|
|
### 16.9 Pause and resume
|
|
|
|
|
|
|
|
|
|
Explicit pause and document visibility loss suspend audio along with the rest of the performance (PRD 117). Audio-specific rules:
|
|
|
|
|
|
|
|
|
|
- Suspending holds every voice in place. It does not release them, so resuming does not restart a bed of ambience that never stopped being wanted.
|
|
|
|
|
- No catch-up bursts. One-shots that would have been invoked while paused are discarded exactly as at unlock, and report through the same `INFO_AUDIO_UNLOCK_SKIP` batch.
|
|
|
|
|
- Automation tracks and sample-hold ticks resume from their held logical position; they do not fast-forward through the paused interval.
|
|
|
|
|
- Visibility restoration must not undo an explicit user pause.
|
|
|
|
|
- A pause longer than the runtime's stall bound releases all voices rather than holding them indefinitely, and resuming re-creates continuous sounds at the current logical position by the 16.8 rule. The exact bound is fixed with the GC4 audio lookahead and long-stall policy and is **Not yet specified** here.
|
|
|
|
|
|
|
|
|
|
### 16.10 New diagnostic codes
|
|
|
|
|
|
|
|
|
|
Added to the section 7 table, which remains the single authoritative list:
|
|
|
|
|
|
|
|
|
|
| Error Code | Stage | Cause |
|
|
|
|
|
| :--- | :--- | :--- |
|
|
|
|
|
| `ERR_AUTOMATION_CONFLICT` | Semantic | More than one automation track directly controls one property in a recipe instance. |
|
|
|
|
|
| `ERR_INDETERMINATE_ONESHOT` | Semantic | A `oneshot` recipe has no computable finite ending; its audible path begins at an unbounded source. |
|
|
|
|
|
| `WARN_AUTOMATION_FALLBACK` | Runtime | An `exponential` automation segment had a zero or sign-crossing endpoint and fell back to linear interpolation. |
|
|
|
|
|
| `WARN_VOICE_LIMIT` | Runtime | A voice ceiling was reached; an instance was evicted or a request refused. |
|
|
|
|
|
| `WARN_AUDIO_NONFINITE` | Runtime | A non-finite sample reached the master chain and the containing block was muted. |
|
|
|
|
|
| `WARN_AUDIO_UNAVAILABLE` | Runtime | No audio device is available; the performance continues silently. |
|
|
|
|
|
|
|
|
|
|
### 16.11 Required traces before Phase 3c implementation is accepted
|
|
|
|
|
|
|
|
|
|
Automated, and executable without an audio device:
|
|
|
|
|
|
|
|
|
|
1. Each automation mode and each interpolation curve produces its documented value at segment start, midpoint, and end; `step` holds; `smooth` has zero slope at both endpoints.
|
|
|
|
|
2. A track holds the first point's value before the first point and the last point's value after the last, and does not loop.
|
|
|
|
|
3. Two tracks on one expanded target are `ERR_AUTOMATION_CONFLICT`; two modulation routes on that same target still sum.
|
|
|
|
|
4. An `exponential` segment with a zero endpoint falls back to linear and raises `WARN_AUTOMATION_FALLBACK` exactly once.
|
|
|
|
|
5. An automation track targeting a property outside the 15.13 registry is `ERR_UNSUPPORTED_TARGET`; one naming an undeclared node is `ERR_INVALID_REFERENCE`; one reaching a component's internals is rejected by the 15.15 encapsulation rule.
|
|
|
|
|
6. `65` tracks or `257` total points is `ERR_NODE_LIMIT_EXCEEDED`; `64` and `256` pass.
|
|
|
|
|
7. Every permitted lifecycle transition is exercised and every forbidden one is rejected; a second stop on a `DISPOSED` instance is a no-op.
|
|
|
|
|
8. An override masking an automated property releases to the track's *current* value, not the value held when the override took hold.
|
|
|
|
|
9. The determinable-ending bound matches the 16.5 table for a graph combining `impulse`, `delay`, `reverb`, and `resonator`; a `oneshot` fed by an `oscillator` is `ERR_INDETERMINATE_ONESHOT`; the same recipe as `continuous` passes.
|
|
|
|
|
10. At the one-shot ceiling the eviction order of 16.6 is followed, `WARN_VOICE_LIMIT` is raised, a continuous sound is never evicted by a one-shot request, and a refused request leaves the runtime stable.
|
|
|
|
|
11. Disposal releases every node, connection, automation track, buffer, and subscription; a `FINISHED` instance still counts against its ceiling until `DISPOSED`.
|
|
|
|
|
12. A non-finite sample injected at the master chain mutes its block and raises `WARN_AUDIO_NONFINITE` once.
|
|
|
|
|
13. Unlocking after a delay skips expired one-shots with a single counted `INFO_AUDIO_UNLOCK_SKIP`, and starts a continuous sound partially elapsed with a procedural sequence identical to an unbroken run.
|
|
|
|
|
14. With no `AudioContext` available the performance runs silently, raises `WARN_AUDIO_UNAVAILABLE` once, and creates no instance.
|
|
|
|
|
|
|
|
|
|
User-observed, and **not** satisfiable by the above:
|
|
|
|
|
|
|
|
|
|
15. The measured digital output peak across worst-case overlapping recipes sits within the ceiling and tolerance of 16.7, on recorded hardware, with the environment details GC6 requires.
|
|
|
|
|
16. Listening observations across the reference exhibits report no clicks, clipping, or pumping on release, eviction, unlock, or pause.
|
|
|
|
|
17. The PRD 129 audio acceptance challenge passes in full.
|
|
|
|
|
|
|
|
|
|
Traces 15 through 17 close Phase 3 slice 3c-4. Until they do, Phase 3 is not accepted no matter how many automated traces pass.
|
|
|
|
|