为 `SFSpeechRecognizer` 实施 "user stopped speaking" 通知

Implementing "user stopped speaking" notification for `SFSpeechRecognizer`

我正在尝试解决这个问题:SFSpeechRecognizer - detect end of utterance

问题是每次检测到的语音字符串发生变化时都会触发 SFSpeechRecognizer 回调,但它只会在 60 秒的静音后触发(因此它会设置 isFinal 标志)。

建议的技术是每次启动一个 2 秒的计时器以触发回调,如果已设置计时器,则首先使它无效。

我已经实现了这个技术。然而,在我的计时器回调中永远不会被击中。

谁能告诉我为什么?

import Foundation
import Speech

@objc
public class Dictation : NSObject, SFSpeechRecognizerDelegate
{
    @objc static let notification_finalText = Notification.Name("speech_gotFinalText")
    @objc static let notification_interimText = Notification.Name("speech_textDidChange")

    private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-UK"))!

    var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?

    private var recognitionTask: SFSpeechRecognitionTask?

    let audioEngine = AVAudioEngine()

    @objc var text_tmp   : String? = ""
    @objc var text_final : String? = ""

    var timer : Timer?

    override init()
    {
        super.init()

        speechRecognizer.delegate = self

        SFSpeechRecognizer.requestAuthorization { authStatus in
            if authStatus != .authorized {
                exit(0)
            }
        }
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    @objc
    func tryStartRecording()
    {
        try! startRecording()
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    func startRecording() throws
    {
        text_final = ""

        // Cancel the previous task if it's running.
        if let recognitionTask = recognitionTask {
            recognitionTask.cancel()
            self.recognitionTask = nil
        }

        recognitionRequest = SFSpeechAudioBufferRecognitionRequest()

        let inputNode = audioEngine.inputNode
        /*
         ^ causes:
         [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x600000247200> F8BB1C28-BAE8-11D6-9C31-00039315CD46
         HALC_ShellDriverPlugIn::Open: Can't get a pointer to the Open routine
         HALC_ShellDriverPlugIn::Open: Can't get a pointer to the Open routine
         */

        if inputNode.inputFormat(forBus: 0).sampleRate == 0 {
            fatalError("Audio engine has no input node")
        }

        guard let recognitionRequest = recognitionRequest else {
            fatalError("Unable to created a SFSpeechAudioBufferRecognitionRequest object")
        }

        // Configure request so that results are returned before audio recording is finished
        recognitionRequest.shouldReportPartialResults = true

        // A recognition task represents a speech recognition session.
        // We keep a reference to the task so that it can be cancelled.
        recognitionTask = speechRecognizer.recognitionTask( with: recognitionRequest )
        { result, error in
            self.timer?.invalidate()
            print( "New Timer" )
            self.timer = Timer(timeInterval:2.0, repeats:false) { _ in

                print( "*** Timer Callback -- NEVER HITS! ***" )

                self.timer?.invalidate()
                self.text_final = result!.bestTranscription.formattedString

                NotificationCenter.default.post( name: Dictation.notification_finalText,  object: nil )

                self.stopRecording()
            }

            var isFinal = false

            if let result = result {
                isFinal = result.isFinal

                if isFinal {
                    self.text_final = result.bestTranscription.formattedString
                } else {
                    self.text_tmp = result.bestTranscription.formattedString
                }

                let notification = isFinal ? Dictation.notification_finalText : Dictation.notification_interimText

                NotificationCenter.default.post( name: notification,  object: nil )
            }

            if error != nil  ||  isFinal {
                self.audioEngine.stop()
                inputNode.removeTap( onBus: 0 )

                self.recognitionRequest = nil
                self.recognitionTask = nil
            }
        }

        let recordingFormat = inputNode.outputFormat(forBus: 0)

        inputNode.installTap( onBus: 0,  bufferSize: 1024,  format: recordingFormat )
        { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
            self.recognitionRequest?.append( buffer )
        }

        audioEngine.prepare()

        try audioEngine.start()

        print( self.audioEngine.description )
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    @objc
    func stopRecording()
    {
        audioEngine.stop()
        recognitionRequest?.endAudio()
    }
}

链接:
- SFSpeechRecognizer - detect end of utterance

这是因为您创建了计时器但从未启动它:

self.timer = Timer(timeInterval:2.0, repeats:false)

相反,说

self.timer = Timer.scheduledTimer( ...