Проблемы при написании функции обратного вызова рендеринга удаленного ввода / вывода

Я пишу приложение для iOS, которое принимает данные с микрофона, пропускает их через аудиоустройство фильтра верхних частот и воспроизводит их через динамики. Я смог сделать это успешно с помощью AUGraph API. В него я поместил два узла: модуль удаленного ввода / вывода и модуль аудио эффектов (kAudioUnitType_Effect, kAudioUnitSubType_HighPassFilter), и подключил область вывода элемента ввода модуля io к входу модуля эффекта, а вывод узла эффекта к области ввода элемента вывода io. Но теперь мне нужно провести некоторый анализ на основе обработанных аудиосэмплов, поэтому мне нужен прямой доступ к буферу. Это значит (и, пожалуйста, поправьте меня, если я ошибаюсь) я больше не могу использовать AUGraphConnectNodeInput чтобы установить связь между выходом узла эффекта и выходным элементом модуля io, а также добавить функцию обратного вызова рендеринга для выходного элемента модуля io, чтобы я мог получить доступ к буферу всякий раз, когда динамикам нужны новые сэмплы. Я сделал это, но я получаю ошибку -50, когда я вызываю AudioUnitRender функция в обратном вызове рендеринга. Я полагаю, что у меня есть случай несоответствия ASBD между двумя аудиоустройствами, так как я ничего не делаю в обратном вызове рендеринга (и AUGraph позаботился об этом раньше). Вот код:

AudioController.h:

@interface AudioController : NSObject
{
    AUGraph mGraph;
    AudioUnit mEffects;
    AudioUnit ioUnit;
}

@property (readonly, nonatomic) AudioUnit mEffects;
@property (readonly, nonatomic) AudioUnit ioUnit;

-(void)initializeAUGraph;
-(void)startAUGraph;
-(void)stopAUGraph;

@end

AudioController.mm:

@implementation AudioController

…

static OSStatus renderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
    AudioController *THIS = (__bridge AudioController*)inRefCon;

    AudioBuffer buffer;

    AudioStreamBasicDescription fxOutputASBD;
    UInt32 fxOutputASBDSize = sizeof(fxOutputASBD);
    AudioUnitGetProperty([THIS mEffects], kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &fxOutputASBD, &fxOutputASBDSize);

    buffer.mDataByteSize = inNumberFrames * fxOutputASBD.mBytesPerFrame;
    buffer.mNumberChannels = fxOutputASBD.mChannelsPerFrame;
    buffer.mData = malloc(buffer.mDataByteSize);

    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0] = buffer;

    //TODO prender ARM y solucionar problema de memoria

    OSStatus result = AudioUnitRender([THIS mEffects], ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);
    [THIS hasError:result:__FILE__:__LINE__];

    memcpy(ioData, buffer.mData, buffer.mDataByteSize);

    return noErr;
}


