Files
skills/docs/ui/react/react-audio-stream/src/hooks/use-stream-mixer/use-stream-mixer.md
2026-05-29 08:20:56 +01:00

2.7 KiB

use-stream-mixer

(no submodules)

Purpose

useStreamMixer exposes per-peer mixer controls — level, mute, solo, and the per-peer FX slot count — and implements DAW-correct solo semantics on top of the session's track mutators. It is the only hook in chunk C that owns cross-peer behaviour: when any peer is soloed, every non-soloed peer's track-volume is set to 0; when no peer is soloed, every track keeps its last user-set volume (the session is the source of truth for "last set" because the mutations go through setTrackVolume).

Every mutation routes through session.setTrackVolume, session.setTrackMute, and session.setTrackSolo rather than being re-implemented inside the hook — this is the explicit Requirement-6 constraint and the reason the test asserts on session.calls.setTrackSolo / setTrackVolume.

Features

  • DAW solo semantics: any solo activates the per-track ducking pass; clearing the last solo lets non-soloed tracks resume their stored volume.
  • Pure delegation: zero local volume state — the session owns it.
  • Stable callbacks via useCallback.
  • Throws REACT_AUDIO_STREAM.NO_PROVIDER outside a <StreamProvider>.

Usage: Basic

const { peers, setVolume, setMute, setSolo } = useStreamMixer();
return peers.map((p) => (
    <PeerStrip
        key={p.id}
        peer={p}
        onVolume={(v) => setVolume(p.id, v)}
        onMute={(m) => setMute(p.id, m)}
        onSolo={(s) => setSolo(p.id, s)}
    />
));

Usage: Advanced

const { peers, setSolo } = useStreamMixer();
const soloExclusive = useCallback((id: string) => {
    // Solo one peer and clear all others' solos in a single React tick.
    peers.forEach((p) => p.soloed && p.id !== id && setSolo(p.id, false));
    setSolo(id, true);
}, [peers, setSolo]);

API Reference

  • UseStreamMixerResult — { peers, setVolume, setMute, setSolo }
  • useStreamMixer(): UseStreamMixerResult

Implementation Notes

setSolo reads the current mixer snapshot from session.getPeerMixerStates(), applies the requested solo change in a local copy, computes anySoloed, and — when true — issues setTrackVolume(otherId, 0) for every non-soloed peer whose current volume is not already 0. The non-zero check avoids redundant mutations that would otherwise show up as extra calls.setTrackVolume entries in tests.

The "restore previous volume on un-solo" arm is delegated to the session: chunk A's session implementation stores the user-set volume independently from the live mixer volume, so when the last solo clears, the session emits a mixer-changed event that restores the stored values. The hook does not have to keep its own restore-map, which keeps the React surface stateless.