Compare commits

...
Author SHA1 Message Date
Edison Jwa 7d6328db0f fix(audio): create CoreML/ONNX VAD worker on backend switch
Root cause: when VadBackend::SileroOnnx was selected, the capture
callback set the worker to None but never called try_new() to create
a replacement. The desktop path (engine.rs) correctly calls
SileroOnnxVadWorker::try_new(), but the iOS (CoreML) and Android
(ONNX) mobile paths only cleared the old worker without spawning a
new one. The VAD stayed stuck at 'warming up' forever, using the
WebRTC fallback for all voice activity detection.

Fix:
- iOS: call AppleCoreMlVadWorker::try_new() when SileroOnnx backend
  is selected; mark fallback only on failure
- Android: call SileroOnnxVadWorker::try_new() with the model bundle
  path when SileroOnnx backend is selected; same fallback logic
2026-06-10 19:56:36 +09:00
Edison Jwa 5ef0eee3f1 fix(ios): activate AVAudioSession before connect to fix voice
Root cause: OSStatus 561145187 - AVAudioSession was not in
.playAndRecord + .voiceChat mode when VoiceProcessingIO tried to start.

Two changes:
1. Activate session BEFORE rust.connect() so it is ready when auto-join runs
2. Defensive activation in Connected handler as backup (idempotent)

This fixes both the audio error and the subsequent voice_in already taken
error that occurred after auto-join consumed voice_in on failure.
2026-06-10 18:40:51 +09:00
3 changed files with 21 additions and 6 deletions
+2
View File
@@ -748,6 +748,7 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
_reconnectAttempt = null; _reconnectAttempt = null;
_reconnectDelay = null; _reconnectDelay = null;
}); });
unawaited(iosAudioSessionController.activate());
unawaited(_refreshSnapshot(recordActivity: false, reportErrors: true)); unawaited(_refreshSnapshot(recordActivity: false, reportErrors: true));
case rust.BridgeEvent_Lost(:final reason): case rust.BridgeEvent_Lost(:final reason):
_recordUiDiagnostic('connection', 'lost: $reason'); _recordUiDiagnostic('connection', 'lost: $reason');
@@ -1161,6 +1162,7 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
_chatMessages.clear(); _chatMessages.clear();
}); });
_releaseFocusedPttIfHeld(); _releaseFocusedPttIfHeld();
await iosAudioSessionController.activate();
try { try {
final snap = await rust.connect( final snap = await rust.connect(
host: (host ?? _hostCtl.text).trim(), host: (host ?? _hostCtl.text).trim(),
@@ -354,10 +354,17 @@ impl AndroidCaptureState {
self.fallback_warned_backend = None; self.fallback_warned_backend = None;
match vad_backend { match vad_backend {
crate::VadBackend::SileroOnnx => { crate::VadBackend::SileroOnnx => {
self.silero_vad_worker = None; let model_path = crate::vad::silero_model_bundle_path();
self.silero_vad_worker =
crate::vad::silero_onnx::SileroOnnxVadWorker::try_new(&model_path);
if self.silero_vad_worker.is_some() {
self.fallback_warned_backend = None;
self.audio_processing_stats.set_vad_fallback_active(false);
} else {
self.mark_vad_fallback_active(crate::VadBackend::SileroOnnx); self.mark_vad_fallback_active(crate::VadBackend::SileroOnnx);
self.audio_processing_stats.set_vad_fallback_active(true); self.audio_processing_stats.set_vad_fallback_active(true);
} }
}
_ => { _ => {
self.silero_vad_worker = None; self.silero_vad_worker = None;
} }
+7 -1
View File
@@ -351,9 +351,15 @@ impl IosCaptureState {
self.current_vad_backend = vad_backend; self.current_vad_backend = vad_backend;
self.fallback_warned_backend = None; self.fallback_warned_backend = None;
if vad_backend == crate::VadBackend::SileroOnnx { if vad_backend == crate::VadBackend::SileroOnnx {
self.silero_coreml_worker = None; self.silero_coreml_worker =
crate::vad::apple_coreml::AppleCoreMlVadWorker::try_new();
if self.silero_coreml_worker.is_some() {
self.fallback_warned_backend = None;
self.audio_processing_stats.set_vad_fallback_active(false);
} else {
self.mark_vad_fallback_active(crate::VadBackend::SileroOnnx); self.mark_vad_fallback_active(crate::VadBackend::SileroOnnx);
self.audio_processing_stats.set_vad_fallback_active(true); self.audio_processing_stats.set_vad_fallback_active(true);
}
} else { } else {
self.silero_coreml_worker = None; self.silero_coreml_worker = None;
self.audio_processing_stats.set_vad_fallback_active(false); self.audio_processing_stats.set_vad_fallback_active(false);