如何为Apple的AVSpeechSynthesizer实现语音快进/快退功能?
AVSpeechSynthesizer 快进/快退功能实现方案
AVSpeechSynthesizer 本身不支持直接基于时间的快进或快退操作(比如直接跳转15秒),但可以通过拆分文本片段+进度追踪的方式模拟实现类似功能。
实现思路
- 提前将需要朗读的文本拆分为多个独立的小片段(比如按句子、段落,或者估算对应15秒左右的文本块)
- 维护当前正在朗读的片段索引与朗读状态
- 执行快进/快退时,停止当前朗读,跳转到目标索引的片段继续朗读
修改后的代码示例
private var synthesizer = AVSpeechSynthesizer() @Published var attributedText: NSAttributedString? // 存储拆分后的朗读片段 private var utterances: [AVSpeechUtterance] = [] // 当前正在朗读的片段索引 private var currentUtteranceIndex = 0 override init() { super.init() synthesizer.delegate = self } private func createUtterance(_ text: String) -> AVSpeechUtterance { let utterance = AVSpeechUtterance(string: text) utterance.rate = 0.5 utterance.pitchMultiplier = 0.8 utterance.postUtteranceDelay = 0.2 utterance.volume = 0.8 let voice = AVSpeechSynthesisVoice(language: "en-US") utterance.voice = voice return utterance } // 拆分文本并初始化朗读队列 func prepareAndSpeak(_ text: String) { // 按标点拆分句子,可根据需求调整拆分规则(比如按段落、固定字符数) let sentences = text.components(separatedBy: [".", "?", "!"].map { String($0) }) .filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty } utterances = sentences.map { createUtterance($0) } currentUtteranceIndex = 0 synthesizer.stopSpeaking(at: .immediate) if !utterances.isEmpty { synthesizer.speak(utterances[currentUtteranceIndex]) } } func pauseSpeaking(at boundary: AVSpeechBoundary = .word) { synthesizer.pauseSpeaking(at: boundary) } func continueSpeaking() { synthesizer.continueSpeaking() } // 快进(跳转到下一个片段) func skipForward() { guard currentUtteranceIndex < utterances.count - 1 else { return } synthesizer.stopSpeaking(at: .immediate) currentUtteranceIndex += 1 synthesizer.speak(utterances[currentUtteranceIndex]) } // 快退(跳转到上一个片段) func skipBackward() { guard currentUtteranceIndex > 0 else { return } synthesizer.stopSpeaking(at: .immediate) currentUtteranceIndex -= 1 synthesizer.speak(utterances[currentUtteranceIndex]) } var isSpeaking: Bool { synthesizer.isSpeaking } var isPaused: Bool { synthesizer.isPaused } // 实现代理方法,自动播放下一个片段 extension YourClassName: AVSpeechSynthesizerDelegate { func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) { if currentUtteranceIndex < utterances.count - 1 { currentUtteranceIndex += 1 synthesizer.speak(utterances[currentUtteranceIndex]) } } }
补充说明
- 如果需要更接近15秒的精准跳转,可以通过测试当前语音速率下的文本朗读时长,调整文本拆分粒度(比如估算对应15秒的字符数,按字符数拆分)。
- 由于AVSpeechSynthesizer未暴露内部时间进度信息,无法做到完全精确的时间跳转,拆分片段是当前可行的替代方案。
内容的提问来源于stack exchange,提问作者Zhou Haibo
相关产品推荐
相关产品推荐

