为啥我的多通道混音器不再在 iOS 8 中播放?

Posted

技术标签:

【中文标题】为啥我的多通道混音器不再在 iOS 8 中播放?【英文标题】:Why is my multi-channel mixer no longer playing in iOS 8?为什么我的多通道混音器不再在 iOS 8 中播放? 【发布时间】:2015-01-05 02:30:43 【问题描述】:

我编写了一些代码来在 ios 上播放多乐器通用 MIDI 文件。它在 iOS 7 中运行良好,但在 iOS 8 上停止运行。

在这里,我已将其简化为本质。我没有为我的多通道混音器创建 16 个通道,而是只创建一个采样器节点,并将所有轨道映射到该通道。它仍然表现出与多采样器版本相同的问题。在 iOS 7 或 iOS 8 中,Audio Toolbox 调用均未返回错误代码(它们都返回 0)。序列通过 iOS 7 中的扬声器播放,在模拟器和 iPhone/iPad 设备上均如此。在 iOS 8 模拟器或 iPhone/iPad 设备上运行完全相同的代码,不会产生声音。

如果您注释掉对 [self initGraphFromMIDISequence] 的调用,它会在 iOS 8 上以默认正弦波声音播放。

@implementation MyMusicPlayer 
    MusicPlayer _musicPlayer;
    MusicSequence _musicSequence;
    AUGraph _processingGraph;


- (void)playMidi:(NSURL*)midiFileURL 
    NewMusicSequence(&_musicSequence);
    MusicSequenceFileLoad(_musicSequence, CFBridgingRetain(midiFileURL), 0, 0);

    NewMusicPlayer(&_musicPlayer);
    MusicPlayerSetSequence(_musicPlayer, _musicSequence);

    [self initGraphFromMIDISequence];

    MusicPlayerPreroll(_musicPlayer);
    MusicPlayerStart(_musicPlayer);


// Sets up an AUGraph with one channel whose instrument is loaded from a sound bank.
// Maps all the tracks of the MIDI sequence onto that channel.  Basically this is a
// way to replace the default sine-wave sound with another (single) instrument.
- (void)initGraphFromMIDISequence 
    NewAUGraph(&_processingGraph);

    // Add one sampler unit to the graph.
    AUNode samplerNode;
    AudioComponentDescription cd = ;
    cd.componentManufacturer = kAudioUnitManufacturer_Apple;
    cd.componentType = kAudioUnitType_MusicDevice;
    cd.componentSubType = kAudioUnitSubType_Sampler;
    AUGraphAddNode(_processingGraph, &cd, &samplerNode);

    // Add a Mixer unit node to the graph
    cd.componentType = kAudioUnitType_Mixer;
    cd.componentSubType = kAudioUnitSubType_MultiChannelMixer;
    AUNode mixerNode;
    AUGraphAddNode(_processingGraph, &cd, &mixerNode);

    // Add the Output unit node to the graph
    cd.componentType = kAudioUnitType_Output;
    cd.componentSubType = kAudioUnitSubType_RemoteIO; // Output to speakers.
    AUNode ioNode;
    AUGraphAddNode(_processingGraph, &cd, &ioNode);

    AUGraphOpen(_processingGraph);

    // Obtain the mixer unit instance from its corresponding node, and set the bus count to 1.
    AudioUnit mixerUnit;
    AUGraphNodeInfo(_processingGraph, mixerNode, NULL, &mixerUnit);
    UInt32 const numChannels = 1;
    AudioUnitSetProperty(mixerUnit,
                         kAudioUnitProperty_ElementCount,
                         kAudioUnitScope_Input,
                         0,
                         &numChannels,
                         sizeof(numChannels));

    // Connect the sampler node's output 0 to mixer node output 0.
    AUGraphConnectNodeInput(_processingGraph, samplerNode, 0, mixerNode, 0);

    // Connect the mixer unit to the output unit.
    AUGraphConnectNodeInput(_processingGraph, mixerNode, 0, ioNode, 0);

    // Obtain reference to the audio unit from its node.
    AudioUnit samplerUnit;
    AUGraphNodeInfo(_processingGraph, samplerNode, 0, &samplerUnit);
    MusicSequenceSetAUGraph(_musicSequence, _processingGraph);

    // Set the destination for each track to our single sampler node.
    UInt32 trackCount;
    MusicSequenceGetTrackCount(_musicSequence, &trackCount);
    MusicTrack track;
    for (int i = 0; i < trackCount; i++) 
      MusicSequenceGetIndTrack(_musicSequence, i, &track);
      MusicTrackSetDestNode(track, samplerNode);
    

    // You can use either a DLS or an SF2 file bundled with your app; both work in iOS 7.
    //NSString *soundBankPath = [[NSBundle mainBundle] pathForResource:@"GeneralUserv1.44" ofType:@"sf2"];
    NSString *soundBankPath = [[NSBundle mainBundle] pathForResource:@"gs_instruments" ofType:@"dls"];
    NSURL *bankURL = [NSURL fileURLWithPath:soundBankPath];
    AUSamplerBankPresetData bpdata;
    bpdata.bankURL  = (__bridge CFURLRef) bankURL;
    bpdata.bankMSB  = kAUSampler_DefaultMelodicBankMSB;
    bpdata.bankLSB  = kAUSampler_DefaultBankLSB;
    bpdata.presetID = 0;
    UInt8 instrumentNumber = 46;  // pick any GM instrument 0-127
    bpdata.presetID = instrumentNumber;
    AudioUnitSetProperty(samplerUnit,
                         kAUSamplerProperty_LoadPresetFromBank,
                         kAudioUnitScope_Global,
                         0,
                         &bpdata,
                         sizeof(bpdata));

