Nikunj5294
Nikunj5294

Reputation: 391

How to get time duration from MusicPlayer ( For Midi file ) in iOS?

I need to time duration and end event of midi file. I am using below code for play midi file. i tried but didn't found anything. thanks in advance

var s: MusicSequence?
NewMusicSequence(&s)

let midiFilePath = Bundle.main.path(forResource: "CCL-20180308-A-04", ofType: "mid")
let midiFileURL = URL(fileURLWithPath: midiFilePath ?? "")

MusicSequenceFileLoad(s!, midiFileURL as CFURL, MusicSequenceFileTypeID(rawValue: 0)!, [])

var p: MusicPlayer?
NewMusicPlayer(&p)

MusicPlayerSetSequence(p!, s)
MusicPlayerPreroll(p!)
MusicPlayerStart(p!)

usleep(3 * 100 * 100)
var now: MusicTimeStamp = 0
MusicPlayerGetTime(p!, &now)

enter image description here

Upvotes: 0

Views: 404

Answers (1)

Andrew Madsen
Andrew Madsen

Reputation: 21373

This will work:

var s: MusicSequence!
NewMusicSequence(&s)

let midiFileURL = Bundle.main.url(forResource: "CCL-20180308-A-04", withExtension: "mid")!

MusicSequenceFileLoad(s!, midiFileURL as CFURL, .midiType, [])

var p: MusicPlayer!
NewMusicPlayer(&p)

MusicPlayerSetSequence(p, s)
MusicPlayerPreroll(p)
MusicPlayerStart(p)

var numTracks: UInt32 = 0
MusicSequenceGetTrackCount(s, &numTracks)
let length = (0..<numTracks).map { (index: UInt32) -> (MusicTimeStamp) in
    var track: MusicTrack?
    MusicSequenceGetIndTrack(s, index, &track)
    var size = UInt32(MemoryLayout<MusicTimeStamp>.size)
    var scratchLength = MusicTimeStamp(0)
    MusicTrackGetProperty(track!, kSequenceTrackProperty_TrackLength, &scratchLength, &size)
    return scratchLength
}.max() ?? 0
var lengthInSeconds = Float64(0)
MusicSequenceGetSecondsForBeats(s, length, &lengthInSeconds)

self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { (t) in
    var now: MusicTimeStamp = 0
    MusicPlayerGetTime(p, &now)
    var nowInSeconds = Float64(0)
    MusicSequenceGetSecondsForBeats(s, now, &nowInSeconds)
    print("\(nowInSeconds) / \(lengthInSeconds)")
})

The important piece you were missing was to get the total sequence length by finding the length of the longest track. You can get the length of a track using MusicTrackGetProperty() for the kSequenceTrackProperty_TrackLength property.

For what it's worth, CoreMIDI is gnarly enough, especially in Swift, that I think it's worth using a higher level API. Check out AVMIDIPlayer, which is part of AVFoundation. If you need something more sophisticated, you might check out MIKMIDI, which is an open source MIDI library that builds on Core MIDI but adds a ton of additional functionality, and is significantly easier to use. (Disclaimer: I'm the original author and maintainer of MIKMIDI.) With MIKMIDI, you'd do this:

let midiFileURL = Bundle.main.url(forResource: "CCL-20180308-A-04", withExtension: "mid")!
let sequence = try! MIKMIDISequence(fileAt: midiFileURL)
let sequencer = MIKMIDISequencer(sequence: sequence)
sequencer.startPlayback()

self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { (t) in
    let now = sequencer.timeInSeconds(forMusicTimeStamp: sequencer.currentTimeStamp, options: [])
    let length = sequence.durationInSeconds
    print("\(now) / \(length)")
})

Just a little bit simpler! Things get even more interesting if you're trying to do recording, more complex synthesis, routing MIDI to/from external devices, etc.

Upvotes: 1

Related Questions