Pasar los datos AVCaptureAudioDataOutput en vDSP / Accelerate.framework

Estoy tratando de crear una aplicación que ejecute un FFT en datos de micrófono, por lo que puedo examinar, por ejemplo, la frecuencia más alta en la input.

Veo que hay muchos methods para get la input de audio (Remote AudioUnit, AudioQueue services y AVFoundation), pero parece que AVFoundation es el más simple. Tengo esta configuration:

// Configure the audio session AVAudioSession *session = [AVAudioSession shanetworkingInstance]; [session setCategory:AVAudioSessionCategoryRecord error:NULL]; [session setMode:AVAudioSessionModeMeasurement error:NULL]; [session setActive:YES error:NULL]; // Optional - default gives 1024 samples at 44.1kHz //[session setPrefernetworkingIOBufferDuration:samplesPerSlice/session.sampleRate error:NULL]; // Configure the capture session (strongly-referenced instance variable, otherwise the capture stops after one slice) _captureSession = [[AVCaptureSession alloc] init]; // Configure audio device input AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:NULL]; [_captureSession addInput:input]; // Configure audio data output AVCaptureAudioDataOutput *output = [[AVCaptureAudioDataOutput alloc] init]; dispatch_queue_t queue = dispatch_queue_create("My callback", DISPATCH_QUEUE_SERIAL); [output setSampleBufferDelegate:self queue:queue]; [_captureSession addOutput:output]; // Start the capture session. [_captureSession startRunning]; 

(más comprobación de errores, omitida aquí para facilitar la lectura).

Luego implemento el siguiente método AVCaptureAudioDataOutputSampleBufferDelegate :

 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { NSLog(@"Num samples: %ld", CMSampleBufferGetNumSamples(sampleBuffer)); // Usually gives 1024 (except the first slice) } 

No estoy seguro de cuál debería ser el siguiente paso. ¿Qué describe exactamente el formatting CMSampleBuffer (y qué supuestos se pueden hacer al respecto, en su caso)? ¿Cómo debo get los datos de audio sin procesar en vDSP_fft_zrip con la menor cantidad posible de preprocesamiento adicional? (Además, ¿qué recomendaría hacer para verificar que los datos sin procesar que veo son correctos?)

El CMSampleBufferRef es un tipo opaco que contiene 0 o más muestras de medios. Hay un poco de propaganda en los documentos:

http://developer.apple.com/library/ios/#documentation/CoreMedia/Reference/CMSampleBuffer/Reference/reference.html

En este caso, contendrá un búfer de audio, así como la descripción del formatting de muestra y la información de temporización, etc. Si está realmente interesado, simplemente coloque un punto de interrupción en la callback de los delegates y eche un vistazo.

El primer paso es get un puntero al búfer de datos que se ha devuelto:

 // get a pointer to the audio bytes CMItemCount numSamples = CMSampleBufferGetNumSamples(sampleBuffer); CMBlockBufferRef audioBuffer = CMSampleBufferGetDataBuffer(sampleBuffer); size_t lengthAtOffset; size_t totalLength; char *samples; CMBlockBufferGetDataPointer(audioBuffer, 0, &lengthAtOffset, &totalLength, &samples); 

El formatting de muestra pnetworkingeterminado para el micrófono del iPhone es PCM lineal, con muestras de 16 bits. Esto puede ser mono o estéreo dependiendo de si hay un micrófono externo o no. Para calcular la FFT necesitamos tener un vector flotante. Afortunadamente, hay una function acelerada para hacer la conversión para nosotros:

 // check what sample format we have // this should always be linear PCM // but may have 1 or 2 channels CMAudioFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer); const AudioStreamBasicDescription *desc = CMAudioFormatDescriptionGetStreamBasicDescription(format); assert(desc->mFormatID == kAudioFormatLinearPCM); if (desc->mChannelsPerFrame == 1 && desc->mBitsPerChannel == 16) { float *convertedSamples = malloc(numSamples * sizeof(float)); vDSP_vflt16((short *)samples, 1, convertedSamples, 1, numSamples); } else { // handle other cases as requinetworking } 

Ahora tiene un vector flotante del búfer de muestra que puede usar con vDSP_fft_zrip . No parece posible cambiar el formatting de input del micrófono para flotar muestras con AVFoundation , por lo que está atascado con este último paso de conversión. Me mantendría cerca de los buffers en la práctica, reasignándolos si fuera necesario cuando llegue un búfer más grande, de modo que no esté bloqueando y liberando búferes con cada callback de los delegates.

En cuanto a su última pregunta, creo que la forma más fácil de hacerlo sería inyectar una input conocida y comprobar que le da la respuesta correcta. Podrías reproducir una onda sinusoidal en el micrófono y comprobar que tu FFT tuviera un pico en el contenedor de frecuencias correcto, algo así.

No sugiero utilizar AVFoundation por 3 razones:

  1. Lo usé para algunas aplicaciones de mina (morsedec, irtty), funciona bien en un simulador y en algún hardware, ¡pero en otros fue totalmente fallido!
  2. No tiene un buen control de la frecuencia de muestreo y el formatting.
  3. la latencia puede ser alta

Sugiero comenzar con el código de ejemplo de apple aurioTouch. Para hacer FFT puede cambiar a vDSP framework utilizando un búfer circular (I LOVE https://github.com/michaeltyson/TPCircularBuffer ).

Espero que esto ayude