You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AVSpeechSynthesis与AVAudioSession切换连续中断4次触发IO错误问题

解决AVSpeechSynthesizer中断播报后切换监听的音频会话错误问题

这个问题我之前帮不少开发者排查过,核心是音频会话的状态没有被正确同步——用户主动中断播报时,AVSpeechSynthesizer的底层I/O操作可能还没完全停止,就急着切换到语音监听的会话配置,连续几次后就触发了系统的Deactivating an audio session that has running I/O错误。下面是具体的修复方案:

1. 中断播报时不要直接切换会话,等待合成器状态回调

不要在用户触发中断的瞬间(比如点击“停止”按钮)立刻修改AVAudioSession的状态,而是依赖AVSpeechSynthesizerDelegate的回调来处理后续操作,确保合成器完全停止后再切换:

// 确保你的音频管理类遵守AVSpeechSynthesizerDelegate
class AudioManager: NSObject, AVSpeechSynthesizerDelegate {
    private let synthesizer = AVSpeechSynthesizer()
    private let audioSession = AVAudioSession.sharedInstance()
    
    override init() {
        super.init()
        synthesizer.delegate = self
    }
    
    // 用户触发中断播报的方法
    func interruptSpeech() {
        if synthesizer.isSpeaking {
            // 只发送停止指令,不立刻处理会话
            synthesizer.stopSpeaking(at: .immediate)
        }
    }
    
    // 合成器正常结束后的回调
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) {
        switchToListeningMode()
    }
    
    // 合成器被中断取消后的回调
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancelSpeechUtterance utterance: AVSpeechUtterance) {
        switchToListeningMode()
    }
    
    // 统一处理会话清理和启动监听
    private func switchToListeningMode() {
        do {
            // 先停用播报的音频会话,确保I/O完全停止
            try audioSession.setActive(false, options: .waitForCompletion)
            // 配置并激活监听用的会话
            try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
            try audioSession.setActive(true)
            // 这里启动你的语音监听逻辑
            startVoiceListening()
        } catch {
            print("音频会话切换错误: \(error.localizedDescription)")
        }
    }
}

2. 启动播报前统一清理会话状态

每次启动语音播报前,也要确保监听的会话已经被正确停用,避免状态冲突:

func startSpeech(with content: String) {
    do {
        // 先停用监听会话(如果正在监听)
        try audioSession.setActive(false, options: .waitForCompletion)
        // 配置播报用的音频会话
        try audioSession.setCategory(.playback, mode: .default, options: .duckOthers)
        try audioSession.setActive(true)
        
        let utterance = AVSpeechUtterance(string: content)
        utterance.voice = AVSpeechSynthesisVoice(language: "zh-CN")
        synthesizer.speak(utterance)
    } catch {
        print("启动播报错误: \(error.localizedDescription)")
    }
}

3. 关键原理说明

AVSpeechSynthesizer调用stopSpeaking(at:)后,底层的音频I/O不会立即停止,需要一点时间完成资源清理。如果在这个间隙强行切换或停用会话,系统会检测到仍有运行中的I/O操作,从而抛出错误。

通过代理回调等待合成器完全停止、使用.waitForCompletion选项强制等待I/O结束,这两个操作能确保会话状态完全同步,避免多次中断后的状态积累问题。

内容的提问来源于stack exchange,提问作者coco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:28:45