Search code examples
swiftmacosmidicoremidiavkit

How to keep AVMIDIPlayer playing?


I'm trying to use Apple's AVMIDIPlayer object for playing a MIDI file. It seems easy enough in Swift, using the following code:

let midiFile:NSURL = NSURL(fileURLWithPath:"/path/to/midifile.mid")
var midiPlayer: AVMIDIPlayer?

do {
    try midiPlayer = AVMIDIPlayer(contentsOf: midiFile as URL, soundBankURL: nil)
    midiPlayer?.prepareToPlay()
} catch {
    print("could not create MIDI player")
}

midiPlayer?.play {
    print("finished playing")
}

And it plays for about 0.05 seconds. I presume I need to frame it in some kind of loop. I've tried a simple solution:

while stillGoing {
midiPlayer?.play {
    let stillGoing = false
}
}

which works, but ramps up the CPU massively. Is there a better way? Further to the first comment, I've tried making a class, and while it doesn't flag any errors, it doesn't work either.

class midiPlayer {

var player: AVMIDIPlayer?

    func play(file: String) {
        let myURL = URL(string: file)
do {
    try self.player = AVMIDIPlayer.init(contentsOf: myURL!, soundBankURL: nil)
    self.player?.prepareToPlay()
} catch {
    print("could not create MIDI player")
}
    self.player?.play()
    }
func stop() {
self.player?.stop()
}
}

// main

let myPlayer = midiPlayer()
let midiFile = "/path/to/midifile.mid"
myPlayer.play(file: midiFile)

Solution

  • You were close with your loop. You just need to give the CPU time to go off and do other things instead of constantly checking to see if midiPlayer is finished yet. Add a call to usleep() in your loop. This one checks every tenth of a second:

    let midiFile:NSURL = NSURL(fileURLWithPath:"/Users/steve/Desktop/Untitled.mid")
    var midiPlayer: AVMIDIPlayer?
    
    do {
        try midiPlayer = AVMIDIPlayer(contentsOfURL: midiFile, soundBankURL: nil)
        midiPlayer?.prepareToPlay()
    } catch {
        print("could not create MIDI player")
    }
    
    var stillGoing = true
    while stillGoing {
        midiPlayer?.play {
            print("finished playing")
            stillGoing = false
        }
        usleep(100000)
    }