【问题标题】:ios - mixing midi files, each with own sound fontios - 混合 MIDI 文件,每个文件都有自己的声音字体
【发布时间】:2012-07-29 01:57:01
【问题描述】:

我正在寻找一种方法来混合 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);
}

【问题讨论】:

    标签: ios audio core-audio midi coremidi


    【解决方案1】:

    多个 MusicPlayer 实例听起来有点尴尬(保持同步),我怀疑多个 AUGraph 是否可以工作。我自己没试过。

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

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

    指定混音器输入的数量:

    // 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;}
    

    【讨论】:

    • 太好了,谢谢。我也有这个想法并开始这样编写代码,但后来我认为我只能将声音字体分配给MusiqSequences而不是MusicTracks,所以我放弃了这种方法。
    • 我已经实现了您建议的多轨静音方法 - 它适用于一个用例。但是有没有办法强制多个玩家同步——midi 时钟在这里有帮助吗?我想自动更改曲目上的播放头/开始和停止。是否不可能在核心音频/音频工具箱中构建像 abelton 这样的应用程序的 midi 混音部分?
    • 当我尝试使用混音器设置多个 AUSampler 节点时,我无法单独调整音量。即使它们连接到单独的输入总线,(通过 CAShow 验证)总线 0 的音量控制所有它们的音量。
    【解决方案2】:

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

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

    这适用于我的项目。

    【讨论】:

      【解决方案3】:

      所以我尝试为每个轨道设置节点,但这并没有改变任何东西。 soundfont 只为第一个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 Developer 页面中的示例方法,我对其进行了修改以将声音字体分配给特定的 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 工作正常。任何想法我在这里缺少什么?

      【讨论】:

      • 在网页中遵循所有这些代码有点困难,但我没有看到任何明显的东西 - 除了我没有看到您在哪里定义混音器输入的数量。您需要这样做(请参阅我的答案编辑)。 CAShow(graph) 也是显示图形+连接的绝佳功能。也可用于序列和轨道:CAShow(sequence)。我知道将唯一的 AUSampler 分配给轨道工作 - 在 dev 的一个应用程序中完成了 10 多个轨道。
      • 非常感谢您迄今为止的帮助。我按照你说的设置了公交车次数,但这并没有改变任何事情。序列图显示 2 条轨道,这是正确的。连接图对我来说似乎是正确的: AudioUnitGraph 0x3D0A000: Member Nodes: node 1: 'aumx' 'mcmx' 'appl' node 2: 'aumu' 'samp' 'appl' node 3: 'aumu' 'samp' 'appl'节点 4:“auou”“rioc”“appl”连接:节点 2 总线 0 => 节点 1 总线 0;节点 3 总线 0 => 节点 1 总线 1 ;节点 1 总线 0 => 节点 4 总线 0 ;
      • 如果没有格式化有点难以看到,但它看起来就像它挂对了。 (fwiw-我喜欢从最低功能级别向上添加节点,因此读取连接并捕获任何错误会更加连贯:RemoteIO,Mixer,然后是所有 AUSamplers。)如果一个采样器正在轨道上工作,也许看看您是否可以让 sampler2 在同一轨道上工作(即两个采样器都可以)。然后两者都是轨道ok,等等(消除过程)。哦-也许只是我的怪癖,但我在添加它们之前设置了 AUSamplers(加载的字体等)。不知道这是否重要。
      • 但是我怎么能确定哪个采样器在哪个轨道上工作呢?在我看来,samplerUnit2 根本没有使用。是否有任何其他方法(如 CAShow())进行调试?对于非格式化图表,我无法在 cmets 部分阻止它。
      • 所以我找到了声音字体分配问题的解决方案:它只有在你执行 MusicSequenceSetAUGraph(musicSequence, _processingGraph); 时才有效。首先,然后是 MusicTrackSetDestNode(track, samplerNode); MusicTrackSetDestNode(track2, samplerNode2);
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-19
      • 1970-01-01
      • 1970-01-01
      • 2011-10-22
      • 1970-01-01
      相关资源
      最近更新 更多