Почему мой многоканальный микшер больше не играет в iOS 8?

Я написал код для воспроизведения мультиинструментальных MIDI-файлов на iOS. В iOS 7 работает нормально, но на iOS 8 перестал работать.

Я разобрал это до сути. Вместо того, чтобы создавать 16 каналов для моего многоканального микшера, я просто создаю один узел сэмплера и отображаю все дорожки на этот канал. Это все еще показывает ту же самую проблему как версия мультисэмплера. Ни один из вызовов Audio Toolbox не возвращает код ошибки (все они возвращают 0) в iOS 7 или iOS 8. Последовательность воспроизводится через динамики в 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));
}

У меня есть некоторый код, не включенный здесь, который опрашивает, чтобы увидеть, если последовательность все еще воспроизводится, вызывая MusicPlayerGetTime на MusicPlayer пример. В iOS 7 результатом этого вызова каждый раз является количество секунд, прошедших с момента начала воспроизведения. В iOS 8 вызов всегда возвращает 0, что, вероятно, означает MusicPlayer не начинает воспроизводить последовательность вызова MusicPlayerStart,

Приведенный выше код сильно зависит от порядка - вы должны делать определенные звонки раньше других; например, открыть график перед вызовом getInfo на узле и не загружать инструменты, пока вы не назначите треки каналам. Я следовал всем советам в других потоках Stackru и убедился, что при правильном заказе коды ошибок исчезают.

Любые эксперты по iOS MIDI знают, что могло измениться между iOS 7 и iOS 8, чтобы этот код перестал работать?

1 ответ

В iOS 8 Apple представила гладкую Obj-C абстракцию основного аудио API - AVAudioEngine. Вы, вероятно, должны это проверить. https://developer.apple.com/videos/wwdc/2014/

Другие вопросы по тегам