核心音频:不推荐使用 AUGraph 时如何通过 MusicPlayer 播放 MIDI MusicSequence?
Posted
技术标签:
【中文标题】核心音频:不推荐使用 AUGraph 时如何通过 MusicPlayer 播放 MIDI MusicSequence?【英文标题】:Core Audio: How to play MIDI MusicSequence by MusicPlayer while AUGraph is deprecated? 【发布时间】:2020-07-20 16:18:28 【问题描述】:我有一个 MIDI 合成器
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,我如何在 Core Audio 中使用 Play MIDI Files?
【问题讨论】:
我猜你不能使用 AVAudiosequencer 或 AVMIDIPlayer? @matt 我倾向于使用 AudioUnit,因为我有其他需要 AudioUnits 的音频处理工作。如果我能把所有东西放在 AudioUnit 中就好了。另外我对 AVAudioSequencer 或 AVMIDIPlayer 的经验很少,我也需要加载 SoundFont 才能播放。所以播放 MIDI 的 AudioUnit 是“不错的”,但不是“必须的”。您是否推荐任何资源让我了解有关 AVAudioSequencer 或 AVMIDIPlayer 的更多信息?谢谢 嗯,关于 AUGraph 被弃用的一点是它被 AVAudioEngine 取代。多年来,Apple 进行了许多 WWDC 演讲,解释 AVAudioEngine 并为此做准备。我建议您观看它们,看看您是否可以在船上进行更改。 @John 我很好奇你是否在这方面取得了任何进展?我发现自己处于类似情况,需要更新一些复杂的遗留 MusicSequence 和 MusicPlayer 代码,而我尝试使用这些 API 和 AVAudioEngine 代替 AUGraph 的尝试尚未成功。据我所知,AVMIDIPlayer 和 AVAudioSequencer 仍然不支持将序列和音轨操作到 MusicSequence API 所做的水平。提前致谢! 【参考方案1】: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);
MusicPlayer 发送的 MIDI 事件通过创建端口时引用的回调接收:
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);
引用端点并开始播放:播放的音符由回调接收,可以重定向到音乐设备类型的音频单元。
MusicSequenceSetMIDIEndpoint(musicSequence, endPointRef); //the port created above
MusicPlayerStart(player);
【讨论】:
以上是关于核心音频:不推荐使用 AUGraph 时如何通过 MusicPlayer 播放 MIDI MusicSequence?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 iOS 的 AUGraph 中添加两个 I/O 音频单元?
在IOS核心音频中,如何找到文件播放器音频单元的真实当前播放头位置?