Why Did I Build This?
"Standard protocols like WebRTC are often overkill for simple audio streaming. I built this to have a lightweight, memory-safe Rust setup that strictly transmits raw PCM audio (`f32` LE bytes) over WebSockets. The goal was to kill feedback loops (echo) by filtering source addresses at the routing layer and to manually sync hardware sample-rate disparities right on the client."
Architecture & Decisions
The core bridges CPAL's synchronous hardware threads with Tokio's asynchronous network I/O. The client downmixes stereo microphone input to mono to save bandwidth, then pipes the bytes to the async WebSocket writer via unbounded MPSC channels. On the playback side, a `Mutex`-wrapped `VecDeque` acts as a Jitter Buffer (triggering at 1000 samples) to smooth out network spikes. The server just uses a `tokio::sync::broadcast` channel mapped with `(SocketAddr, Message)` tuples, broadcasting packets to everyone except the original sender to prevent echo.
Key Features
- 01.Low-latency audio capture and playback using CPAL
- 02.`VecDeque`-based concurrent Jitter Buffer to handle network instability
- 03.Server-side echo suppression using `SocketAddr` packet mapping
- 04.Cargo feature flags to drop hardware audio dependencies (like ALSA/PulseAudio) from the server build
- 05.On-the-fly Mono/Stereo downmixing and upmixing to keep bandwidth usage low
This project highlights how to cleanly manage the boundary between synchronous hardware interrupts and async event loops. Forcing `in_config.sample_rate = out_config.sample_rate;` on the client is a straightforward, low-level fix for hardware clock mismatches. Also, hiding the `cpal` dependency behind a Cargo feature flag keeps the server binary incredibly lean, cutting down the Docker footprint and reducing the attack surface in production.