Cyberwave is in Private Beta.
Request early access to get access to the Cyberwave dashboard.
What It Provides
The native speaker driver runs on the edge device that has the speaker attached. It is the downstream/playback counterpart of the microphone driver — it consumes a WebRTC audio stream from the media-service, hands the decoded PCM tosounddevice / PortAudio, and plays it through the configured output device. It also subscribes to the workflow-emitted Zenoh cue channels so the device can play short status chimes from a bundled MP3 library.
The driver lives at:
generic-microphone and reuses the same BaseAudioTrack / BaseAudioStreamer SDK base classes — the speaker subclasses are SpeakerAudioTrack(BaseAudioTrack) and SpeakerAudioStreamer(BaseAudioStreamer) in cyberwave.sensor.speaker.
Quick start
./run-local.sh from the driver directory — Docker Desktop on macOS runs in LinuxKit and cannot expose /dev/snd, so playback must happen on the host so PortAudio can talk to CoreAudio.
MQTT command surface (TR-1.26)
The driver also mirrors the legacy
start_audio / stop_audio verbs so existing integrations keep working.
Zenoh cue contract (TR-1.17 / TR-1.18)
The driver subscribes to two cue channels withpolicy="latest":
Payloads are the standard Cyberwave envelope (
HeaderTemplate + JSON body). The driver dedups identical (channel, command) pairs inside a 250 ms window, matching the browser-side handler.
Standard catalog sensor configuration
The standard Cyberwave catalog speaker asset declares one sensor block.id and name are "audio" (same routing key as the native microphone driver); type is "speaker" (passive consumer).
parameters → CYBERWAVE_METADATA_* env vars. The driver publishes WebRTC offers with:
Media-service contract (TR-1.21 / TR-1.22 / TR-1.24)
WebRTC offers carrysensor (routing id), sensor_type (catalog type), and role. The media-service classifies offers before dispatch:
Catalog standard pairs: microphone
sensor_type: "audio" + role: "producer"; speaker sensor_type: "speaker" + role: "consumer". Both use sensor: "audio".
Offers that conflict with this contract are rejected (error answer on webrtc-answer). Raw microphone traffic stays inside the media-service — only the speaker consumer leg is fanned out to edge/frontend peers.
Configuration (CYBERWAVE_* env vars)
Nothing about the device, sample rate, channel count, or routing target is hard-coded — every value flows through env vars, runtime device discovery, or the twin metadata
audio_device block.
Cross-platform behaviour
- Linux — ALSA direct pass-through via
/dev/sndand--group-add audio. All discovery usessounddevice.query_devices(); onpyudev-capable hosts, hot-plug events come from udev. - macOS — Bare-metal CoreAudio (no Docker), with full in-container DSP (volume, per-channel gain, channel routing matrix) applied before audio leaves the Python process. PulseAudio-CoreAudio bridging is a documented fallback for power users.
Hot-plug (TR-1.11 – TR-1.13)
Disconnecting the speaker triggersAudioDeviceMonitor, which reopens the SFU consumer leg + sounddevice.OutputStream against the new device — even when the replacement reports a different channel count or sample rate. The driver re-resolves the selected device against twin metadata on every recovery cycle and publishes SPEAKER_FAILURE / resolution alerts as the hardware comes and goes.
SDK helpers
The driver delegates everything tocyberwave.sensor.speaker:
SpeakerAudioStreamer— WebRTC + MQTT lifecycle (subclassesBaseAudioStreamer)SpeakerAudioTrack— minimal upstream track required by the WebRTC SDP contractHostSpeakerCapture— hostsounddevice.OutputStreamwrapper with file / queue / Zenoh sourcesplay_file(...),associate_speaker_to_microphone(...),associate_speaker_to_microphones(...)— high-level helpers from TR-1.25
sensor: "audio" routing contract.