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

如何重置SFSpeechAudioBufferRecognitionRequest缓冲区?无需销毁对象

问题

我编写了如下record函数:

func record(sketchVM: SketchViewModel, isRecording: Binding<Bool>) {

    // MARK: 1. Create a recognizer.

    guard let recognizer = SFSpeechRecognizer(), recognizer.isAvailable else {
        handleError(withMessage: "Speech recognizer not available.")
        return
    }

    // MARK: 2. Create a speech recognition request.

    recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
    guard let recognitionRequest = recognitionRequest else { return }
    recognitionRequest.shouldReportPartialResults = true

    recognizer.recognitionTask(with: recognitionRequest) { result, error in
        guard error == nil else { self.handleError(withMessage: error!.localizedDescription); return }
        guard let result = result else { return }

        print("got a new result: \(result.bestTranscription.formattedString), final : \(result.isFinal)")

    }

    // MARK: 3. Create a recording and classification pipeline.

    audioEngine = AVAudioEngine()
    guard let audioEngine = audioEngine else { return }

    inputNode = audioEngine.inputNode
    guard let inputNode = inputNode else { return }

    let recordingFormat = inputNode.outputFormat(forBus: 0)
    inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { buffer, _ in
        self.recognitionRequest?.append(buffer)
    }

    // Build the graph.
    audioEngine.prepare()

    // MARK: 4. Start recognizing speech.

    do {
        // Activate the session.
        audioSession = AVAudioSession.sharedInstance()
        guard let audioSession = audioSession else { return }
        try audioSession.setActive(false)
        try audioSession.setCategory(.record, mode: .default)
        try audioSession.setActive(true, options: .notifyOthersOnDeactivation)

        // Start the processing pipeline.
        try audioEngine.start()
        Task { @MainActor in
            isRecording.wrappedValue = true
        }
    } catch {
        handleError(withMessage: error.localizedDescription)
    }
}

该函数功能正常,但result.bestTranscription会持续追加之前的录音内容。我希望重启录音,但不想销毁所有会话、请求、引擎对象,请问是否可以在保留这些对象的前提下,清除已保存的录音内容?

解决方案

不行,没办法在保留现有SFSpeechAudioBufferRecognitionRequest和识别任务的前提下清除已积累的转录内容——因为SFSpeechRecognitionTask和请求是绑定的,请求一旦开始接收音频缓冲区,转录结果就会持续累加,苹果没有提供公开API来重置这个状态。

不过你不需要销毁所有对象,只需要重置识别请求和任务,音频引擎和AVAudioSession可以保留复用,步骤如下:

1. 先停止当前的识别任务与音频输入

先把正在运行的识别任务结束,移除音频输入节点的tap并停止引擎:

// 建议在类中添加属性保存当前的识别任务
var recognitionTask: SFSpeechRecognitionTask?

private func resetCurrentRecording() {
    // 结束旧的识别任务
    recognitionTask?.finish()
    recognitionTask = nil
    
    // 移除输入节点的tap,避免重复安装导致崩溃
    inputNode?.removeTap(onBus: 0)
    // 停止音频引擎
    audioEngine?.stop()
}

2. 复用引擎与会话,重新创建请求和任务

重启录音时,不需要重新初始化audioEngine和audioSession,只需要重新创建识别请求和任务,再重新配置输入tap并启动引擎:

func restartRecording(sketchVM: SketchViewModel, isRecording: Binding<Bool>) {
    resetCurrentRecording()
    
    guard let recognizer = SFSpeechRecognizer(), recognizer.isAvailable else {
        handleError(withMessage: "Speech recognizer not available.")
        return
    }
    
    // 重新创建识别请求
    recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
    guard let recognitionRequest = recognitionRequest else { return }
    recognitionRequest.shouldReportPartialResults = true
    
    // 重新创建识别任务并保存引用
    recognitionTask = recognizer.recognitionTask(with: recognitionRequest) { result, error in
        guard error == nil else { self.handleError(withMessage: error!.localizedDescription); return }
        guard let result = result else { return }
        
        print("got a new result: \(result.bestTranscription.formattedString), final : \(result.isFinal)")
    }
    
    // 重新安装输入tap
    guard let inputNode = audioEngine?.inputNode else { return }
    let recordingFormat = inputNode.outputFormat(forBus: 0)
    inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { buffer, _ in
        self.recognitionRequest?.append(buffer)
    }
    
    audioEngine?.prepare()
    
    do {
        // 复用已有的AVAudioSession,无需重复设置类别(只要之前配置正确)
        try audioSession?.setActive(true, options: .notifyOthersOnDeactivation)
        try audioEngine?.start()
        
        Task { @MainActor in
            isRecording.wrappedValue = true
        }
    } catch {
        handleError(withMessage: error.localizedDescription)
    }
}

关键注意点

  • SFSpeechAudioBufferRecognitionRequest是一次性对象,一旦开始接收音频就无法重置内部缓存,必须重新创建。
  • 音频引擎和AVAudioSession可以安全复用,只要在重启前正确停止引擎、移除tap,就能避免资源泄漏和崩溃。
  • 一定要调用recognitionTask?.finish()结束旧任务,否则会导致资源占用、识别结果混乱,甚至新任务无法正常启动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 02:22:01