@@ -106,6 +106,7 @@ final class VoiceWakeManager: NSObject {
106106 private var tapDrainTask : Task < Void , Never > ?
107107 private var scheduledStartTask : Task < Void , Never > ?
108108 private var isStarting : Bool = false
109+ private var isSuspendedForExternalAudio : Bool = false
109110
110111 private var lastDispatched : String ?
111112 private var onCommand : ( @Sendable ( String) async -> Void ) ?
@@ -162,7 +163,10 @@ final class VoiceWakeManager: NSObject {
162163 self . suppressedByTalk = suppressed
163164 if suppressed {
164165 self . cancelScheduledStart ( )
165- _ = self . suspendForExternalAudioCapture ( )
166+ if self . isListening {
167+ self . isListening = false
168+ self . tearDownRecognitionPipeline ( )
169+ }
166170 if self . isEnabled {
167171 self . statusText = " Paused "
168172 }
@@ -196,6 +200,11 @@ final class VoiceWakeManager: NSObject {
196200 guard self . isEnabled else { return }
197201 if self . isListening { return }
198202 if self . isStarting { return }
203+ guard !self . isSuspendedForExternalAudio else {
204+ self . isListening = false
205+ self . statusText = " Paused "
206+ return
207+ }
199208
200209 self . isStarting = true
201210 defer { self . isStarting = false }
@@ -241,9 +250,9 @@ final class VoiceWakeManager: NSObject {
241250 return
242251 }
243252
244- guard self . isEnabled, !self . suppressedByTalk else {
253+ guard self . isEnabled, !self . suppressedByTalk, ! self . isSuspendedForExternalAudio else {
245254 self . isListening = false
246- self . statusText = self . suppressedByTalk ? " Paused " : " Off "
255+ self . statusText = self . isEnabled ? " Paused " : " Off "
247256 return
248257 }
249258
@@ -262,17 +271,19 @@ final class VoiceWakeManager: NSObject {
262271 self . isEnabled = false
263272 self . isListening = false
264273 self . statusText = " Off "
274+ self . isSuspendedForExternalAudio = false
265275 self . cancelScheduledStart ( )
266276 self . tearDownRecognitionPipeline ( )
267277 }
268278
269279 /// Temporarily releases the microphone so other subsystems (e.g. camera video capture) can record audio.
270- /// Returns `true` when listening or a pending restart was active and was suspended.
280+ /// Returns `true` when listening, starting, or a pending restart was active and was suspended.
271281 func suspendForExternalAudioCapture( ) -> Bool {
272282 let hadPendingStart = self . scheduledStartTask != nil
273283 self . cancelScheduledStart ( )
274- guard self . isEnabled, self . isListening || hadPendingStart else { return false }
284+ guard self . isEnabled, self . isListening || self . isStarting || hadPendingStart else { return false }
275285
286+ self . isSuspendedForExternalAudio = true
276287 self . isListening = false
277288 self . statusText = " Paused "
278289 self . tearDownRecognitionPipeline ( )
@@ -281,11 +292,12 @@ final class VoiceWakeManager: NSObject {
281292
282293 func resumeAfterExternalAudioCapture( wasSuspended: Bool ) {
283294 guard wasSuspended else { return }
295+ self . isSuspendedForExternalAudio = false
284296 self . scheduleStart ( after: Self . externalAudioResumeDelayNs)
285297 }
286298
287299 private func startRecognition( ) throws {
288- guard self . isEnabled, !self . suppressedByTalk else { return }
300+ guard self . isEnabled, !self . suppressedByTalk, ! self . isSuspendedForExternalAudio else { return }
289301
290302 self . recognitionTask? . cancel ( )
291303 self . recognitionTask = nil
@@ -517,5 +529,9 @@ extension VoiceWakeManager {
517529 func _test_handleRecognitionCallback( transcript: String ? , segments: [ WakeWordSegment ] , errorText: String ? ) {
518530 self . handleRecognitionCallback ( transcript: transcript, segments: segments, errorText: errorText)
519531 }
532+
533+ func _test_setStartInFlight( _ isStarting: Bool ) {
534+ self . isStarting = isStarting
535+ }
520536}
521537#endif
0 commit comments