Implementing “user stopped speaking” notification for `SFSpeechRecognizer`

妖精的绣舞 提交于 2019-12-11 05:51:26

问题


I'm attempting to solve this problem: SFSpeechRecognizer - detect end of utterance

The problem is that SFSpeechRecognizer callback fires every time the detected speech string changes, but it only fires after 60 seconds of silence (whereupon it sets the isFinal flag).

The suggested technique is to start a 2 second timer each time to callback fires, first invalidating the timer if it is already set.

I have implemented this technique. However at my timer callback is never getting hit.

Can anyone tell me why?

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()
    }
}

LINKS:
- SFSpeechRecognizer - detect end of utterance


回答1:


It's because you create the timer but you never start it:

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

Instead, say

self.timer = Timer.scheduledTimer( ...


来源:https://stackoverflow.com/questions/57148596/implementing-user-stopped-speaking-notification-for-sfspeechrecognizer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!