Проблемы при написании функции обратного вызова рендеринга удаленного ввода / вывода
Я пишу приложение для 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 вернулся, это потому, что я испортил один из них.