# useWebAudioMixer

> Binds mixer state to a Web Audio graph, with ducking, monitor sends, a limited master and meters.

```npm
npx shadcn-svelte@latest add @audiocn-svelte/use-web-audio-mixer
```

## Usage

```svelte
<script lang="ts">
	import { LevelMeter } from "#lib/components/ui/level-meter/index.js";
	import { useAudioPlayer } from "#lib/hooks/use-audio-player.svelte.js";
	import { useMicrophone } from "#lib/hooks/use-microphone.svelte.js";
	import { useMixer } from "#lib/hooks/use-mixer.svelte.js";
	import { useWebAudioMixer } from "#lib/hooks/use-web-audio-mixer.svelte.js";

	const mixer = useMixer({
		channels: [{ id: "mic" }, { id: "music", monitor: true }],
	});
	const microphone = useMicrophone({ enabled: true });
	const player = useAudioPlayer({ src: "/music.mp3" });

	const graph = useWebAudioMixer(mixer, () => ({
		inputs: { mic: microphone.stream, music: player.element },
		ducking: { trigger: "mic", targets: ["music"] },
	}));
</script>

<LevelMeter source={graph.meters.mic} />
<LevelMeter source={graph.master.meter} />
```

Call it during component setup. Pass the options as a getter, as above, so the graph follows `microphone.stream` when it opens. `meters`, `visuals`, `output`, `destination` and `context` are getters, so read them from the returned object.

The [System Audio Mixer](/docs/blocks/system-audio-mixer) block is built on it.

## The graph

Each channel runs through a gain, a ducking stage and a stereo panner. The panner feeds the master bus, and a monitor send feeds the speakers. The master bus runs through an optional peak limiter into a `MediaStream` for recording.

```
input → gain → duck → pan ─┬─→ master → limiter → output stream
                           └─→ monitor send → speakers
```

- Mute and solo are gain ramps, so they never click. Channels are built silent and fade in, so a muted channel is never heard while it is created.
- A duck holds for 200 ms after the trigger's last loud frame, then releases. It also releases when the trigger channel goes away mid-duck.
- `monitor` decides whether a channel is heard in the speakers. It is off by default so a microphone does not feed back; turn it on for music and sounds.
- A media element used as an input is taken over by the mixer while mounted, and routed back to the speakers afterwards.

## Options

<PropsTable
	rows={[
		[
			"inputs",
			"Record<string, MediaStream | HTMLMediaElement | AudioNode | null>",
			null,
			"One input per channel id.",
		],
		[
			"ducking",
			"DuckingOptions | DuckingOptions[]",
			null,
			"{ trigger, targets, thresholdDb = -35, amountDb = -12, attackMs = 50, releaseMs = 400 }.",
		],
		["limiter", "boolean", "true", "A peak limiter on the master."],
		[
			"analyser",
			"{ fftSize?, bands?, historySize?, smoothing? }",
			null,
			"Settings for the meters.",
		],
		["enabled", "boolean", "true", "Build the graph."],
	]}
/>

## Returns

<PropsTable
	rows={[
		[
			"meters",
			"Record<string, FrameSource<MeterFrame>>",
			null,
			"Post-fader stereo meter per channel.",
		],
		[
			"visuals",
			"Record<string, FrameSource<VisualFrame>>",
			null,
			"Post-fader visual frames per channel.",
		],
		["master", "{ meter, visual }", null, "After the limiter."],
		["output", "MediaStream | null", null, "The mix, for MediaRecorder or WebRTC."],
		["destination", "AudioNode | null", null, "The master bus input, for your own nodes."],
		["context", "AudioContext | null", null, null],
	]}
/>

The sources stay the same while the graph is rebuilt, so components never need to resubscribe.
