swiftavaudioplayercore-audio

AVMIDIPlayer init with MusicSequence


AVMIDIPlayer has this initializer:

initWithData:soundBankURL:error:

This is in addition to an initializer for reading from a standard MIDI file.

The data is NSData, which I assumed is in a standard MIDI file format. So, how to get that data? Well, the current way to create a MIDI sequence is the AudioToolbox MusicSequence. (AVAudioEngine even has a member of this type).

MusicSequence has this converter:

MusicSequenceFileCreateData (
        MusicSequence            inSequence,
        MusicSequenceFileTypeID  inFileType,
        MusicSequenceFileFlags   inFlags,
        SInt16                   inResolution,
        CFDataRef                *outData
    );

So let's try that.

var status = OSStatus(noErr)
var data:Unmanaged<CFData>?
status = MusicSequenceFileCreateData (musicSequence,
        MusicSequenceFileTypeID(kMusicSequenceFile_MIDIType),
        MusicSequenceFileFlags(kMusicSequenceFileFlags_EraseFile),
        480, &data)

var ns:NSData = data!.takeRetainedValue()
var error:NSError?
self.mp = AVMIDIPlayer(data: ns, soundBankURL: soundbank, error: &error)

(I've loaded the soundbank previously). Good news: That does play the MIDI data. Bad news: Upon reaching the end of the sequence, it crashes.

queue = 'CallbackQueue', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)

Two guesses. There is a bug in the platform code, or (more likely) my code.

tl;dr Am I getting the data incorrectly?


Solution

  • I received the Tumbleweed badge for this!

    Anyway, the problem is the AVMIDIPlayer's play function. If you give it a nil completion handler, it is happy and it won't crash. If you do give it a completion handler, then blammo - crash. I've tried both a closure and a garden variety function for the completion handler.

    var completion:AVMIDIPlayerCompletionHandler = {
         println("done")
     }
    

    tl;dr

    Do this:

    midiPlayer.play(nil)
    

    and not this:

    midiPlayer.play(completion)