Skip to content

Fix handle 58 second timeout with full engine restart to prevent teleprompter freeze#59

Open
G10Bi wants to merge 4 commits into
f:masterfrom
G10Bi:Fix-handle-58-second-timeout-with-full-engine-restart-to-prevent-teleprompter-freeze
Open

Fix handle 58 second timeout with full engine restart to prevent teleprompter freeze#59
G10Bi wants to merge 4 commits into
f:masterfrom
G10Bi:Fix-handle-58-second-timeout-with-full-engine-restart-to-prevent-teleprompter-freeze

Conversation

@G10Bi

@G10Bi G10Bi commented Jun 12, 2026

Copy link
Copy Markdown

Problem

After ~58 seconds of continuous speech recognition, SFSpeechRecognizer hits
its internal timeout (error 1110 / 216) and the teleprompter freezes permanently.

The original code called restartTask() — a soft restart that reuses the existing
AVAudioEngine. But macOS also pauses the microphone hardware during these
timeouts. The engine's internal state becomes unrecoverable, and no amount of
task-level restart can bring it back. The only way out was a manual stop/start.

Root cause

AVAudioEngine caches the audio device format internally. After the system pauses
the mic (triggered by the 58s speech recognition limit), the cached format is
invalidated but the engine doesn't detect this. A soft restart (restartTask())
only recreates the SFSpeechRecognitionTask, not the engine. The new task receives
silence or corrupt buffers and fails silently.

Solution

Added forceRestartAfterTimeout() — a method that performs a complete teardown
and recreation
of the entire audio stack:

private func forceRestartAfterTimeout() {
    // 1. Save state
    let savedOffset = recognizedCharCount
    let savedSource = sourceText
    
    // 2. Full cleanup (task + engine + tap)
    cleanupRecognition()
    
    // 3. Fresh engine — picks up current hardware format
    audioEngine = AVAudioEngine()
    
    // 4. Restore state
    sourceText = savedSource
    recognizedCharCount = savedOffset
    matchStartOffset = savedOffset
    
    // 5. Restart after short delay for resource release
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
        self.beginRecognition()
    }
}

github-actions Bot and others added 4 commits February 26, 2026 21:47
## Problem

After ~58 seconds of continuous speech recognition, `SFSpeechRecognizer` hits
its internal timeout (error 1110 / 216) and the teleprompter freezes permanently.

The original code called `restartTask()` — a soft restart that reuses the existing
`AVAudioEngine`. But macOS also **pauses the microphone hardware** during these
timeouts. The engine's internal state becomes unrecoverable, and no amount of
task-level restart can bring it back. The only way out was a manual stop/start.

## Root cause

`AVAudioEngine` caches the audio device format internally. After the system pauses
the mic (triggered by the 58s speech recognition limit), the cached format is
invalidated but the engine doesn't detect this. A soft restart (`restartTask()`)
only recreates the `SFSpeechRecognitionTask`, not the engine. The new task receives
silence or corrupt buffers and fails silently.

## Solution

Added `forceRestartAfterTimeout()` — a method that performs a **complete teardown
and recreation** of the entire audio stack:

```swift
private func forceRestartAfterTimeout() {
    // 1. Save state
    let savedOffset = recognizedCharCount
    let savedSource = sourceText
    
    // 2. Full cleanup (task + engine + tap)
    cleanupRecognition()
    
    // 3. Fresh engine — picks up current hardware format
    audioEngine = AVAudioEngine()
    
    // 4. Restore state
    sourceText = savedSource
    recognizedCharCount = savedOffset
    matchStartOffset = savedOffset
    
    // 5. Restart after short delay for resource release
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
        self.beginRecognition()
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant