AVSpeechSynthesizer停止后新播报从断点续播如何重置
问题原因
这个是AVSpeechSynthesizer的系统级缓存问题:调用stopSpeaking(at: .immediate)立即中断播报后,系统不会立刻清空当前播放队列的进度缓存,如果在缓存清空完成前就对同一个合成器实例调用speak传入新文本,系统会错误地把新文本套用上一条内容的中断进度,导致新内容从截断位置开始播放,出现前半段丢失的情况。
可行解决方案
以下两个方案都可以彻底解决该问题,可根据业务场景选择:
方案1:重建合成器实例(最稳定,无时序依赖)
直接在每次启动新播报前重建AVSpeechSynthesizer实例,从根源上清空所有历史状态缓存,不需要等待停止回调,适配全系统版本。
修改后的核心代码如下:
class TextToSpeechService: NSObject, AVSpeechSynthesizerDelegate { // 改成可变变量,支持每次重建 var synthesizer: AVSpeechSynthesizer! var wasStopped: Bool = false init () { super.init() // 初始化首次使用的合成器 self.synthesizer = AVSpeechSynthesizer() self.synthesizer.delegate = self } public func start (text: String, completion: @escaping (Result<Void, TextToSpeechError>) -> Void) { self.setAudioPreferences() self.wasStopped = false // 停止旧实例、重建全新合成器,清空所有历史缓存 self.synthesizer.stopSpeaking(at: .immediate) self.synthesizer = AVSpeechSynthesizer() self.synthesizer.delegate = self let hasLanguage = AVSpeechSynthesisVoice.speechVoices().first(where: { $0.language == "de-DE" }) != nil if (hasLanguage) { let utterance = AVSpeechUtterance(string: text) utterance.voice = AVSpeechSynthesisVoice(identifier: "com.apple.ttsbundle.siri_Helena_de-DE_compact") // 放到主队列异步执行,避开同runloop的状态冲突 DispatchQueue.main.async { self.synthesizer.speak(utterance) } } else { print("LANGUAGE NOT AVAILABLE") } } public func stop () { self.wasStopped = true self.synthesizer.stopSpeaking(at: .immediate) } // 其余代理方法、音频设置方法保持原有逻辑即可 }
方案2:等待停止完成后再启动新播报(无需频繁创建实例)
如果不想频繁创建合成器实例,可以加状态标记,等didCancel代理回调触发、确认上一条内容完全取消后,再发起新的播报,避免缓存冲突。
核心修改点:
- 新增
isStopping状态位,标记当前是否正在执行停止操作 - 调用
stop时置位isStopping = true - 收到
didCancel回调时重置isStopping = false,如果此时有待播报的排队内容,再调用speak start方法检测到isStopping == true时,不要立刻调用speak,先把待播报文本暂存,等停止完成后再执行
验证效果
修改后调用逻辑的运行表现会符合预期:
- "Hello" - 已停止
- "Hello Earth"(完整播报)
内容的提问来源于stack exchange,提问作者chocolate cake
相关产品推荐
相关产品推荐

