ios-混合midi文件,每个文件都有自己的声音字体

ios-混合midi文件,每个文件都有自己的声音字体,ios,audio,core-audio,midi,coremidi,Ios,Audio,Core Audio,Midi,Coremidi,我正在寻找一种混合2个或更多midi文件的方法,每个文件都有自己的声音字体文件。我为一个文件找到了以下代码,并尝试使用多个音乐播放器,但我想这不应该是正确的方法。而且我每秒钟都会听到一些奇怪的流行音乐 那么,有没有其他方法,可能没有MusicLayer和musicsequence方法,只使用au单位 以下是我在另一个线程中找到的代码: -(void) playMusic:(NSString*) name { NSString *presetURLPath = [[NSBundle mainBun

我正在寻找一种混合2个或更多midi文件的方法,每个文件都有自己的声音字体文件。我为一个文件找到了以下代码,并尝试使用多个音乐播放器,但我想这不应该是正确的方法。而且我每秒钟都会听到一些奇怪的流行音乐

那么,有没有其他方法,可能没有MusicLayer和musicsequence方法,只使用au单位

以下是我在另一个线程中找到的代码:

-(void) playMusic:(NSString*) name
{
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);

if (NewMusicSequence(&musicSequence) != noErr) 
{
    [NSException raise:@"play" format:@"Can't create MusicSequence"];  
}

if(MusicSequenceFileLoad(musicSequence, (CFURLRef)midiFileURL, 0, 0 != noErr)) 
{
    [NSException raise:@"play" format:@"Can't load MusicSequence"];
}

MusicPlayerSetSequence(musicPlayer, musicSequence);
MusicSequenceSetAUGraph(musicSequence, _processingGraph);
MusicPlayerPreroll(musicPlayer);
MusicPlayerStart(musicPlayer);
}

多个MusicLayer实例听起来有点尴尬(为了保持同步),我怀疑多个螺旋图是否会起作用。我自己没试过

使用
MusicPlayer
的唯一实例的一种方法是将每个midi文件中的曲目加载到“主”序列中。您可以为每个曲目(
MusicTrackSetDestNode
)分配一个AUSampler节点(具有唯一的soundFont),并通过一个
AUMixer
将它们连接起来。您需要管理每个midi文件中的节奏(使用
MusicTrackNewExtendedTempoEvent
)将文件节奏分配给相关曲目。静音音轨+音轨音量可在音轨或混音器级别轻松处理

我想这很大程度上取决于您使用的midi文件的性质。Fwiw-我在iOS上做了很多关于midi文件播放的研究,没有什么比MusicPlayer更容易使用。但是,如果MusicLayer不适合您,那么这个问题可能值得一看

指定混音器输入的数量:

// set the bus count
UInt32 numBuses = BUS_COUNT; // a constant defined elsewhere
result = AudioUnitSetProperty(mixerUnit, 
                              kAudioUnitProperty_ElementCount, 
                              kAudioUnitScope_Input, 
                              0, 
                              &numBuses, 
                              sizeof(numBuses));

if (noErr != result) {[self printErrorMessage: @"Error setting Bus Count" withStatus: result]; return;}

所以我试着为每个轨迹设置节点,但这并没有改变任何事情。soundfont仅为第一个
采样器单元设置。
下面是我如何设置图表的:

AudioComponentDescription MixerUnitDescription;
MixerUnitDescription.componentType          = kAudioUnitType_Mixer;
MixerUnitDescription.componentSubType       = kAudioUnitSubType_MultiChannelMixer;
MixerUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
MixerUnitDescription.componentFlags         = 0;
MixerUnitDescription.componentFlagsMask     = 0;

AudioComponentDescription cd = {};
cd.componentManufacturer     = kAudioUnitManufacturer_Apple;
cd.componentType = kAudioUnitType_MusicDevice; // type - music device
cd.componentSubType = kAudioUnitSubType_Sampler; // sub type - sampler to convert our MIDI

result = NewAUGraph (&_processingGraph);
result = AUGraphAddNode(self.processingGraph, &MixerUnitDescription, &mixerNode);

result = AUGraphAddNode (self.processingGraph, &cd, &samplerNode);

result = AUGraphAddNode (self.processingGraph, &cd, &samplerNode2);

cd.componentType = kAudioUnitType_Output;  // Output
cd.componentSubType = kAudioUnitSubType_RemoteIO;  // Output to speakers

result = AUGraphAddNode (self.processingGraph, &cd, &ioNode);
result = AUGraphOpen (self.processingGraph);

result = AUGraphConnectNodeInput (self.processingGraph, samplerNode, 0, mixerNode, 0);
result = AUGraphConnectNodeInput (self.processingGraph, samplerNode2, 0, mixerNode, 1);

result = AUGraphConnectNodeInput (self.processingGraph, mixerNode, 0, ioNode, 0);
result = AUGraphNodeInfo (self.processingGraph, samplerNode, 0, &_samplerUnit);
result = AUGraphNodeInfo (self.processingGraph, samplerNode2, 0, &_samplerUnit2);
result = AUGraphNodeInfo (self.processingGraph, ioNode, 0, &_ioUnit);
这是Apple Developer页面中的示例方法,我对其进行了修改,将soundfont分配给特定的
采样器单元

-(OSStatus) loadFromDLSOrSoundFont: (NSURL *)bankURL withPatch: (int)presetNumber withAudioUnit:(AudioUnit)auUnit{

OSStatus result = noErr;

// fill out a bank preset data structure
AUSamplerBankPresetData bpdata;
bpdata.bankURL  = (__bridge CFURLRef) bankURL;
bpdata.bankMSB  = kAUSampler_DefaultMelodicBankMSB;
bpdata.bankLSB  = kAUSampler_DefaultBankLSB;
bpdata.presetID = (UInt8) presetNumber;

// set the kAUSamplerProperty_LoadPresetFromBank property
result = AudioUnitSetProperty(auUnit,
                          kAUSamplerProperty_LoadPresetFromBank,
                          kAudioUnitScope_Global,
                          0,
                          &bpdata,
                          sizeof(bpdata));

// check for errors
NSCAssert (result == noErr,
       @"Unable to set the preset property on the Sampler. Error code:%d '%.4s'",
       (int) result,
       (const char *)&result);

return result;
}
然后我这样做了两次,让每首曲目进入
音乐序列

if(MusicSequenceFileLoad(tmpSequence, (__bridge CFURLRef)midiFileURL, 0, 0 != noErr)) 
{
    [NSException raise:@"play" format:@"Can't load MusicSequence"];
}


MusicSequenceGetIndTrack(tmpSequence, 0, &tmpTrack);
MusicSequenceNewTrack(musicSequence, &track);
MusicTimeStamp trackLen = 0;
UInt32 trackLenLen = sizeof(trackLen);
MusicTrackGetProperty(tmpTrack, kSequenceTrackProperty_TrackLength, &trackLen, &trackLenLen);
MusicTrackCopyInsert(tmpTrack, 0, trackLenLen, track, 0);
最后:

MusicTrackSetDestNode(track, samplerNode);
MusicTrackSetDestNode(track2, samplerNode2);
但这不会将soundfont分配给
采样器单元2

[self loadFromDLSOrSoundFont: (NSURL *)presetURL2 withPatch: (int)0 withAudioUnit:self.samplerUnit2];

分配给
采样器单元
可以正常工作。你知道我在这里遗漏了什么吗?

我和你的问题完全一样。所有曲目都使用第一个声音字体乐器播放。我遵循了你的解决方案,但一开始它不起作用。最后,我解决了这个问题。正如您提到的,调用函数的顺序非常重要。是的。实际上,序列调用应该是这样的:

.....
MusicSequenceSetAUGraph(s, _processingGraph);
.......
MusicTrackSetDestNode(track[i], samplerNodes[i]);
......
[self loadFromDLSOrSoundFont];
......
MusicPlayerStart(p);

这在我的项目中起作用

太好了,谢谢。我也有这个想法,并开始这样编写代码,但后来我认为我可以只将soundfonts分配给MusiqSequences,而不是MusicTrack,因此我放弃了这种方法。我实现了您建议的多音轨静音方法——它在一个用例中运行良好。但是有没有办法强制多个播放器同步-midi时钟在这里有帮助吗?我想自动更改曲目上的播放头/开始和停止。像abelton live in core audio/audio toolbox这样的应用程序不可能构建midi混音部分吗?当我尝试使用混音器设置多个AUSampler节点时,我无法单独调整音量。即使它们连接到单独的输入总线(用CAShow验证),总线0的音量控制着所有输入总线的音量。在网页中理解所有这些代码有点困难,但我没有看到任何引人注目的地方——除了我没有看到在哪里定义混音器输入的数量。您需要这样做(请参阅我的答案编辑)。此外,CAShow(graph)是一个显示图形+连接的强大功能。也可用于序列和曲目:CAShow(序列)。我知道为曲目分配唯一的AUSampler很有效-我已经在dev的一个应用程序中完成了10多首曲目。非常感谢您迄今为止的帮助。我像你说的那样设置了公共汽车的数量,但那没有改变任何事情。sequnce图显示有2条轨道,这是正确的。连接图对我来说似乎是正确的:AudioUnitGraph 0x3D0A000:成员节点:节点1:'aumx''mcmx''appl'节点2:'aumu''samp''appl'节点3:'aumu''samp''appl'节点4:'auou''rioc''appl'连接:节点2总线0=>节点1总线0;节点3总线0=>节点1总线1;节点1总线0=>节点4总线0;没有格式就有点难看,但它看起来像是连在一起的。(fwiw-我喜欢从最低的功能级别向上将节点添加到图形中,这样读取连接并捕获任何错误会更加连贯:RemoteIO、Mixer,然后是所有的Ausampler。)如果一个采样器在一条轨道上工作,也许可以看看是否可以让sampler2在同一条轨道上工作(即,两个采样器都正常)。然后两个轨迹都正常,等等(消除过程)。哦,也许这只是我的怪癖,但我在添加它们之前设置了ausampler(加载字体等)。我不知道这是否重要。但我如何才能确定哪个采样器在哪个轨道上工作?在我看来,采样器单元2根本不用。是否有其他方法(如CAShow())用于调试?对于未格式化的图形,我很抱歉,我无法在评论部分阻止它。因此我找到了soundfont分配问题的解决方案:它只适用于使用MusicSequenceSetAUGraph(musicSequence,_processingGraph);首先,然后是MusicTrackSetDestNode(曲目,采样节点);MusicTrackSetDestNode(track2,samplerNode2);