Search code examples
iosswiftavfoundationavaudioplayernodeavaudiopcmbuffer

Swift: How to stop scheduleBuffer completion handler being called when interrupted?


I have a AVAudioPlayerNode that I'm scheduling a lot of buffers on using the following:

node.scheduleBuffer(buffer, at: nil, options: .interrupts, completionHandler: completeFunc)

But I'm having a bit of a problem. If I play another buffer interrupting the currently playing buffer, the completion handler for the buffer getting interrupted is still being called. I guess this makes sense, but I can't seem to find a way to check if the file actually COMPLETED playing or if it was interrupted. Is there a way I can do this?

Any answers will help!


Solution

  • You will need an interruption state variable that is referenced in the completion handler and toggled by the interrupting code. Here's some shorthand code that makes some assumptions on how you're looping through buffers:

    var node: AVAudioPlayerNode!
    var loopingBuffers: [AVAudioPCMBuffer] = [buffer1, buffer2, buffer3]
    var interruptBuffers = false // state variable to handle the interruption
    
    func scheduleNextBuffer() {
        /* code to find the next buffer in the loopingBuffers queue */
    
        // assuming you have instantiated an AVAudioPlayerNode elsewhere
        node.scheduleBuffer(nextBuffer, completionHandler: bufferCompletion)
    }
    
    func bufferCompletion() {
        // check the state variable to make certain the completionHandler isn't being triggered by a scheduling interruption
        guard !interruptBuffers else { return }
        scheduleNextBuffer()
    }
    
    func interruptBuffers() {
        let newBuffers: [AVAudioPCMBuffer] = [buffer4, buffer5, buffer6]
    
        interruptBuffers = true
    
        node.scheduleBuffer(buffer4, at: nil, options: .interrupts, completionHandler: bufferCompletion)
    
        // cleanup your state and queue up your new buffers
        interruptBuffers = false
        loopingBuffers = newBuffers
    }