- (void)initializeAUGraph
{
    OSStatus result = noErr;

    // create a new AUGraph
    result = NewAUGraph(&mGraph);

    AUNode outputNode;
    AUNode effectsNode;

    AudioComponentDescription effects_desc;
    effects_desc.componentType = kAudioUnitType_Effect;
    effects_desc.componentSubType = kAudioUnitSubType_LowPassFilter;
    effects_desc.componentFlags = 0;
    effects_desc.componentFlagsMask = 0;
    effects_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

    AudioComponentDescription output_desc;
    output_desc.componentType = kAudioUnitType_Output;
    output_desc.componentSubType = kAudioUnitSubType_RemoteIO;
    output_desc.componentFlags = 0;
    output_desc.componentFlagsMask = 0;
    output_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

    // Add nodes to the graph to hold the AudioUnits
    result = AUGraphAddNode(mGraph, &output_desc, &outputNode);
    [self hasError:result:__FILE__:__LINE__];
    result = AUGraphAddNode(mGraph, &effects_desc, &effectsNode );
    [self hasError:result:__FILE__:__LINE__];

    // Connect the effect node's output to the output node's input
    // This is no longer the case, as I need to access the buffer
    // result = AUGraphConnectNodeInput(mGraph, effectsNode, 0, outputNode, 0);
    [self hasError:result:__FILE__:__LINE__];

    // Connect the output node's input scope's output to the effectsNode input
    result = AUGraphConnectNodeInput(mGraph, outputNode, 1, effectsNode, 0);
    [self hasError:result:__FILE__:__LINE__];

    // open the graph AudioUnits
    result = AUGraphOpen(mGraph);
    [self hasError:result:__FILE__:__LINE__];

    // Get a link to the effect AU
    result = AUGraphNodeInfo(mGraph, effectsNode, NULL, &mEffects);
    [self hasError:result:__FILE__:__LINE__];

    // Same for io unit
    result = AUGraphNodeInfo(mGraph, outputNode, NULL, &ioUnit);
    [self hasError:result:__FILE__:__LINE__];

    // Enable input on io unit
    UInt32 flag = 1;
    result = AudioUnitSetProperty(ioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flag, sizeof(flag));
    [self hasError:result:__FILE__:__LINE__];

    // Setup render callback struct
    AURenderCallbackStruct renderCallbackStruct;
    renderCallbackStruct.inputProc = &renderInput;
    renderCallbackStruct.inputProcRefCon = (__bridge void*)self;

    // Set a callback for the specified node's specified input
    result = AUGraphSetNodeInputCallback(mGraph, outputNode, 0, &renderCallbackStruct);
    [self hasError:result:__FILE__:__LINE__];

    // Get fx unit's input current stream format...
    AudioStreamBasicDescription fxInputASBD;
    UInt32 sizeOfASBD = sizeof(AudioStreamBasicDescription);

    result = AudioUnitGetProperty(mEffects, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &fxInputASBD, &sizeOfASBD);
    [self hasError:result:__FILE__:__LINE__];

    // ...and set it on the io unit's input scope's output 
    result = AudioUnitSetProperty(ioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Output,
                                  1,
                                  &fxInputASBD,
                                  sizeof(fxInputASBD));
    [self hasError:result:__FILE__:__LINE__];

    // Set fx unit's output sample rate, just in case
    Float64 sampleRate = 44100.0;

    result = AudioUnitSetProperty(mEffects,
                                  kAudioUnitProperty_SampleRate,
                                  kAudioUnitScope_Output,
                                  0,
                                  &sampleRate,
                                  sizeof(sampleRate));
    [self hasError:result:__FILE__:__LINE__];

    // Once everything is set up call initialize to validate connections
    result = AUGraphInitialize(mGraph);
    [self hasError:result:__FILE__:__LINE__];
}

@end

Как я уже говорил, я получаю ошибку -50 на AudioUnitRender позвоните, и я нахожу мало или вообще никаких документов по этому поводу.

Любая помощь будет высоко ценится.

Спасибо Тиму Болстаду ( http://timbolstad.com/2010/03/14/core-audio-getting-started/) за отличное начальное руководство.

3 ответа

Существуют более простые рабочие примеры использования RemoteIO только для воспроизведения буферов аудио. Возможно, начните с одного из этих первых, а не с графика.

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

Теперь, если вы хотите сделать фильтр, он вам может понадобиться, но даже в этом случае убедитесь, что вы действительно правильно соединяете компоненты.

Вот фрагмент приложения, над которым я работаю:

AUGraphConnectNodeInput(graph, outputNode, kInputBus, mixerNode, kInputBus);
AUGraphConnectNodeInput(graph, mixerNode, kOutputBus, outputNode, kOutputBus);

Это соединяет вход от блока RemoteIO с блоком многоканального микшера, а затем соединяет выход микшера с выходом RemoteIO на динамик.

Мне кажется, что вы передаете не тот аудио блок AudioUnitRender, Я думаю, что вам нужно пройти в ioUnit вместо mEffects, В любом случае, дважды проверьте все параметры, которые вы передаете AudioUnitRender, Когда я вижу -50 вернулся, это потому, что я испортил один из них.

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