npm package: @silyze/async-audio-mixer
Description: Simple audio mixer that uses @silyze/async-audio-stream
Author: Mihail Mojsoski
Latest version: 1.0.0

# Async Audio Mixer

`@silyze/async-audio-mixer` is a lightweight, battery‑included **audio mixer** built on top of \
[`@silyze/async-audio-stream`](https://www.npmjs.com/package/@silyze/async-audio-stream).  
It lets you combine any number of PCM/encoded streams, produce a single mixed \
output, and even **bridge** two full‑duplex `AudioStream`s with automatic \
transcoding when their formats differ.

---

## Install

```bash
npm install @silyze/async-audio-mixer
```

---

## Quick start

```ts
import AudioMixer from "@silyze/async-audio-mixer";
import { OpusFormat } from "@silyze/async-audio-format-webm";

// Imagine three incoming Opus/WebM streams (8 kHz mono):
const format = new OpusFormat(8000);
const speakers = [streamA, streamB, streamC].map((s) => format.decode(s));

// Mix to PCM and then re‑encode to Opus:
const mixedOpus = AudioMixer.mix(speakers, { format });

// Pipe to a WebSocket or file:
await mixedOpus.pipe(wsWritable);
```

---

## API

### `AudioMixer.mix(streams, config?) ⇒ AsyncReadStream<Buffer>`

| Parameter       | Type & Default                | Description                                                                                                                                            |
| --------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `streams`       | `Iterable<AudioOutputStream>` | Any audio output streams to be mixed. All must share the same PCM sample‑rate.                                                                         |
| `config.signal` | `AbortSignal` _(optional)_    | Cancel mixing early.                                                                                                                                   |
| `config.buffer` | `number` (ms) **default 100** | Length of each mixing frame in milliseconds.                                                                                                           |
| `config.live`   | `boolean` **default false**   | If `true`, the mixer starts emitting only new audio frames (live broadcast). If `false`, the full buffer history is replayed once a consumer attaches. |
| `config.format` | `AudioFormat` _(optional)_    | If supplied, the PCM mix is **encoded** with `format.encode()` before being returned.                                                                  |

> The returned stream is **PCM‑16‑LE** unless you supply `config.format`.

### `AudioMixer.connect(a, b, pipeConfig?) ⇒ Promise<void>`

Bidirectionally **connects two `AudioStream`s** (`a` ⇆ `b`).  
If the streams already share the same format & sample‑rate, raw data is piped.  
Otherwise each direction is transcoded on‑the‑fly using the respective \
`encode/decode` methods.

`pipeConfig` is forwarded to `AsyncTransform.pipe()` for back‑pressure and error \
handling options.

---

## Example – Simple Voice Chat Room

```ts
// listenerStreams: Set<AudioOutputStream> coming from WebRTC peers
const mix = AudioMixer.mix(listenerStreams, {
  buffer: 20, // 20 ms frames → low latency
  live: true,
});

// Broadcast the mixed PCM to a speech‑to‑text service …
await mix.pipe(sttInputWritable);
```

---

## Internals

- **Frame‑based mixing** – samples are summed per frame, with hard clipping at \
  ±32768 to avoid overflow.
- **Mono‑only** – each input is treated as mono; stereo mixing is out of scope \
  (PRs welcome!).
- **Sample‑rate safety** – a runtime assertion prevents accidental mixing of \
  streams that use different PCM sample‑rates.