Files
skills/docs/media/audio/audio-engine-browser/src/adapter/adapter-track-ops.md
2026-05-29 08:20:56 +01:00

3.7 KiB

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

import { addTrackImpl } from "@suaveplan/audio-engine-browser/adapter/adapter-track-ops";

addTrackImpl(env, track);

Usage: Advanced Example

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

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.