From 581353b6d26b3581e51a1cef089d09aa6f58c90d Mon Sep 17 00:00:00 2001 From: Edison Jwa Date: Sun, 7 Jun 2026 19:21:13 +0900 Subject: [PATCH] docs(sdd): document VoiceActivity gate, macOS render cadence, iOS render cadence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/architecture/sdd.md: three new rows in the Audio Detailed Design table. 'VoiceActivity gate (capture-side)' documents voice_activity::VoiceActivityStateMachine — the 10 ms-cadence gate for TransmitMode::VoiceActivity with open-after (40 ms) / hangover (500 ms) / min-tx (200 ms) / weak-hold (30-100 frames) timers, plus live configure() re-clamping behaviour. 'macOS render cadence (producer + ring)' documents the 20 ms tokio producer task + crossbeam ArrayQueue ring with 100 ms prebuffer, the design chosen to decouple ingress quantums (20 ms Opus frames) from egress quantums (whatever VPIO asks for). 'iOS render cadence (direct-fill)' documents the direct-fill callback path with preallocated 4096x2 f32 scratch buffer and try_lock semantics (not blocking lock). Closes the VoiceActivity / macOS-producer-ring / iOS-direct-fill doc gaps flagged in the PR #27 'Deferred' list. --- docs/architecture/sdd.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/architecture/sdd.md b/docs/architecture/sdd.md index b47ae90..022cbc3 100644 --- a/docs/architecture/sdd.md +++ b/docs/architecture/sdd.md @@ -63,9 +63,12 @@ Design rules: | Codec | Opus encode/decode lives in `opus_voice.rs` and associated audio modules | | DSP chain | High-pass filter, noise suppression, echo cancellation, and AGC are represented by audio processing modules/backends | | Transmit control | `TransmitMode` supports `Ptt`, `Continuous`, and reserved `VoiceActivity`; `VoiceActivity` has no active MVP implementation | +| VoiceActivity gate (capture-side) | `voice_activity::VoiceActivityStateMachine` is the 10 ms-cadence gate for `TransmitMode::VoiceActivity`; open-after 40 ms (debounce), hangover 500 ms (anti-chatter), min-tx 200 ms (anti-flicker), weak-hold 30-100 frames (anti-stale-VAD); live `configure()` re-clamps existing timers on settings change without resetting state; 9 unit tests cover the main paths | | PTT | Desktop/mobile backends expose capability level and active backend; missed-key-up watchdog prevents stuck transmit | | Release tail | Tail handling prevents abrupt cutoffs after PTT release where configured | | Render peak limiter | `voice_render::limit_peak_inplace` is a single-pass, allocation-free per-frame peak scaler applied in both the macOS and iOS render callbacks before the i16 downmix; default threshold 0.99 prevents hard clipping on multi-client mixes that sum past 0 dBFS while remaining transparent for normal voice levels (allocation-free, lock-free, safe on the realtime audio thread) | +| macOS render cadence (producer + ring) | `ios_voice_unit.rs:851-961` runs a 20 ms tokio producer task that calls `AudioHandler::fill_buffer(1920)` and `force_push`es each sample into a `crossbeam ArrayQueue` (SPSC-effective, MPMC-but-wait-free-per-end); ring capacity 12000 samples ≈ 6.25× pull quantum; 100 ms prebuffer (`PREBUFFER_SAMPLES = 9600` stereo f32) before the VPIO render callback starts draining, matching Mumble's playout margin and WebRTC's kStartDelayMs order of magnitude | +| iOS render cadence (direct-fill) | `ios_voice_unit.rs:968-1034` does `AudioHandler::fill_buffer` directly in the VPIO render callback (VPIO on iOS requests 480-frame ≈ 10 ms slices that align with tsclientlib's 20 ms Opus frame); scratch buffer preallocated to 4096×2 f32 at setup time so the realtime callback never `resize()`s; `try_lock` (not `lock`) on the AudioHandler mutex so contention never stalls the realtime IO thread; on `WouldBlock` the callback emits silence and increments `callback_xrun` | | VPIO ducking config (macOS 14+) | `ios_voice_unit.rs` writes an 8-byte `AuVoiceIoOtherAudioDuckingConfiguration` struct (`m_enable_advanced_ducking = 0` disables dynamic voice-activity-driven ducking; `m_ducking_level = kAUVoiceIOOtherAudioDuckingLevelMin = 10`) to selector `kAUVoiceIOProperty_OtherAudioDuckingConfiguration` (= 2108) on the VoiceProcessingIO AudioUnit at startup, minimising the ducking of other apps' audio during a voice session; on macOS 13 the property is silently ignored (VPIO returns the default ducking behaviour) and the code logs a debug message and continues | | Benchmarks | Realtime capture, Opus, and resampler benchmarks provide advisory baseline evidence |