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

iOS开发:如何在持续运行SFSpeechRecognizer时定时清除识别结果?

解决方案

核心问题分析

你当前代码的关键问题是:在识别完成(isFinal = true)或出现错误时,调用了audioEngine.stop()和inputNode.removeTap(onBus: 0),直接终止了音频输入,导致麦克风关闭无法持续识别。另外,移除输入节点的tap并不能清除识别结果,反而会中断音频流。

具体修改步骤

  1. 保留音频引擎持续运行
    移除error != nil || isFinal分支中停止音频引擎、移除tap的代码,仅在用户主动停止识别时才执行这些操作。当识别完成(isFinal)时,只需要取消当前的recognitionTask并重新创建新任务,就能继续接收新的语音输入。

  2. 实现定时清除识别结果
    使用定时器实现每隔2秒清空文本框,每次收到新的识别结果时重置定时器,避免在用户说话过程中提前清空内容,同时确保在主线程更新UI。

修改后的完整代码

private var clearResultTimer: Timer?

private func startRecording() throws {
    // 取消之前的任务和定时器
    recognitionTask?.cancel()
    self.recognitionTask = nil
    clearResultTimer?.invalidate()
    
    let audioSession = AVAudioSession.sharedInstance()
    try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
    try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
    let inputNode = audioEngine.inputNode

    recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
    guard let recognitionRequest = recognitionRequest else { fatalError("Unable to create a SFSpeechAudioBufferRecognitionRequest object") }
    recognitionRequest.shouldReportPartialResults = true
    
    if #available(iOS 13, *) {
        recognitionRequest.requiresOnDeviceRecognition = false
    }
    
    recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { [weak self] result, error in
        guard let self = self else { return }
        var isFinal = false
        
        if let result = result {
            self.textView.text = result.bestTranscription.formattedString
            
            // 重置定时器:每次收到新结果后,2秒后清空
            self.clearResultTimer?.invalidate()
            self.clearResultTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { _ in
                DispatchQueue.main.async {
                    self.textView.text = "(Go ahead, I'm listening)"
                }
            }
            
            isFinal = result.isFinal
            print("Text \(result.bestTranscription.formattedString)")
        }
        
        // 错误处理:根据实际错误类型决定是否终止,这里仅打印错误
        if error != nil {
            print("识别错误: \(error!.localizedDescription)")
        } else if isFinal {
            // 识别完成后,重新创建任务继续监听
            self.recognitionTask?.cancel()
            self.recognitionTask = nil
            do {
                try self.startRecording()
            } catch {
                print("重新启动识别失败: \(error.localizedDescription)")
            }
        }
    }
    
    let recordingFormat = inputNode.outputFormat(forBus: 0)
    // 避免重复安装tap,先移除再添加
    inputNode.removeTap(onBus: 0)
    inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
        self.recognitionRequest?.append(buffer)
    }
    
    if !audioEngine.isRunning {
        audioEngine.prepare()
        try audioEngine.start()
    }
    
    textView.text = "(Go ahead, I'm listening)"
}

// 供用户主动停止识别调用的方法
private func stopRecording() {
    recognitionTask?.cancel()
    recognitionTask = nil
    clearResultTimer?.invalidate()
    audioEngine.stop()
    audioEngine.inputNode.removeTap(onBus: 0)
    recognitionRequest = nil
    recordButton.isEnabled = true
    recordButton.setTitle("Start Recording", for: [])
}

关键说明

  • 定时器重置逻辑:每次收到新的识别结果(包括部分结果)时重置定时器,确保只有用户停止说话2秒后才清空结果,不打断正在进行的识别。
  • 持续识别实现:当一次语音识别完成(isFinal = true)时,取消当前任务并重新启动识别,实现无间断监听。
  • 错误处理优化:仅在严重错误(如权限失效、硬件故障)时才考虑停止引擎,普通识别错误可忽略,保持识别持续运行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 11:45:37