first commit
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# `stream-source-plugin`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`StreamSourcePlugin` is the bridge that lets a remote peer's
|
||||
`MediaStream` participate in the audio engine's plugin-chain on equal
|
||||
footing with any other source plugin (microphone, sampler,
|
||||
audio-player). Implementing the `AudioFxPlugin` contract from
|
||||
`@suaveplan/audio-fx-plugin-base` means the chain can be queried,
|
||||
mutated, and rendered without special-case code for peer sources —
|
||||
adding an EQ, compressor, or reverb to a peer's chain is the same
|
||||
`chain.add(plugin)` call as on any local track. Every materialised
|
||||
peer becomes its own `peer:<participantId>` track with this plugin at
|
||||
slot 0.
|
||||
|
||||
The plugin is intentionally a thin shell at chunk-A scope: it owns the
|
||||
`MediaStream` reference and outputs silence until the engine's
|
||||
source-stream adapter (chunk B) replaces the silent fallback with the
|
||||
live samples. Keeping the bridge inert at this scope is what allows
|
||||
`@suaveplan/react-audio-stream` to satisfy the stream-tier purity rule
|
||||
— there are no `AudioContext`, `AudioNode`, or `AudioWorklet`
|
||||
references in the package.
|
||||
|
||||
## Features
|
||||
|
||||
- Implements `AudioFxPlugin` via `AbstractAudioFxPlugin` — every
|
||||
parameter/preset hook works out of the box.
|
||||
- `io.inputs[0].count === 0` per the source-plugin protocol, so the
|
||||
chain validator places it at slot 0.
|
||||
- `setSourceStream(stream)` / `getSourceStream()` for orchestration.
|
||||
- Zod-validated options + preset payloads; failures throw
|
||||
`REACT_AUDIO_STREAM.INVALID_OPTIONS`.
|
||||
- Silence fallback in `processBlock` keeps the chain numerically safe
|
||||
before the engine adapter takes over.
|
||||
- Participant id baked into plugin metadata for OTel + UI labelling.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createStreamSourcePlugin } from "@suaveplan/react-audio-stream/session";
|
||||
|
||||
const plugin = createStreamSourcePlugin({ participantId: "alice" });
|
||||
plugin.setSourceStream(remoteAliceStream);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
createStreamSourcePlugin,
|
||||
type StreamSourcePluginPort,
|
||||
} from "@suaveplan/react-audio-stream/session";
|
||||
|
||||
function attachPeer(
|
||||
participantId: string,
|
||||
stream: MediaStream,
|
||||
chain: PluginChain,
|
||||
): StreamSourcePluginPort {
|
||||
const plugin = createStreamSourcePlugin({
|
||||
participantId,
|
||||
initialStream: stream,
|
||||
outputChannelCount: 2,
|
||||
});
|
||||
chain.add(plugin); // slot 0 — source plugin
|
||||
return plugin;
|
||||
}
|
||||
|
||||
// Later, when the peer ends their stream:
|
||||
function detach(plugin: StreamSourcePluginPort): void {
|
||||
plugin.setSourceStream(undefined);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `StreamSourcePlugin` — `AbstractAudioFxPlugin` subclass implementing
|
||||
`StreamSourcePluginPort`.
|
||||
- `createStreamSourcePlugin(options): StreamSourcePlugin` — factory.
|
||||
- `StreamSourcePluginOptions` — `{ participantId, initialStream?,
|
||||
outputChannelCount? }`.
|
||||
- `StreamSourcePluginPort` — minimal surface `{ participantId,
|
||||
getSourceStream, setSourceStream }` exposed to orchestration code.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The plugin uses `unknown` rather than `MediaStream` as the stream
|
||||
type to stay strictly free of browser-globals at compile time — the
|
||||
package's TS config does not pull DOM lib references for non-test code,
|
||||
and the `unknown` shape lets chunk-B transports narrow at their own
|
||||
seam. The plugin's metadata id is suffixed with the participant id so
|
||||
OTel emissions keyed by plugin id distinguish per-peer rates even
|
||||
inside a single session. `processBlock` writes zeros into every output
|
||||
channel rather than allocating a fresh buffer because
|
||||
`AbstractAudioFxPlugin` already supplies fixed-length output arrays;
|
||||
this keeps the inner loop free of allocations under steady-state
|
||||
playback. The Zod boundary on options + preset matches the convention
|
||||
used by sibling source plugins (`audio-fx-plugin-microphone`) so
|
||||
consumers see consistent error shapes across the family.
|
||||
Reference in New Issue
Block a user