简体   繁体   English

swift AVAudioEngine 故障

[英]swift AVAudioEngine Glitches

I am using AVAudioCV to record sound and analyze it using Apple's built-in dictation.我正在使用 AVAudioCV 录制声音并使用 Apple 的内置听写对其进行分析。 However, when I try to turn the audio engine on with a button, the audio buffers complete instantly and do not pick up any sound.但是,当我尝试使用按钮打开音频引擎时,音频缓冲区会立即完成并且不会接收到任何声音。 However, the buffers only begin glitching when I turn the button on for the second time, which leads me to believe that some of the taps aren't being removed from the audio engine when I stop recording, and the taps collide with each other, ending the buffers immediately.但是,缓冲区仅在我第二次打开按钮时才开始出现故障,这使我相信当我停止录制时,某些水龙头并未从音频引擎中移除,并且水龙头相互碰撞,立即结束缓冲区。

I've searched through a number of posts on glitches in AVAudioCV due to unremoved taps but nothing I've seen has worked so far.由于未移除的水龙头,我已经搜索了一些关于 AVAudioCV 故障的帖子,但到目前为止我没有看到任何工作。 Here is what I have:这是我所拥有的:

 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
             
             //get info from microphone
             let audioSession = AVAudioSession.sharedInstance()
             try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
             try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
             
             let inputNode = audioEngine.inputNode
             
             //audio buffer; takes a continuous input of audio and recognizes speech
             recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
             //allows device to print results of your speech before you're done talking
             recognitionRequest?.shouldReportPartialResults = true
             
             
             recognitionTask = speechRecognizer!.recognitionTask(with: recognitionRequest!) {result, error in
                 
                 var isFinal = false
                 
                 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{
             if(!isRecording){
                audioEngine.mainMixerNode.removeTap(onBus: 0)
                audioEngine.stop()
                 return
             }
             try startRecording()
         }
         catch{
             print(error)
         }
         
         
     }

Above is my recognizeAudioStream() function.以上是我的recognizeAudioStream() function。 it begins by initializing a SFSpeechRecognizer() and AVAudioEngine(), and then calls startRecording().它首先初始化 SFSpeechRecognizer() 和 AVAudioEngine(),然后调用 startRecording()。 The previous audio task is cancelled, and the audioEngine of type AVAudioEngine() is set as the inputNode, on which the tap is installed.取消之前的音频任务,将AVAudioEngine()类型的audioEngine设置为inputNode,在其上安装tap。 The variable isFinal stores the data member isFinal of variable result, which is set in the recognitionTask() closure above it.变量 isFinal 存储变量 result 的数据成员 isFinal,它在上面的 recognitionTask() 闭包中设置。 isFinal is set to True when the 1024 byte filter fills up and the taps on the inputNode are removed.当 1024 字节过滤器填满并移除 inputNode 上的抽头时,isFinal 设置为 True。 The recognitionTask is reset and the text from the buffer is analyzed and loaded into my words dictionary.重新设置识别任务,分析缓冲区中的文本并将其加载到我的单词词典中。 Beneath this block is the setup of the buffer and audioEngine.此块下方是缓冲区和音频引擎的设置。

