我正在开发一个使用音频单元播放midi序列(.mid)的应用程序。 midi文件是使用Logic创建的,可以在时间轴上添加标记。
在代码中,我使用MusicSequence MusicPlayer读取文件,使用MIDIClientCreate MIDIDestinationCreate来解析MIDI数据包。
主要方法
OSStatus result = noErr;
// Initialise the music sequence
NewMusicSequence(&_s);
// Get a string to the path of the MIDI file which
// should be located in the Resources folder
NSString *midiFilePath = [[NSBundle mainBundle]
pathForResource:@"mymidifile"
ofType:@"mid"];
// Create a new URL which points to the MIDI file
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath];
// Load the file
MusicSequenceFileLoad(_s, (__bridge CFURLRef) midiFileURL, 0, 0);
// Initialise the music player
NewMusicPlayer(&_p);
// Load the sound from EXS file
[self loadFromEXS:@"Grand Piano" withSampler:_samplerUnit];
//Load Click
[self loadFromSoundFont:@"hit set" withSampler:_samplerUnit2];
//Assign channel to tracks
MusicTrack track = NULL;
MusicTrack track2 = NULL;
MusicSequenceGetIndTrack(_s, 1, &track);
MusicSequenceGetIndTrack(_s, 2, &track2);
//Assign tracks to audio units
MusicTrackSetDestNode(track, _samplerNode);
MusicTrackSetDestNode(track2, _samplerNode2);
// Create a client
result = MIDIClientCreate(CFSTR("Virtual Client"),MyMIDINotifyProc,(__bridge void *)(self),&_virtualMidi);
NSAssert( result == noErr, @"MIDIClientCreate failed. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Create an endpoint
result = MIDIDestinationCreate(_virtualMidi, (CFStringRef)@"Virtual Destination", MyMIDIReadProc, (__bridge void *)(self), &_virtualEndPoint);
NSAssert( result == noErr, @"MIDIDestinationCreate failed. Error code: %d '%.4s'", (int) result, (const char *)&result);
// ************* Set the endpoint of the sequence to be our virtual endpoint
MusicSequenceSetMIDIEndpoint(_s, _virtualEndPoint);
// Load the sequence into the music player
MusicPlayerSetSequence(_p, _s);
// Called to do some MusicPlayer setup. This just
// reduces latency when MusicPlayerStart is called
MusicPlayerPreroll(_p);
// Starts the music playing
MusicPlayerStart(_p);
我的readProc功能
void MyMIDIReadProc(const MIDIPacketList *pktlist,
AudioProcessor *refCon,
void *connRefCon) {
AudioUnit *player = nil;
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
NSString *messageType;
for (int i=0; i < pktlist->numPackets; i++) {
Byte midiStatus = packet->data[0];
Byte midiCommand = midiStatus >> 4;// mask off all but top 4 bits
Byte note = packet->data[1] & 0x7F;
Byte velocity = packet->data[2] & 0x7F;
// find the channel by masking off all but the low 4 bits
NSInteger midiChannel = midiStatus & 0x0F;
switch (midiStatus & 0xF0) {
case 0x80:
messageType = @"Note Off";
break;
case 0x90:
messageType = @"Note On";
break;
case 0xA0:
messageType = @"Aftertouch";
break;
case 0xB0:
messageType = @"Control change";
break;
case 0xC0:
messageType = @"Program Change";
break;
case 0xD0:
messageType = @"Channel Pressure";
break;
case 0xE0:
messageType = @"Pitch Wheel";
break;
default:
messageType = @"Unk";
break;
}
NSLog(@"%@",messageType);
int noteNumber = ((int) note) % 12;
NSString *noteType;
switch (noteNumber) {
case 0:
noteType = @"C";
break;
case 1:
noteType = @"C#/Db";
break;
case 2:
noteType = @"D";
break;
case 3:
noteType = @"D#/Eb";
break;
case 4:
noteType = @"E";
break;
case 5:
noteType = @"F";
break;
case 6:
noteType = @"F#/Gb";
break;
case 7:
noteType = @"G";
break;
case 8:
noteType = @"G#/Ab";
break;
case 9:
noteType = @"A";
break;
case 10:
noteType = @"A#/Bb";
break;
case 11:
noteType = @"B";
break;
default:
break;
}
if( velocity == 0 ){
UInt32 noteOff = kMIDIMessage_NoteOff << 4 | 0;
if( midiChannel == 0 ){
MusicDeviceMIDIEvent (refCon.samplerUnit, noteOff, note, 0, 0);
}else if( midiChannel == 1 ){
MusicDeviceMIDIEvent (refCon.samplerUnit2, noteOff, note, 0, 0);
}
}else{
if( midiChannel == 0 ){
MusicDeviceMIDIEvent (refCon.samplerUnit, midiStatus, note, velocity, 0);
}else if( midiChannel == 1 ){
MusicDeviceMIDIEvent (refCon.samplerUnit2, midiStatus, note, velocity, 0);
}
}
packet = MIDIPacketNext(packet);
}
}
使用我的readProc功能,我可以看到所有midi消息,但不能看到标记......
如果我在Logic中重新打开midi文件,标记就在文件中,但是哪里......我怎样才能在代码中找到它?
答案 0 :(得分:1)
标记作为Marker Meta Events包含在MIDI文件中。因此,您需要扩展解析以支持元事件。元事件的状态为0xFF。对于标记,下一个字节是0x06,然后是长度和字符数。
答案 1 :(得分:0)
您可以使用MusicSequenceSetUserCallback&#34;音乐序列会为添加到序列所拥有的任何音乐曲目的每个用户事件调用您的回调。&#34;如果Logic实际上将标记导出为用户事件,则说:
// in your principal method after loading the sequence into the
result = MusicSequenceSetUserCallback(sequence, sequenceUserCallback, (__bridge void *)self);
然后将此方法添加到您的文件中:
void sequenceUserCallback (
void *inClientData,
MusicSequence inSequence,
MusicTrack inTrack,
MusicTimeStamp inEventTime,
const MusicEventUserData *inEventData,
MusicTimeStamp inStartSliceBeat,
MusicTimeStamp inEndSliceBeat
)
{
// cast <yourclass>* selfPlayer = (__bridge <yourclass> *)inClientData;
NSLog(@"track received marker");
};
如果Logic没有将标记导出为用户事件,您可以添加自己的用户事件,如下所示:
// after loading the sequence and getting the track from sequence
static MusicEventUserData userData = {1, 0x01 };
result = MusicTrackNewUserEvent(track, sequenceLength /* timestamp where to invoke the callback*/ , &userData);