benwiggy
benwiggy

Reputation: 2719

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)

Upvotes: 1

Views: 1046

Answers (3)

benwiggy
benwiggy

Reputation: 2719

Combining Brendan and Steve's answers, the key is sleep or usleep and sticking the play method outside the loop to avoid revving the CPU.

player?.play({return})
while player!.isPlaying { 
sleep(1) // or usleep(10000)
}

The original stillGoing value works, but there is also an isPlaying method.

.play needs something between its brackets to avoid hanging forever after completion.

Many thanks.

Upvotes: 0

SSteve
SSteve

Reputation: 10708

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)
}

Upvotes: 2

Brendan Shanks
Brendan Shanks

Reputation: 3236

You need to ensure that the midiPlayer object exists until it's done playing. If the above code is just in a single function, midiPlayer will be destroyed when the function returns because there are no remaining references to it. Typically you would declare midiPlayer as a property of an object, like a subclassed controller.

Upvotes: 2

Related Questions