我有一些代码,这里没有包含,它通过在 MusicPlayer 实例上调用 MusicPlayerGetTime 来轮询以查看序列是否仍在播放。在 iOS 7 中,每次调用的结果是自开始播放以来经过的秒数。在 iOS 8 中,调用总是返回 0,这大概意味着 MusicPlayer 不会在调用 MusicPlayerStart 时开始播放序列。

上面的代码是高度依赖顺序的——你必须先进行某些调用;例如,在节点上调用 getInfo 之前打开图表,并且在将轨道分配给通道之前不加载乐器。我遵循了其他 *** 线程中的所有建议,并验证了正确的顺序会使错误代码消失。

任何 iOS MIDI 专家都知道 iOS 7 和 iOS 8 之间可能发生了什么变化以使此代码停止工作?

【问题讨论】:

你从CAShow(theGraph)AUGraphIsRunning(theGraph, &amp;running)得到什么? 【参考方案1】:

在 iOS 8 中,Apple 引入了核心音频 API 的巧妙 Obj-C 抽象 - AVAudioEngine。 你应该检查一下。 https://developer.apple.com/videos/wwdc/2014/#502

【讨论】:

感谢您的建议,我能够将我的代码从 500 行不产生声音的代码减少到仅 5 行不产生声音的代码。我认为这是某种进步。 (从技术上讲,AVMIDIPlayer 从每个文件中产生大约 100 毫秒的声音,但随后它停止了。)无论我提供哪个(有效)音库或 DLS 文件,也不管哪个 MIDI 文件。 sim 卡和设备上的结果相同 - 只是一个嘟嘟声,它会切断。叹。但感谢您的指点。

以上是关于为啥我的多通道混音器不再在 iOS 8 中播放?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的多通道映射不能正常工作?

m4a 音频文件无法在 iOS 9 上播放

为啥我的混音器有时会在播放时按 A/D 来调节音量?

Core Audio 大(压缩)文件播放和内存占用

Live10 用户福利:免费的多通道波谱 Max 组件 Multi Analyzer

FCM - 为啥在较新的 Firebase Admin SDK 中不再支持 FCM 直接通道?