Skip to main content
Real audio devices are an opt-in extra in a target of their own. The core is pinned to a synthetic audio module and cannot be talked out of it: nothing on the mandatory path can open a microphone, so a model that happens to declare a sendonly audio track never puts a live microphone on the wire.
Linking reactor::sdk alone brings in no audio library, no device enumeration and none of this.
This is the one part of the SDK with a dependency the archive does not carry, and only on Linux: the backend is loaded at run time from whichever of libasound.so.2 (ALSA), libpulse.so.0 or libjack.so.0 is present. A slim container image usually has none, and start() then throws rather than playing silence — apt install libasound2 or dnf install alsa-lib is the fix. macOS and Windows use the system frameworks and need nothing installed.

Speaker

Plays what arrives on a recvonly audio track.
Definition
Constructing one attaches to the track without starting playback. Throws InvalidStateError if the track is not a recvonly audio track — a speaker on anything else would play nothing, forever, without saying why. Neither copyable nor movable: the device callback holds a pointer to it.

start() / stop()

Signature
Both idempotent, and stop() is safe to call from a handler. The device is opened on the first frame rather than in start(), because its sample rate and channel count come from the audio itself — guessing them and reopening on the first mismatch is audible.

dropped_ms() / under_runs()

Signature
The queue inside a Speaker is a jitter buffer, and that is the whole design: audio arrives in ~10 ms frames on the library’s thread, and a playback device asks for samples on its clock. Neither waits for the other, so the buffer absorbs the difference — and when it cannot, the two failure modes are worth telling apart. A single “glitches” counter would hide which of the two you have.

submit()

Signature
Push PCM in directly, without a track — for a caller mixing their own audio. This is what the track handler calls.

Microphone

Captures from a real microphone and pushes into a published sendonly track.
Definition
Nothing here starts by itself: constructing a Microphone opens no device, and start() is the only thing that does. Throws InvalidStateError if the track is not a sendonly audio track.
The track has to be published before start(). Publishing is what puts a sender behind the slot, and a push before it is refused rather than dropped — which is what blocks_refused() counts.

blocks_sent() / blocks_refused()

Signature
Blocks of PCM handed to the track, and captured blocks the track refused. A refusal is recorded rather than thrown: this runs on the device’s own thread, where there is nobody to catch it. A blocks_refused() climbing with blocks_sent() stuck at zero means the track was never published.

devices_available()

Signature
Whether this build can open real devices. false in a build made without the audio backend — the classes above then throw on start() rather than silently playing nothing.