iosavfoundationavaudioengineshazam

AVAudioEngine recording microphone input seems to stop upon playing music


I’m recording microphone input to match it to a song in the Shazam catalog. This works, but if I start() the AVAudioEngine then something happens like music starts playing via MPMusicPlayerController.applicationMusicPlayer.play(), it seems the audio engine stops or gets interrupted. The microphone recording shuts off, thus the SHSessionDelegate never finds a match or fails with an error, so my UI is stuck showing it's listening when it’s not anymore. Is there a way to be informed when this happens so that I may update the UI to handle cancelation?

private lazy var shazamAudioEngine = AVAudioEngine()
private lazy var shazamSession: SHSession = {
    let session = SHSession()
    session.delegate = self
    return session
}()

...

try? AVAudioSession.sharedInstance().setCategory(.record)

//Create an audio format for our buffers based on the format of the input, with a single channel (mono)
let audioFormat = AVAudioFormat(standardFormatWithSampleRate: shazamAudioEngine.inputNode.outputFormat(forBus: 0).sampleRate, channels: 1)

//Install a "tap" in the audio engine's input so that we can send buffers from the microphone to the session
shazamAudioEngine.inputNode.installTap(onBus: 0, bufferSize: 2048, format: audioFormat) { [weak self] buffer, when in
    //Whenever a new buffer comes in, we send it over to the session for recognition
    self?.shazamSession.matchStreamingBuffer(buffer, at: when)
}

do {
    try shazamAudioEngine.start()
} catch {
    ...
}

Solution

  • In my testing isRunning tracks this state, so it changes from true to false when you start playing music and the microphone stops being recorded. Unfortunately that property can't be observed with KVO so what I did was set up a repeating Timer to detect if it changes to handle cancelation, making sure to invalidate() the timer when other state changes occur.

    audioEngineRunningTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [weak self] timer in    
        //the audio engine stops running when you start playing music for example, so handle cancelation here
        if self?.shazamAudioEngine.isRunning == false {
            //...
        }
    }