first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
# `adapter-track-ops`
## Purpose
`adapter-track-ops` packs every track-scoped mutation that the
`BrowserTimelineAdapter` exposes — adding, removing, updating, and the
five per-track setters (`volume`, `mute`, `solo`, `pan`, plus the chain
+ subgraph teardown helper) — into pure functions that operate on an
explicit `TrackOpEnv` rather than on adapter-private state. The adapter
itself becomes a thin coordinator: it owns the lifecycle invariants and
the lazy environment, and delegates the actual work here.
Pulling the work out of the class keeps `browser-adapter.ts` inside the
300-line hard cap and makes every track operation unit-testable by
passing a hand-built `TrackOpEnv` with deterministic state lambdas. The
adapter file still exposes the same public methods; readers should
follow the call chain to this module for the implementation.
## Features
+ `TrackOpEnv` interface — explicit dependency injection of the track
map, graph, context, bus orchestrator, and the four lazy state
accessors (`currentTime`, `playState`, `masterVolume`).
+ `addTrackImpl` — builds the subgraph, constructs the chain, wires
the latency-changed event, applies mixer + ambient state.
+ `releaseTrackImpl` — symmetric teardown: dispose chain, tear down
subgraph, drop from the map.
+ `updateTrackImpl`, `setTrack{Volume,Mute,Solo,Pan}Impl` — small
composable mutations sharing the same epilogue (mixer / ambient
recompute).
## Usage: Basic Example
```ts
import { addTrackImpl } from "@suaveplan/audio-engine-browser/adapter/adapter-track-ops";
addTrackImpl(env, track);
```
## Usage: Advanced Example
```ts
import {
addTrackImpl,
releaseTrackImpl,
setTrackPanImpl,
setTrackVolumeImpl,
} from "@suaveplan/audio-engine-browser/adapter/adapter-track-ops";
addTrackImpl(env, track);
setTrackVolumeImpl(env, rec, 0.8);
setTrackPanImpl(env, rec, -0.3);
releaseTrackImpl(env, track.id);
```
## API Reference
### `TrackOpEnv`
```ts
interface TrackOpEnv {
tracks: Map<string, TrackRecord>;
graph: WebAudioGraph;
context: ContextLike;
busOrch: BusOrchestrator;
currentTime(): number;
playState(): "stopped" | "playing" | "paused";
masterVolume(): number;
}
```
### `addTrackImpl(env, track) → void`
Builds the per-track subgraph, constructs a `PluginChain` bound to the
project's shared `BusRegistry`, subscribes to `latency-changed`, and
applies the mixer + ambient state.
### `releaseTrackImpl(env, trackId) → void`
Removes the chain + subgraph for `trackId`. Idempotent for missing ids.
### `updateTrackImpl(env, rec, track) → void`
Mutates the `TrackRecord` in place, refreshes the pan node, and
re-applies mixer + ambient state.
### `setTrackVolumeImpl / setTrackMuteImpl / setTrackSoloImpl / setTrackPanImpl`
Single-purpose updaters. Each mutates the relevant fields and runs the
small epilogue: `applyMixerState` then `applyAmbientForTrack`.
## Implementation Notes
+ The OTel counter is module-scoped because every adapter shares the
same series; the adapter itself owns the `kind=fx` increment, and
this module owns `kind=track-subgraph` (+7 per added track).
+ `TrackOpEnv` accessors are lambdas rather than direct fields so the
adapter can pass `() => this.currentTime` without leaking `this`
binding. This keeps the helpers callable from any code path
(top-level, tests, future async restoration).
+ `releaseTrackImpl` swallows chain dispose errors so a misbehaving
plugin disposal cannot prevent the subgraph teardown — the adapter
invariant is "track must be fully released by `removeTrack`".
+ Adding the same track id again replays the release path first, which
is how the adapter handles `updateTrack` on a recreated chain shape.