Reputation: 391
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)
Upvotes: 0
Views: 404
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