Estoy escribiendo una aplicación iOS que toma la entrada desde el micrófono, la ejecuta a través de una unidad de audio de filtro de paso alto y la reproduce a través de los altavoces. Pude hacer esto con éxito utilizando la API de AUGraph. En él, puse dos nodos: una unidad de E/S remotas y una unidad de audio de efectos (kAudioUnitType_Effect
, kAudioUnitSubType_HighPassFilter
) y conecté el alcance de salida del elemento de entrada de la unidad io a la entrada de la unidad de efectos y la salida del nodo de efectos a la unidad alcance de entrada del elemento de salida. Pero ahora tengo la necesidad de hacer un análisis basado en las muestras de audio procesadas, por lo que necesito acceso directo al buffer. Esto significa (y corríjame si estoy equivocado) ya no puedo usar AUGraphConnectNodeInput
para establecer la conexión entre la salida del nodo de efectos y el elemento de salida de la unidad io, y tengo que adjuntar una función de devolución de llamada para el elemento de salida de la unidad io, para que pueda acceder al búfer cada vez que los altavoces necesitan nuevas muestras. Lo he hecho, pero obtengo un error -50 cuando llamo a la función AudioUnitRender
en la devolución de llamada de renderizado. Creo que tengo un caso de desajuste de ASBD entre las dos unidades de audio, ya que no estoy haciendo nada al respecto en la devolución de llamada de render (y el AUGraph se encargó de eso antes). Aquí está el código:Problema al escribir una función de devolución de llamada de renderizado de E/S remotas
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
Como he dicho antes, estoy recibiendo un error de -50 en la llamada AudioUnitRender
, y Estoy encontrando poca o ninguna documentación sobre eso.
Cualquier ayuda será muy apreciada.
Gracias a Tim Bolstad (http://timbolstad.com/2010/03/14/core-audio-getting-started/) por proporcionar un excelente tutorial de punto de partida.