macOS realtime audio was suffering buffer underruns on CoreAudio's VPIO output callback. Root cause was twofold: AudioHandler was decoded under a Mutex held across the realtime callback, and the render path hard-coded mono i16 output regardless of the channel count the callback actually exposed (CoreAudio occasionally hands the callback stereo or quad output buffers, in which case writing only every Nth sample produced silence + clicks). This change brings macOS in line with the lock-free Android audio architecture introduced for output stutter elimination: * chanora_audio: AudioPacket / AudioCommand / AudioEventQueue (previously gated to `target_os = "android"`) are now compiled on macOS too. The decode loop in AudioEngine pushes inbound packets into the queue; the VPIO render callback owns AudioHandler outright and drains the queue, so the realtime thread never blocks on a cross-thread mutex. set_client_volume also routes through the command queue on macOS instead of locking the handler. * voice_render.rs: new downmix_stereo_f32_to_interleaved_i16 helper downmixes stereo f32 from AudioHandler to mono i16 and replicates that mono sample across every output channel the callback exposes. The existing downmix_stereo_f32_to_mono_i16 helper is retained for iOS, where VPIO is reliably configured for single-channel output via the AudioUnit stream format we pin at unit-create time. Compile-gated to ios + test so the macos build doesn't warn on dead code. * ios_voice_unit.rs: render callback reads data.channels from the args struct and forwards it to the new interleaved helper, so the macOS path tolerates whatever channel count CoreAudio assigns. A level decimation counter avoids running sqrt+log10 on every callback (~93 Hz) when the Flutter consumer only reads at 30 Hz; same regression class as the capture-side fix already in engine.rs. * mobile_voice_backend.rs: VoiceAudioParams now carries event_producer on macOS, and the AudioHandler is no longer wrapped in Arc<Mutex<…>> on macOS because ownership moves into the render callback. iOS keeps Arc<Mutex<…>> because its callback design shares the handler with the decode task. * lib.rs: audio_event_queue module is now compiled on macOS in addition to android. apps/chanora_flutter/lib/main.dart wraps the home tree in a Stack and overlays AudioDebugStatsPanel on macOS so the live engine counters (callback rate, drift, queue depth) used to diagnose the underrun are visible while iterating on this code. iOS and other platforms are unaffected. apps/chanora_flutter/macos/Frameworks/chanora_bridge.framework binary is rebuilt with these changes so flutter run on macOS picks up the new realtime path without requiring developers to rebuild the Rust crate locally. cargo check -p chanora_audio passes on macOS host.
chanora_flutter
Chanora — cross-platform voice client for TeamSpeak-compatible servers.
Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
For help getting started with Flutter development, view the online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.