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_PROVIDERoutside 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.