fix(audio,flutter): restore VAD on iOS and fix Android build

- Add iOS to voiceActivityTransmitAvailable — iOS has CoreML Silero
  VAD pipeline (AppleCoreMlVadWorker) but was excluded by DEC-030
  gating that predated the CoreML integration
- Inline deleted VadWorkerPolicy in android_voice_unit.rs — PR #37
  removed the enum from vad/mod.rs but missed updating Android
This commit is contained in:
Edison Jwa
2026-06-10 09:18:32 +09:00
parent ddd977796f
commit 08d7ace25d
2 changed files with 24 additions and 40 deletions
@@ -65,13 +65,12 @@ List<ButtonSegment<rust.BridgeTransmitMode>> transmitModeSegmentsFor({
/// True when this host advertises VAD transmit per DEC-030. /// True when this host advertises VAD transmit per DEC-030.
/// ///
/// The desktop Silero ONNX + WebRTC fallback ships on Windows and /// The desktop Silero ONNX + WebRTC fallback ships on Windows and
/// Linux; the Android Oboe + WebRTC path covers Android. iOS and /// Linux; the Android Oboe + WebRTC path covers Android; iOS uses the
/// macOS still rely on the Apple VoiceProcessingIO unit and have no /// Apple CoreML Silero VAD pipeline via `vad::apple_coreml`. macOS is
/// Chanora-owned VAD pipeline, so the bridge cannot honour the /// still gated until its VAD pipeline is confirmed.
/// voice-activity transmit mode there.
bool get voiceActivityTransmitAvailable { bool get voiceActivityTransmitAvailable {
if (kIsWeb) return false; if (kIsWeb) return false;
return Platform.isWindows || Platform.isLinux || Platform.isAndroid; return Platform.isWindows || Platform.isLinux || Platform.isAndroid || Platform.isIOS;
} }
/// Android hardware/WebRTC selector segments. /// Android hardware/WebRTC selector segments.
+20 -35
View File
@@ -375,38 +375,20 @@ impl AndroidCaptureState {
speech: true, speech: true,
} }
} else if vad_backend == crate::VadBackend::SileroOnnx { } else if vad_backend == crate::VadBackend::SileroOnnx {
match crate::vad::callback_vad_worker_policy( if let Some(worker) = self.silero_vad_worker.as_ref() {
voice_activity_mode, let enqueued = worker.try_send(capture_seq, &frame);
vad_backend, if !worker.is_stale(capture_seq) {
self.silero_vad_worker.is_some(), let p = worker.latest_probability();
) { crate::vad::VadOutput {
crate::vad::VadWorkerPolicy::UseWorker => { probability: p,
let worker = self speech: p >= 0.5,
.silero_vad_worker
.as_ref()
.expect("policy checked worker");
let enqueued = worker.try_send(capture_seq, &frame);
if !worker.is_stale(capture_seq) {
let p = worker.latest_probability();
crate::vad::VadOutput {
probability: p,
speech: p >= 0.5,
}
} else if enqueued {
crate::vad::VadOutput {
probability: 0.0,
speech: false,
}
} else {
used_fallback_vad = true;
self.mark_vad_fallback_active(vad_backend);
crate::vad::VoiceActivityDetector::process_10ms(
&mut self.vad_detector,
&frame,
)
} }
} } else if enqueued {
crate::vad::VadWorkerPolicy::UseFallback => { crate::vad::VadOutput {
probability: 0.0,
speech: false,
}
} else {
used_fallback_vad = true; used_fallback_vad = true;
self.mark_vad_fallback_active(vad_backend); self.mark_vad_fallback_active(vad_backend);
crate::vad::VoiceActivityDetector::process_10ms( crate::vad::VoiceActivityDetector::process_10ms(
@@ -414,10 +396,13 @@ impl AndroidCaptureState {
&frame, &frame,
) )
} }
crate::vad::VadWorkerPolicy::NotModelBacked => crate::vad::VadOutput { } else {
probability: 1.0, used_fallback_vad = true;
speech: true, self.mark_vad_fallback_active(vad_backend);
}, crate::vad::VoiceActivityDetector::process_10ms(
&mut self.vad_detector,
&frame,
)
} }
} else { } else {
crate::vad::VoiceActivityDetector::process_10ms(&mut self.vad_detector, &frame) crate::vad::VoiceActivityDetector::process_10ms(&mut self.vad_detector, &frame)