Core Audio: как воспроизводить MIDI MusicSequence с помощью MusicPlayer, когда AUGraph устарел?
У меня есть MIDI Synth Unit
AudioComponentDescription midiSynthDesc;
midiSynthDesc.componentType = kAudioUnitType_MusicDevice;
midiSynthDesc.componentSubType = kAudioUnitSubType_MIDISynth;
midiSynthDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
midiSynthDesc.componentFlags = 0;
midiSynthDesc.componentFlagsMask = 0;
который раньше был в AUGraph. Но поскольку AUGraph устарел, я использовал AudioComponentInstanceNew, чтобы создать его без использования AUNode и AUGraph.
AudioComponent foundMIDISynthReference = AudioComponentFindNext ( NULL, &midiSynthDesc);
AudioComponentInstanceNew(foundMIDISynthReference, &midiSynthUnit);
Я использовал его для воспроизведения последовательности, прикрепив ее к AUGraph
NSString *presetURLPath = [[NSBundle mainBundle] pathForResource:@"GortsMiniPianoJ1" ofType:@"SF2"];
NSURL * presetURL = [NSURL fileURLWithPath:presetURLPath];
[self loadFromDLSOrSoundFont: (NSURL *)presetURL withPatch: (int)3];
NSString *midiFilePath = [[NSBundle mainBundle] pathForResource:name ofType:@"mid"];
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath];
NewMusicPlayer(&musicPlayer);
MusicPlayerSetSequence(musicPlayer, musicSequence);
MusicSequenceSetAUGraph(musicSequence, _processingGraph);
MusicPlayerPreroll(musicPlayer);
MusicPlayerStart(musicPlayer);
Но теперь, когда AUGraph устарел и используется только AudioUnit, как я могу использовать воспроизведение файлов MIDI в Core Audio?
0 ответов
MusicSequence принимает ссылку на MIDIEndPointRef вместо AUGraph. Возможный обходной путь - создать внутренний виртуальный порт для приема событий, отправляемых MusicPlayer, и повторной отправки их на ваши аудиоустройства. Вот упрощенный пример:
Создает MIDI-клиент:
MIDIClientRef clientRef;
id myManager;
MIDIClientCreate(CFSTR("MyMidiClient"), NULL, (__bridge void *)myManager, &clientRef);
Создает виртуальный входной порт:
MIDIEndpointRef endPointRef;
MIDIDestinationCreate(clientRef, CFSTR("MyVirtualInPort"), (MIDIReadProc)MidiInputCallback, (__bridge void *)myManager, &endPointRef);
MIDI-события, отправляемые MusicPlayer, принимаются через обратный вызов, на который ссылается при создании порта:
void MidiInputCallback(const MIDIPacketList *pktlist, void *refCon, void *connRefCon)
{
id myManager = (__bridge id)refCon;
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
UInt8 status, byte1, byte2;
for (unsigned int ipack = 0; ipack < pktlist->numPackets; ++ipack)
{
status = packet->data[0];
byte1 = packet->data[1];
byte2 = packet->data[2];
// Do something with the MIDI events
printf("Received Event %d %d %d\n", status, byte1, byte2);
if (packet) packet = MIDIPacketNext(packet);
}
}
Создает очень простой MusicPlayer, содержащий одну ноту:
MusicSequence musicSequence;
MusicPlayer player;
MusicTrack track;
NewMusicSequence(&musicSequence);
NewMusicPlayer(&player);
MusicPlayerSetSequence(player, musicSequence);
MusicSequenceNewTrack(musicSequence, &track);
MIDIChannelMessage msg = {
.status = 0 | 9 << 4, // channel 1, type note on
.data1 = 60, // pitch
.data2 = 80 // velocity
};
MusicTrackNewMIDIChannelEvent(track, 0.0, &msg);
msg.data2 = 0; // pseudo note off
MusicTrackNewMIDIChannelEvent(track, 4.0, &msg);
Ссылается на EndPoint и начинает воспроизведение: проигранная нота принимается функцией обратного вызова, где ее можно перенаправить на аудиоустройство типа музыкальное устройство.
MusicSequenceSetMIDIEndpoint(musicSequence, endPointRef); //the port created above
MusicPlayerStart(player);