我正在尝试编写一个简单的 MIDI 播放器,使用高质量的音色库。但是我面临着播放 MIDI 文件的问题。问题是所有的 MIDI 轨道(鼓、键盘、低音、合成器等)都在播放,但它们只使用了一个乐器。我找到了 OS X 的解决方案,但我需要 iOS 的解决方案。
我是否需要为每个乐器创建一个 audioUnit
,并用 kAudioUnitSubType_Sampler
来播放?
或者,在选定的通道上实时更改所使用的乐器,这是否可能?如何实现?
以下是我的代码,但它不能正常工作:
// Create a client
MIDIClientRef virtualMidi;
Check(MIDIClientCreate(CFSTR("Virtual Client"),
MyMIDINotifyProc,
NULL,
&virtualMidi));
// Create an endpoint
MIDIEndpointRef virtualEndpoint;
Check(MIDIDestinationCreate(virtualMidi, CFSTR("Virtual Destination"), MyMIDIReadProc, samplerUnit, &virtualEndpoint));
// Initialise the music sequence
NewMusicSequence(&midiSequence);
if (!midiFilePath) {
midiFilePath = [[NSBundle mainBundle]
pathForResource:@"carelesswhisper"
ofType:@"mid"];
}
NSLog(@"midiFilePath %@", midiFilePath);
// Create a new URL which points to the MIDI file
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath];
MidiParser *midiParser = [[MidiParser alloc] init];
NSData *data = [NSData dataWithContentsOfFile:midiFilePath];
[midiParser parseData:data];
NSString *midiInfo = [midiParser log];
NSLog(@"midiInfo %@", midiInfo);
MusicSequenceLoadFlags loadFlags = 0;
loadFlags = kMusicSequenceLoadSMF_ChannelsToTracks;
MusicSequenceFileLoad(midiSequence, (__bridge CFURLRef) midiFileURL, 0, loadFlags);
// Initialise the music player
NewMusicPlayer(&midiPlayer);
// ************* Set the endpoint of the sequence to be our virtual endpoint
MusicSequenceSetMIDIEndpoint(midiSequence, virtualEndpoint);
if (!soundBankFilePath) {
soundBankFilePath = [[NSBundle mainBundle] pathForResource:@"SGM-V2.01-1" ofType:@"sf2"];
}
NSLog(@"soundBankFilePath %@", soundBankFilePath);
NSURL *presetURL = [NSURL fileURLWithPath:soundBankFilePath];
// Initialise the sound font
AUSamplerInstrumentData bpdata;
bpdata.fileURL = (__bridge CFURLRef) presetURL;
bpdata.bankMSB = kAUSampler_DefaultMelodicBankMSB;
bpdata.bankLSB = kAUSampler_DefaultBankLSB;
bpdata.instrumentType = kInstrumentType_SF2Preset;
// set the kAUSamplerProperty_LoadPresetFromBank property
result = AudioUnitSetProperty(samplerUnit,
kAUSamplerProperty_LoadInstrument,
kAudioUnitScope_Global,
0,
&bpdata,
sizeof(bpdata));
MusicPlayerSetSequence(midiPlayer, midiSequence);
// Called to do some MusicPlayer setup. This just
// reduces latency when MusicPlayerStart is called
// MusicPlayerPreroll(midiPlayer);
// Starts the music playing
MusicPlayerStart(midiPlayer);
// Get length of track so that we know how long to kill time for
MusicTrack track;
MusicTimeStamp len;
UInt32 sz = sizeof(MusicTimeStamp);
MusicSequenceGetIndTrack(midiSequence, 1, &track);
MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &len, &sz);
while (1) { // kill time until the music is over
usleep (3 * 1000 * 1000);
MusicTimeStamp now = 0;
MusicPlayerGetTime (midiPlayer, &now);
if (now >= len)
break;
}