AVSpeechRecognizer: required condition is false: _

2019-07-12 18:52发布

问题:

I have no idea why I have this error. The error I got is

Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: _recordingTap == nil'

UPDATE

Actually, this works, but after several times, suddenly the button is disabled and the mic no longer is in action. Then it causes the error and get crashed.

Could you please help me with this?

class ViewController: UIViewController, SFSpeechRecognizerDelegate, UITextViewDelegate, AVSpeechSynthesizerDelegate {

@IBOutlet weak var myTextView: UITextView!
@IBOutlet weak var microphoneButton: UIButton!

private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US"))!
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
private let audioEngine = AVAudioEngine()

var rightButton = UIBarButtonItem()

override func viewDidLoad() {
    super.viewDidLoad()

    microphoneButton.isEnabled = false
    speechRecognizer.delegate = self
    speechRecognizerFunc()

    myTextView.delegate = self
    myTextView.isUserInteractionEnabled = false

}



@IBAction func cancelButton(_ sender: UIBarButtonItem) {
    self.dismiss(animated: true, completion: nil)
}


func speechRecognizerFunc(){
    SFSpeechRecognizer.requestAuthorization {
        (authStatus) in
        var isButtonEnabled = false

        switch authStatus {

        case .authorized:
            isButtonEnabled = true

        case .denied:
            isButtonEnabled = false
            print("User denied access to speech recognition")

        case .restricted:
            isButtonEnabled = false
            print("Speech recognition restricted on this device")

        case .notDetermined:
            isButtonEnabled = false
            print("Speech recognition not yet authorized")
        }

        OperationQueue.main.addOperation() {
            self.microphoneButton.isEnabled = isButtonEnabled
        }
    }
}

@IBAction func microphoneTapped(_ sender: AnyObject) {


    if audioEngine.isRunning {

        audioEngine.stop()
        recognitionRequest?.endAudio()
        microphoneButton.isEnabled = false
        microphoneButton.setTitle("Start", for: .normal)
        myTextView.text = ""

    } else {

        myTextView.text = "Say something. I'm listening...."
        startRecording()
        microphoneButton.setTitle("Stop", for: .normal)
    }
}

func startRecording() {

    if recognitionTask != nil {
        recognitionTask?.cancel()
        recognitionTask = nil
    }

    let audioSession = AVAudioSession.sharedInstance()

    do {

        try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, with: .defaultToSpeaker)
        try audioSession.setMode(AVAudioSessionModeMeasurement)

        try audioSession.setActive(true, with: .notifyOthersOnDeactivation)

    } catch {

        print("audioSession properties weren't set because of an error.")

    }

    recognitionRequest = SFSpeechAudioBufferRecognitionRequest()

    guard let inputNode = audioEngine.inputNode else {
        fatalError("Audio engine has no input node")
    }

    guard let recognitionRequest = recognitionRequest else {
        fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
    }

    recognitionRequest.shouldReportPartialResults = true

    recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest, resultHandler: {

        (result, error) in

        var isFinal = false

        if result != nil {

            self.myTextView.text = result?.bestTranscription.formattedString
            isFinal = (result?.isFinal)!

        }

        if error != nil || isFinal {

            self.audioEngine.stop()
            inputNode.removeTap(onBus: 0)

            self.recognitionRequest = nil
            self.recognitionTask = nil

            self.microphoneButton.isEnabled = true
            self.performSegue(withIdentifier: "nv", sender: nil)
        }
    })

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

    audioEngine.prepare()

    do {
        try audioEngine.start()

    } catch {
        print("audioEngine couldn't start because of an error.")
    }
}

func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
    if available {
        microphoneButton.isEnabled = true
    } else {
        microphoneButton.isEnabled = false
    }
}

func popUpAlert(title: String, msg: String){
    let alert = UIAlertController(title: title, message: msg, preferredStyle: .alert)
    let okay = UIAlertAction(title: "Okay", style: .default, handler: nil)
    alert.addAction(okay)
    self.present(alert, animated: true, completion: nil)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "nv" {
        let vc = segue.destination as! SpeechTableViewController
        vc.text = self.myTextView.text
        print("ViewControoler: text value: \(textValue)")

    }

}

}

回答1:

You can click start record button fast to repeat this crash, the reason is if you stop audioEngine, audioEngine.inputNode still there. You need add this to stop record.

    audioEngine.stop()
    recognitionRequest?.endAudio()
    audioEngine.inputNode?.removeTap(onBus: 0)