Finally, a do/catch block checks to see if the audioEngine should be recording (isRecording = false if it shouldn't), in which case the tap is removed and audioEngine is stopped.最后,do/catch 块检查audioEngine 是否应该录制(如果不应该录制,则isRecording = false),在这种情况下,点击被移除并且audioEngine 被停止。 Otherwise, startRecording() is called again to fill another buffer.否则,再次调用 startRecording() 来填充另一个缓冲区。 I have no problems with this until the record button is turned on for the second time.在第二次打开录制按钮之前,我对此没有任何问题。

Here is my record button's objc function:这是我的录制按钮的 objc function:

    @objc func recordingState(){
        if isRecording{
            recordButton.setImage(UIImage(named: "unfilled_star.png"), for: .normal)
            isRecording = false
        }
        
        else{
            print("try to start recording")
            recordButton.setImage(UIImage(named: "filled_star.png"), for: .normal)
 
            let speechRecognizer = SFSpeechRecognizer()
            requestDictAccess();
            
            if speechRecognizer!.isAvailable { //if the user has granted permission
                speechRecognizer?.supportsOnDeviceRecognition = true //for offline data
                isRecording = true
                
                recognizeAudioStream()
            }
        }
    }

When running recognizeAudioStream() for the second time, I get this output, the entire blockin roughly a second:第二次运行识别音频流()时,我得到了这个 output,整个块大约一秒钟:

authorized
2021-04-22 22:38:29.671122-0400 DictoCounter[76126:11539342] [aurioc] 323: Unable to join I/O thread to workgroup ((null)): 2
2021-04-22 22:38:29.679718-0400 DictoCounter[76126:11539130] [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=209 "(null)"
completed buffer
2021-04-22 22:38:29.688449-0400 DictoCounter[76126:11538858] Words successfully saved.
2021-04-22 22:38:29.690209-0400 DictoCounter[76126:11539349] [aurioc] 323: Unable to join I/O thread to workgroup ((null)): 2
2021-04-22 22:38:29.699224-0400 DictoCounter[76126:11539130] [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=209 "(null)"
completed buffer
2021-04-22 22:38:29.719147-0400 DictoCounter[76126:11538858] Words successfully saved.
2021-04-22 22:38:29.720698-0400 DictoCounter[76126:11539352] [aurioc] 323: Unable to join I/O thread to workgroup ((null)): 2
2021-04-22 22:38:29.734173-0400 DictoCounter[76126:11539102] [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=209 "(null)"
completed buffer
2021-04-22 22:38:29.740684-0400 DictoCounter[76126:11538858] Words successfully saved.
2021-04-22 22:38:29.741952-0400 DictoCounter[76126:11539370] [aurioc] 323: Unable to join I/O thread to workgroup ((null)): 2
2021-04-22 22:38:29.754000-0400 DictoCounter[76126:11539102] [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=209 "(null)"
completed buffer
2021-04-22 22:38:29.761909-0400 DictoCounter[76126:11538858] Words successfully saved.
2021-04-22 22:38:29.763036-0400 DictoCounter[76126:11539371] [aurioc] 323: Unable to join I/O thread to workgroup ((null)): 2
2021-04-22 22:38:29.776616-0400 DictoCounter[76126:11539104] [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=209 "(null)"
completed buffer

I'm not too sure what to do here.我不太确定在这里做什么。 I've looked through many other posts which suggest that the taps are not being removed, but I removed all taps in the do/catch block.我查看了许多其他帖子,这些帖子表明没有删除水龙头,但我删除了 do/catch 块中的所有水龙头。 I'm not sure what I'm mussing.我不确定我在搞什么。 Appreciatative of any and all help- thank you in advance!感谢任何和所有帮助 - 提前感谢您!

A possible problem is route/configuration change or interruption, you can check if any of these notifications gets called when you end the recording可能的问题是路由/配置更改或中断,您可以检查结束录制时是否调用了这些通知中的任何一个

private var notificationTokens = [NSObjectProtocol]()

// init
notificationTokens = [
    NotificationCenter.default.addObserver(
        forName: AVAudioSession.routeChangeNotification,
        object: nil,
        queue: .main
    ) { notification in
        self.engineStartTimer?.invalidate()
        guard self.resumePlaybackAfterSeek == nil else { return }
        self.scheduledBufferFrames = 0
        self.ignoreBufferIndex = self.lastBufferIndex
        self.resumePlaybackAfterSeek = false
        startRestartEngineTimer()
    },
    NotificationCenter.default.addObserver(
        forName: AVAudioSession.interruptionNotification,
        object: nil,
        queue: .main
    ) { notification in
        startRestartEngineTimer()
    },
    NotificationCenter.default.addObserver(
        forName: .AVAudioEngineConfigurationChange,
        object: nil,
        queue: .main
    ) { notification in
        startRestartEngineTimer()
    }
]

If this won't help, please provide a MRE如果这没有帮助,请提供MRE

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM