AvAudioEngine not restarted after interruption

68 views Asked by At

I am getting audio from mic. with using AvAudioEngine but when coming call and makes interruption, I want to stop and then restart getting audio process but When I try handleInterruption(I shared below) method I am getting this error. how can I stop and restart AvAudioEngine after interruption

AURemoteIO.cpp:1702 AUIOClient_StartIO failed (561145187) AVAEInternal.h:109 [AVAudioEngineGraph.mm:1545:Start: (err = PerformCommand(*ioNode, kAUStartIO, NULL, 0)): error 561145187 Can't start the engine: Error Domain=com.apple.coreaudio.avfaudio Code=561145187 "(null)" UserInfo={failed call=err = PerformCommand(*ioNode, kAUStartIO, NULL, 0)}

@objc func handleInterruption(notification: Notification) {
    
    guard let userInfo = notification.userInfo,
        let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
        let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
            return
    }
    
    // Switch over the interruption type.
    switch type {
    case .began:
        print("interrupt begin")
        socket.close()
        socket=nil
        self.socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)
        audioEngine.stop()
        print("Audio player stopped")
   
    case .ended:
        print("interrupt end")
        
        self.audioEngine = AVAudioEngine()
        self.audioPlayer = AVAudioPlayerNode()
        self.mixer = AVAudioMixerNode()

        do {
            try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .allowBluetoothA2DP])
            
            print("Audio session category set to playback")
        } catch {
            print("Setting category to AVAudioSessionCategoryPlayback failed: \(error)")
        }

        self.mixer = AVAudioMixerNode()
        self.mixer.volume = 0
        self.audioEngine.attach(audioPlayer)
        self.audioEngine.attach(mixer)
        
        try! self.audioEngine.inputNode.setVoiceProcessingEnabled(true)
        try! AVAudioSession.sharedInstance().setActive(true)

        
        DispatchQueue.global(qos: .background).async { [weak self] in
            guard let self = self else { return }
            do {
                self.socket.setIPv4Enabled(true)
                self.socket.setIPv6Enabled(false)
                try self.socket.connect(toHost:"239.10.10.100" ?? "", onPort: 4545 ?? 0)
                try self.socket.beginReceiving()
                print("Socket started")
            } catch {
                print("Socket Started Error: \(error)")
            }
        }
        
        
        audioEngine.inputNode.installTap(onBus: 0, bufferSize: 1024, format: audioEngine.inputNode.inputFormat(forBus: 0)) {
            (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
            do {
                let inputBlock: AVAudioConverterInputBlock = { _, outStatus in
                    outStatus.pointee = AVAudioConverterInputStatus.haveData
                    return buffer
                }
                let frameCapacity =
                AVAudioFrameCount(self.outputFormat.sampleRate) * buffer.frameLength
                / AVAudioFrameCount(buffer.format.sampleRate)
                let outputBuffer = AVAudioPCMBuffer(
                    pcmFormat: self.outputFormat,
                    frameCapacity: frameCapacity
                )!
                var error: NSError?
                self.converter.convert(to: outputBuffer, error: &error, withInputFrom: inputBlock)
                
                //let data = Data(bytes: (outputBuffer.int16ChannelData![0]), count: Int(outputBuffer.frameLength))
                let data = Data(buffer: UnsafeBufferPointer(start: outputBuffer.int16ChannelData![0], count: Int(outputBuffer.frameLength)))
                
                print(data)
                
                DispatchQueue.global(qos: .background).async { [weak self] in
                    guard let self = self else { return }
                    do {
                        self.socket.send(data, withTimeout: 0, tag: 0)
                        
                    } catch {
                        print("Socket send Error: \(error)")
                    }
                }
                
                
            } catch {
                print(error)
            }
        }
        
        audioEngine.prepare()
        do {
            try audioEngine.start()
            print("Audio player started")
        } catch {
            print("Can't start the engine: \(error)")
        }

        
    default:
        print("Default print")
        break
    }
}
1

There are 1 answers

0
Gordon Childs On

The error you're seeing (561145187) is AVAudioSessionErrorCodeCannotStartRecording.

These days I think you're only allowed to restart audio IO if your AVAudioSession.InterruptionOptions contain .shouldResume. This doesn't always happen!

If this is a problem, you could try making it less likely that you be interrupted by adding .mixWithOthers to you AVAudioSession category options. You will need .playAndRecord and not .record in this case.