ios - 混合 MIDI 文件,每个文件都有自己的声音字体
-
13-12-2019 - |
题
我正在寻找一种方法来混合 2 个或更多 MIDI 文件,每个文件都有自己的声音字体文件。我找到了一个文件的以下代码,并尝试做多个音乐播放器,但我想这不应该是正确的方法。而且我每秒都会听到一些奇怪的流行声音。
那么有没有其他方法,也许没有音乐播放器和音乐序列方法,仅使用 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);
}
解决方案
多个 MusicPlayer 实例听起来有点尴尬(保持同步),我怀疑多个 AUGraph 是否可以工作。我自己没试过。
一种使用单个实例的方法 MusicPlayer
是将每个 MIDI 文件中的曲目加载到“主”序列中。您可以为每个轨道分配一个 AUSampler 节点(具有唯一的 soundFont)(MusicTrackSetDestNode
)并通过一个连接它们 AUMixer
. 。您需要管理每个 MIDI 文件的速度(使用 MusicTrackNewExtendedTempoEvent
) 将文件速度分配给相关轨道。静音轨道+轨道音量可以在轨道或混音器级别轻松处理。
我想很大程度上取决于您正在使用的 MIDI 文件的性质。Fwiw - 我对 iOS 上的 MIDI 文件播放进行了大量研究,没有什么比 MusicPlayer 更容易使用的了。但是,那 低音库 如果 MusicPlayer 不适合你,可能值得一看。
指定混频器输入的数量:
// 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;}
其他提示
我的问题与您完全相同。所有曲目都与第一个声音字体乐器一起玩。我跟着你的解决方案,但它起初不起作用。最后,我解决了这个问题。如上所述,呼叫函数的序列真的很好。是的。实际上,序列调用应该是这样的:
.....
MusicSequenceSetAUGraph(s, _processingGraph);
.......
MusicTrackSetDestNode(track[i], samplerNodes[i]);
......
[self loadFromDLSOrSoundFont];
......
MusicPlayerStart(p);
.
这在我的项目中有效。
所以我尝试为每个轨道设置节点,但这并没有改变任何东西。声音字体仅设置为第一个 samplerUnit
. 。这是我设置图表的方法:
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 开发人员页面中的示例方法,我修改了该方法以将声音字体分配给特定的 samplerUnit
:
-(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;
}
然后我做了两次让每个轨道进入 musicSequence
:
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);
但这不会将声音字体分配给 samplerUnit2
:
[self loadFromDLSOrSoundFont: (NSURL *)presetURL2 withPatch: (int)0 withAudioUnit:self.samplerUnit2];
分配给 samplerUnit
工作正常。我在这里缺少什么想法吗?