Pulling Data From Midi File in Swift, Objective-C and CoreAudio

Pulling Data From Midi File in Swift, Objective-C and CoreAudio

I needed to play mixed mp3's, wav's and midi files while simultaneously displaying the associated, synchronized sheet music. I was given the midi file and all other meta data that was entered at the time the music was recorded. In iOS, it is not as easy to do as you would think. The sheet music data was found in Meta Events in the Midi file http://www.somascape.org/midi/tech/mfile.html#meta.

Problem 1: The first hurdle was that the eventData is cast to MIDIMetaEvent struct with a single item array that points to the beginning of the string, but doesn't return the whole string, and dataLength tells you how long the string is. So, how do we get at that data? Well we need to write a little bit more C. data[1] holds the start of the char array, so we need

typedef struct MIDIMetaEvent
{
  UInt8 metaEventType;
  UInt8 unused1;
  UInt8 unused2;
  UInt8 unused3;
  UInt32 dataLength;
  UInt8 data[1];
} MIDIMetaEvent;

kMusicEventType_Meta

MusicEventType eventType;
MusicTimeStamp eventTimeStamp;
UInt32 eventDataSize;
const void *eventData;

MusicEventIteratorGetEventInfo(iterator, &eventTimeStamp, &eventType, &eventData, &eventDataSize);
MIDIMetaEvent* metaEvent = (MIDIMetaEvent*)eventData;
char tempBuffer[1000];
strncpy(tempBuffer, (char *)metaEvent->data, (int)metaEvent->dataLength);
tempBuffer[metaEvent->dataLength] = '\0';
NSString *str = [NSString stringWithFormat:@"%s", tempBuffer];

Problem 2: I didn't want to start my own timer to keep track of when to display the content from the midi file. So there is another event in MIDI file that can call back to play now I used this call back to display a visual metronome, so it was already present. http://www.somascape.org/midi/tech/mfile.html#events

So I used events to store the index of my array of marker data and when the Midi player calls the event it looks up the string in the array with the index. I did this code in Swift. And and a bridge header in the other to call the MetaEvents.

func setCallback(markerHandler: @escaping ACCallback) {
  let sequencerCallback: MusicSequenceUserCallback = {
  (clientData:UnsafeMutableRawPointer?,
  sequence:MusicSequence,
  track:MusicTrack,
  eventTime:MusicTimeStamp,
  eventData:UnsafePointer<MusicEventUserData>,
  startSliceBeat:MusicTimeStamp,
  endSliceBeat:MusicTimeStamp)
  -> Void in

  let userData = eventData.pointee
  let marker = AXSequencer.markers![Int(userData.length)] as! MarkerEvent
  ACSequencer.markerHandler?(marker.data)
  }
  MusicSequenceSetUserCallback(self.sequence!, sequencerCallback, nil)
}

SHARE


comments powered by Disqus

Follow Us

Latest Posts

subscribe to our newsletter