IOS - 混合MIDI文件,每个文件自己的声音字体(ios - mixing midi file

2019-06-26 14:14发布

我正在寻找一种方式混合2个或更多的MIDI文件,每个都有自己的声音字体文件。 我发现下面的代码为一个文件,并试图做多的音乐播放器,但我想这不应该是正确的做法。 此外,我得到一些奇怪的爆音声的每一秒。

那么,有没有其他办法,也许没有musicplayer和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);
}

Answer 1:

多MusicPlayer实例听起来有点别扭(保持同步),我有怀疑,多个AUGraphs会工作。 我还没有尝试过自己。

使用的鞋底实例的一个方法MusicPlayer将来自每个MIDI文件轨道加载到一个“主”序列。 你可以指定一个AUSampler节点(具有独特的音色库),以每首曲目( MusicTrackSetDestNode )和连接它们都通过AUMixer 。 你会需要管理从每个MIDI文件(使用节奏MusicTrackNewExtendedTempoEvent ),以文件的节奏分配到相关的轨道。 静音轨道+轨道体积在轨道或混合器级容易地处理。

我想很大程度上取决于你正在使用的MIDI文件的性质。 FWIW - 我做了iOS上大量的MIDI文件播放的研究并没有什么在那里,是那么容易,因为MusicPlayer使用。 然而, 低音的lib可能是值得考虑的,如果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;}


Answer 2:

我有完全一样的问题,因为你。 所有曲目与第一声音字体乐器进行演奏。 我跟着你的解决方案,但起初它不能正常工作。 最后,我解决这个问题。 正如你提到的,调用函数真的事宜中的顺序。 是的。 事实上,程序调用应该是这样的:

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

这部作品在我的项目。



Answer 3:

所以我试图设置节点为每个轨道,但这并没有改变任何东西。 该音色库得到只能为第一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);

这是来自苹果开发的网页的示例方法,该方法我修饰为声音字体指定给特定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工作正常。 任何想法我缺少在这里吗?



文章来源: ios - mixing midi files, each with own sound font