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

如何为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 23:55:04