feat(voice): add iOS VAD runtime support
This commit is contained in:
@@ -11,8 +11,7 @@
|
||||
//! on PTT mode)
|
||||
//!
|
||||
//! Hard-mute is a final clamp; leaving the channel forces the gate
|
||||
//! to `false`. `VoiceActivity` is treated identically to
|
||||
//! `Continuous` per DEC-030 until a VAD implementation lands.
|
||||
//! to `false`. `VoiceActivity` is driven by Rust-owned VAD state.
|
||||
//!
|
||||
//! All four inputs are stored as atomics so any thread can update
|
||||
//! them without taking a lock. After each update we call
|
||||
@@ -92,6 +91,7 @@ pub struct TransmitModeSelector {
|
||||
in_channel: AtomicBool,
|
||||
hard_mute: AtomicBool,
|
||||
ptt_held: AtomicBool,
|
||||
voice_activity_open: AtomicBool,
|
||||
/// SDD-106 §5/§6 / SRS-209: latest resolved microphone
|
||||
/// permission state. Stored as a `u8` so writes from the
|
||||
/// JNI thread (Android permission requester → bridge) and
|
||||
@@ -133,6 +133,7 @@ impl TransmitModeSelector {
|
||||
in_channel: AtomicBool::new(false),
|
||||
hard_mute: AtomicBool::new(false),
|
||||
ptt_held: AtomicBool::new(false),
|
||||
voice_activity_open: AtomicBool::new(false),
|
||||
// SDD-106 §5: default to Granted on construction so
|
||||
// non-Android hosts (which never publish a permission
|
||||
// event) are not silently clamped. The Android bridge
|
||||
@@ -226,6 +227,17 @@ impl TransmitModeSelector {
|
||||
self.ptt_held.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Rust-owned VAD gate input for VoiceActivity mode.
|
||||
pub fn set_voice_activity_open(&self, v: bool) {
|
||||
self.voice_activity_open.store(v, Ordering::Relaxed);
|
||||
self.recompute();
|
||||
}
|
||||
|
||||
/// Current Rust-owned VAD gate state.
|
||||
pub fn voice_activity_open(&self) -> bool {
|
||||
self.voice_activity_open.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Subscribe to `ptt_held` transitions. Used by the
|
||||
/// missed-key-up watchdog (SAD-079) so it fires on the actual
|
||||
/// PTT-key-down lifetime, not on the resolved `transmit_active`
|
||||
@@ -265,8 +277,8 @@ impl TransmitModeSelector {
|
||||
}
|
||||
match self.mode() {
|
||||
TransmitMode::Ptt => self.ptt_held.load(Ordering::Relaxed),
|
||||
// DEC-030: VoiceActivity behaves as Continuous in v1.
|
||||
TransmitMode::Continuous | TransmitMode::VoiceActivity => true,
|
||||
TransmitMode::Continuous => true,
|
||||
TransmitMode::VoiceActivity => self.voice_activity_open.load(Ordering::Relaxed),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,13 +335,17 @@ mod tests {
|
||||
assert!(g.load(), "continuous independent of key state");
|
||||
}
|
||||
|
||||
/// SWE4-UV-037: voice-activity mode matches continuous in v1.
|
||||
/// SWE4-UV-037: voice-activity mode follows VAD state.
|
||||
#[test]
|
||||
fn voice_activity_matches_continuous_v1() {
|
||||
fn voice_activity_requires_vad_open() {
|
||||
let (g, s) = fresh();
|
||||
s.set_mode(TransmitMode::VoiceActivity);
|
||||
s.set_in_channel(true);
|
||||
assert!(!g.load());
|
||||
s.set_voice_activity_open(true);
|
||||
assert!(g.load());
|
||||
s.set_voice_activity_open(false);
|
||||
assert!(!g.load());
|
||||
}
|
||||
|
||||
/// SWE4-UV-037 / SWE4-UV-041: hard-mute clamps the transmit
|
||||
|
||||
Reference in New Issue
Block a user