5 votes

Passage des données AVCaptureAudioDataOutput dans vDSP / Accelerate.framework

J'essaie de créer une application qui exécute une FFT sur les données d'un microphone, afin de pouvoir examiner, par exemple, la fréquence la plus élevée dans l'entrée.

Je constate qu'il existe plusieurs méthodes pour obtenir une entrée audio (AudioUnit RemoteIO, services AudioQueue et AVFoundation), mais il semble que AVFoundation soit la plus simple. J'ai la configuration suivante :

// Configure the audio session
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryRecord error:NULL];
[session setMode:AVAudioSessionModeMeasurement error:NULL];
[session setActive:YES error:NULL];

// Optional - default gives 1024 samples at 44.1kHz
//[session setPreferredIOBufferDuration: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];

(plus le contrôle des erreurs, omis ici pour des raisons de lisibilité).

Je mets ensuite en œuvre ce qui suit AVCaptureAudioDataOutputSampleBufferDelegate méthode :

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

Je ne sais pas quelle est la prochaine étape. Que fait exactement le CMSampleBuffer décrit (et quelles hypothèses peuvent être formulées à son sujet, le cas échéant) ? Comment dois-je obtenir les données audio brutes dans le format vDSP_fft_zrip avec le moins possible de prétraitement supplémentaire ? (Par ailleurs, que recommanderiez-vous de faire pour vérifier que les données brutes que je vois sont correctes ?)

6voto

Tark Points 3264

En CMSampleBufferRef est un type opaque qui contient 0 ou plusieurs échantillons de média. Il y a un peu de blurb dans la documentation :

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

Dans ce cas, il contiendra une mémoire tampon audio, ainsi que la description du format de l'échantillon et des informations de synchronisation, etc. Si vous êtes vraiment intéressé, placez un point d'arrêt dans le callback du délégué et jetez-y un coup d'œil.

La première étape consiste à obtenir un pointeur sur le tampon de données qui a été renvoyé :

// 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);

Le format d'échantillonnage par défaut pour le micro de l'iPhone est PCM linéaire, avec des échantillons de 16 bits. Il peut être mono ou stéréo selon qu'il y a un micro externe ou non. Pour calculer la FFT, nous avons besoin d'un vecteur flottant. Heureusement, il existe une fonction accélérée qui effectue la conversion pour nous :

// 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 required
}

Vous disposez maintenant d'un vecteur flottant du tampon d'échantillonnage que vous pouvez utiliser avec vDSP_fft_zrip . Il ne semble pas possible de changer le format d'entrée du microphone en échantillons flottants avec AVFoundation Vous devez donc vous contenter de cette dernière étape de conversion. Je garderais les tampons en pratique, en les réallouant si nécessaire lorsqu'un tampon plus grand arrive, de sorte que vous ne soyez pas en train de malloquer et de libérer des tampons à chaque rappel de délégué.

Pour ce qui est de votre dernière question, je pense que la manière la plus simple de procéder serait d'injecter une entrée connue et de vérifier qu'elle donne une réponse correcte. Vous pourriez jouer une onde sinusoïdale dans le micro et vérifier que votre FFT a un pic dans la bonne bande de fréquence, quelque chose comme ça.

1voto

jackdev23 Points 116

Je ne suggère pas d'utiliser AVFoundation pour 3 raisons :

  1. Je l'ai utilisé pour certaines de mes applications (morsedec , irtty), il fonctionne bien sur le simulateur et dans certains matériels, mais dans d'autres il a totalement échoué !
  2. vous n'avez pas un bon contrôle de la fréquence d'échantillonnage et du format.
  3. la latence pourrait être élevée.

Je vous suggère de commencer par l'exemple de code aurioTouch d'Apple. Pour faire de la FFT, vous pouvez passer au framework vDSP en utilisant un buffer circulaire (j'AIME https://github.com/michaeltyson/TPCircularBuffer ).

J'espère que cela vous aidera

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X