I know that similar questions already were asked but proposed solutions didn't help me.
In my app I have to recognize user's speech and to synth text-to-speech as well. The problem is, I have random crashes when the app trying to recognize (especially after text-to-speech, but also in other cases: on the second recognition attempt, for example).
I have a special class Speecher with several obvious vars (some private) and two main functions: startRecord
and killRecord
.
func startRecord() {
session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSession.Category.playAndRecord, mode: .spokenAudio, options: [.defaultToSpeaker, .mixWithOthers])
} catch {
print("audio session \(error)")
return
}
audioEngine = AVAudioEngine()
inputNode = audioEngine.inputNode
inputNode.removeTap(onBus: 0)
let recordingFormat = inputNode.inputFormat(forBus: 0)
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
guard let recognitionRequest = recognitionRequest else { fatalError("Unable to create a SFSpeechAudioBufferRecognitionRequest object") }
recognitionRequest.shouldReportPartialResults = true
if #available(iOS 13, *) {
recognitionRequest.requiresOnDeviceRecognition = true
}
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { buffer, _ in
self.recognitionRequest.append(buffer)
}
audioEngine.prepare()
do {
try audioEngine.start()
} catch {
return print("audio engine start error \(error)")
}
guard let myRecognizer = SFSpeechRecognizer() else {
print("recognizer error")
return
}
if !myRecognizer.isAvailable {
print("Recognizer is not available right now error")
return
}
recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { result, error in
if let result = result {
let bestString = result.bestTranscription.formattedString
print(bestString)
self.speecherDelegate?.sendSpeechString(str: bestString)
} else if let error = error {
print("result error \(error)")
}
})
}
The next function is called before I'm trying to use text-to-speech or before I want to cancel/stop recognition session:
func killRecord() {
if recognitionRequest != nil {
recognitionRequest.endAudio()
}
if audioEngine != nil {
audioEngine.stop()
if audioEngine.inputNode != nil {
audioEngine.inputNode.removeTap(onBus: 0)
}
}
if recognitionTask != nil {
recognitionTask?.cancel()
}
if session != nil {
do {
try session.setCategory(AVAudioSession.Category.playback, mode: .spokenAudio, options: [.allowBluetooth, .allowBluetoothA2DP, .defaultToSpeaker, .mixWithOthers])
try session.setActive(false, options: .notifyOthersOnDeactivation)
} catch {
print("switch audiosession error not nil \(error)")
return
}
} else {
session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSession.Category.playback, mode: .spokenAudio, options: [.allowBluetooth, .allowBluetoothA2DP, .defaultToSpeaker, .mixWithOthers])
try session.setActive(false, options: .notifyOthersOnDeactivation)
} catch {
print("switch audiosession error \(error)")
return
}
}
}
What I'm getting a lot is a lldb errors. It looks like that:
The problem string in the code seems to be inputNode = audioEngine.inputNode
but I don't know how to solve it. I've already tried audioEngine.inputNode.removeTap(onBus: 0)
just before inputNode = audioEngine.inputNode
but to no avail.