I am having some trouble working with the AVAudioEngine and Apple's dictation to record and process external microphone inputs. My code is as follows:
func recognizeAudioStream() {
let speechRecognizer = SFSpeechRecognizer()
//performs speech recognition on live audio; as audio is captured, call append
//to request object, call endAudio() to end speech recognition
var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
//determines & edits state of speech recognition task (end, start, cancel, etc)
var recognitionTask: SFSpeechRecognitionTask?
let audioEngine = AVAudioEngine()
func startRecording() throws{
//cancel previous audio task
recognitionTask?.cancel()
recognitionTask = nil
print("here2")
//get info from microphone
let audioSession = AVAudioSession.sharedInstance()
print("here2-")
try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
print("here2--")
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
print("here2----")
print("here2")
let inputNode = audioEngine.inputNode
print("here3")
//audio buffer; takes a continuous input of audio and recognizes speech
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
print("here4")
//allows device to print results of your speech before you're done talking
recognitionRequest?.shouldReportPartialResults = true
print("here5")
recognitionTask = speechRecognizer!.recognitionTask(with: recognitionRequest!) {result, error in
var isFinal = false
print("here")
if let result = result{ //if we can let result be the nonoptional version of result, then
isFinal = result.isFinal
print("Text: \(result.bestTranscription.formattedString)")
}
if error != nil || result!.isFinal{ //if an error occurs or we're done speaking
audioEngine.stop()
inputNode.removeTap(onBus: 0)
recognitionTask = nil
recognitionRequest = nil
let bufferText = result?.bestTranscription.formattedString.components(separatedBy: (" "))
print("completed buffer")
self.addToDictionary(wordNames: bufferText)
self.populateTempWords()
wordsColl.reloadData()
//
do{
try startRecording()
}
catch{
print(error)
}
}
}
//configure microphone; let the recording format match with that of the bus we are using
let recordingFormat = inputNode.outputFormat(forBus: 0)
//contents of buffer will be dumped into recognitionRequest and into result, where
//it will then be transcribed and printed out
//1024 bytes = dumping "limit": once buffer fills to 1024 bytes, it is appended to recognitionRequest
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
recognitionRequest?.append(buffer)
}
audioEngine.prepare()
try audioEngine.start()
}
do{
try startRecording()
}
catch{
print(error)
}
}
From an entirely inefficient debugging session, I was able to determine that the line let audioSession = AVAudioSession.sharedInstance() produces the first error in the log, while I'm certain the recognitionTask closure produces the second error message:
here2
2021-06-23 22:45:22.707332-0500 DictoCounter[38814:6314709] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID
here2-
here2--
here2----
here2
here3
here4
here5
2021-06-23 22:45:22.953963-0500 DictoCounter[38814:6314846] [aurioc] 323: Unable to join I/O thread to workgroup ((null)): 2
reg
I should also note that I was able to detect and process audio without errors just a few months ago with this same code, on the same Macbook (and same xcworkspace project). This leads me to believe that it is probably not a code issue, but more of a settings issue.
I did a search prior to posting this question: Swift5 Can't record audio on mac os using AVAudioRecorder ( Error : [plugin] AddInstanceForFactory: No factory registered for id <CFUUID ) seems to indicate the problem is with the info.plist file. However, I've updated it to allow both speech recognition and microphone detection.
Please let me know if you need any more information. Looking forward to solving this problem!
