我找到了答案。对于序列中的每个轨道,需要一个单独的 AUSampler。
编辑:2016 年 10 月 1 日。对于冗长的回答,我深表歉意。
有我的播放 MIDI 文件的代码。
- (void)loadMidi:(NSString*)midiFilePath andSoundBank:(NSString*)soundBankFilePath {
[self setupStereoStreamFormat];
[self createGraph];
Check(AUGraphInitialize (graph));
Check(AUGraphStart (graph));
// get URL midi file
if (!midiFilePath) {
midiFilePath = [[NSBundle mainBundle] pathForResource:@"Ресницы" ofType:@"kar"];
}
NSLog(@"midiFilePath %@", midiFilePath);
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath];
// get URL SFbank
if (!soundBankFilePath) {
soundBankFilePath = [[NSBundle mainBundle] pathForResource:@"SGM-V2.01-1" ofType:@"sf2"];
}
NSLog(@"soundBankFilePath %@", soundBankFilePath);
bankUrl = [NSURL fileURLWithPath:soundBankFilePath];
// create sequence from midi
sequence = 0;
Check(NewMusicSequence(&sequence));
Check(MusicSequenceFileLoad (sequence, (__bridge CFURLRef)(midiFileURL), 0, 0));
MidiParser *parser = [[MidiParser alloc] init];
[parser parseData:[NSData dataWithContentsOfFile:midiFilePath]];
NSLog(@"PARSE MIDI %@", [parser log]);
metaLyrics = [[NSMutableArray alloc] initWithArray:[parser syllableArray]];
// do not delete set sequense to graph
Check(MusicSequenceSetAUGraph(sequence, graph));
[self getMidiNodesArray];
// read each track and set instruments & effects & volume for AUSamplers
[self parseSequence];
CAShow(sequence);
MusicTrack tempoTrack;
MusicSequenceGetTempoTrack(sequence, &tempoTrack);
NSDictionary *infoDict = (__bridge NSDictionary *)(MusicSequenceGetInfoDictionary(sequence));
float tempo = [[infoDict valueForKey:@"tempo"] floatValue];
CAShow(tempoTrack);
NSLog(@"Tempo in sequence %f", tempo);
// Load the sequence into the music player
Check(NewMusicPlayer (&player));
// setup speed player
Check(MusicPlayerSetPlayRateScalar(player, 1));
Check(MusicPlayerSetSequence(player, sequence));
Check(MusicPlayerSetTime(player, 0));
MusicPlayerPreroll(player);
}
所以解析midi轨道代码:
- (void) parseSequence {
// get numbers of tracks
UInt32 numTracks;
Check(MusicSequenceGetTrackCount(sequence, &numTracks));
NSLog(@"Number of tarcks %d", (unsigned int)numTracks);
// mute some tracks if needed
NSSet *mutedTracks = [NSSet setWithObjects: @"11", nil];
// mute unused channels
NSLog(@"LOADING TRACKS");
for (UInt32 i = 0; i < numTracks; ++i) {
MusicTrack track;
MusicTimeStamp trackLength;
UInt32 propsize = sizeof(MusicTimeStamp);
Check(MusicSequenceGetIndTrack(sequence, i, &track));
Check(MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength,
&trackLength, &propsize));
// log track info if needed
CAShow(track);
MusicEventIterator myIterator;
MusicTimeStamp timeStamp;
MusicEventType eventType;
const void *refData = 0;
UInt32 dataSize;
Check(NewMusicEventIterator(track, &myIterator));
Boolean hasCurrentEvent;
Check(MusicEventIteratorHasCurrentEvent (myIterator, &hasCurrentEvent));
NSMutableSet *instrumentsSet = [[NSMutableSet alloc] init];
int noActions = 0;
while (hasCurrentEvent) {
MusicEventIteratorGetEventInfo(myIterator, &timeStamp, &eventType, &refData, &dataSize);
if (eventType == 7) {
NSData *dataChaunk = [[NSData alloc] initWithBytes:refData length:dataSize];
void *channelByte_0 = 0;
void *channelByte_1 = 0;
void *channelByte_2 = 0;
void *channelByte_3 = 0;
[dataChaunk getBytes:&channelByte_0 range:NSMakeRange(0, 1)];
[dataChaunk getBytes:&channelByte_1 range:NSMakeRange(1, 1)];
[dataChaunk getBytes:&channelByte_2 range:NSMakeRange(2, 1)];
[dataChaunk getBytes:&channelByte_3 range:NSMakeRange(3, 1)];
Byte command = (int)channelByte_0;
if (command < 208 && command >= 192) {
// setup track on AUsampler
if (![instrumentsSet containsObject:[NSString stringWithFormat:@"%d",(command & 0xf)]]) {
[self setDestNode:(command & 0xf) forTrack:track];
[self setInstrumentMSB:(int)channelByte_1 presetLSB:0 trackID:(command & 0xf)];
}
[instrumentsSet addObject:[NSString stringWithFormat:@"%d",(command & 0xf)]];
} else if ( command <192 && command >= 176){
switch ((NSInteger)channelByte_1) {
case 0: // bank select MSB
NSLog(@"CHANNEL %d CONTROLLER 0xB bankMSB value %d", command & 0xf, (int)channelByte_2);
break;
case 7: // chanell volume
[self setVolume:(int)channelByte_2 inChannel:(command & 0xf)];
break;
case 10: // pan
[self setPan:(int)channelByte_2 inChannel:(command & 0xf)];
break;
case 32: // bank select LSB
NSLog(@"CHANNEL %d CONTROLLER 0xB bankLSB value %d", command & 0xf, (int)channelByte_2);
break;
case 94:
NSLog(@"CHANNEL %d CONTROLLER 0xB setEffect value %d", command & 0xf, (int)channelByte_2);
break;
default:
break;
}
} else
noActions++;
}
// do work here
MusicEventIteratorNextEvent (myIterator);
MusicEventIteratorHasCurrentEvent (myIterator, &hasCurrentEvent);
}
NSLog(@"No actions count %d", noActions);
if ([mutedTracks count] > 0 && [mutedTracks containsObject:[NSString stringWithFormat:@"%d", (unsigned int)i]])
{
Boolean mute = true;
Check(MusicTrackSetProperty(track, kSequenceTrackProperty_MuteStatus, &mute, sizeof(mute)));
printf ("played tracks %u\n", (unsigned int)i);
}
instrumentsSet = nil;
}
UInt32 nodeInd = [[midiNodesArray objectAtIndex:9] intValue];
NSLog(@"setPercussionBankMSB %d for %d track %d node", 1, 9, (unsigned int)nodeInd);
AUNode node;
AudioUnit unit;
Check(AUGraphGetIndNode(graph, nodeInd, &node));
Check(AUGraphNodeInfo(graph, node, 0, &unit));
AUSamplerInstrumentData bpdata;
bpdata.fileURL = (__bridge CFURLRef) bankUrl;
bpdata.bankMSB = kAUSampler_DefaultPercussionBankMSB;
bpdata.bankLSB = kAUSampler_DefaultBankLSB;
bpdata.instrumentType = kInstrumentType_SF2Preset;
bpdata.presetID = (UInt8) 1;
Check(AudioUnitSetProperty (unit,
kAUSamplerProperty_LoadInstrument,
kAudioUnitScope_Global, 0,
&bpdata, sizeof(bpdata)));
[self setVolume:20 inChannel:0];
}
从图表中获取 MIDI 曲目:
- (void) getMidiNodesArray {
UInt32 nodeCount;
Check(AUGraphGetNodeCount (graph, &nodeCount));
AudioUnit outSynth;
if (!midiNodesArray) {
midiNodesArray = [[NSMutableArray alloc] init];
}
for (UInt32 i = 0; i < nodeCount; ++i)
{
AUNode node;
Check(AUGraphGetIndNode(graph, i, &node));
AudioComponentDescription desc;
Check(AUGraphNodeInfo(graph, node, &desc, 0));
if (desc.componentSubType == kAudioUnitSubType_Sampler) {
Check(AUGraphNodeInfo(graph, node, 0, &outSynth));
[midiNodesArray addObject:[NSString stringWithFormat:@"%d", (unsigned int)i]];
}
}
}