Передача данных AVCaptureAudioDataOutput в vDSP/Accelerate.framework

Я пытаюсь создать приложение, которое запускает БПФ для данных микрофона, поэтому я могу проверить, например. самая громкая частота на входе.

Я вижу, что существует множество способов получения аудиовхода (службы RemoteIO AudioUnit, AudioQueue и AVFoundation), но кажется, что AVFoundation является самым простым. У меня есть эта установка:

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

(плюс проверка ошибок, опущенная здесь для удобства чтения).

Затем я реализую следующий AVCaptureAudioDataOutputSampleBufferDelegate. метод:

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

Я не уверен, каким должен быть следующий шаг. Что именно описывает формат CMSampleBuffer (и какие предположения можно сделать по этому поводу, если таковые имеются)? Как получить необработанные аудиоданные в vDSP_fft_zrip с наименьшим объемом дополнительной предварительной обработки? (Кроме того, что бы вы порекомендовали сделать, чтобы убедиться, что необработанные данные, которые я вижу, верны?)


person jtbandes    schedule 30.12.2012    source источник


Ответы (2)


CMSampleBufferRef — это непрозрачный тип, который содержит 0 или более образцов мультимедиа. В документах есть небольшая заминка:

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

В этом случае он будет содержать аудиобуфер, а также описание формата сэмпла и информацию о времени и так далее. Если вам действительно интересно, просто поставьте точку останова в обратном вызове делегата и посмотрите.

Первым шагом является получение указателя на буфер данных, который был возвращен:

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

Формат семпла по умолчанию для микрофона iPhone — линейный PCM с 16-битными сэмплами. Это может быть моно или стерео в зависимости от того, есть ли внешний микрофон или нет. Для расчета БПФ нам нужен вектор с плавающей запятой. К счастью, есть функция ускорения, которая сделает преобразование за нас:

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

Теперь у вас есть вектор с плавающей запятой буфера выборки, который вы можете использовать с vDSP_fft_zrip. Кажется невозможным изменить входной формат с микрофона на плавающие сэмплы с помощью AVFoundation, поэтому вы застряли на этом последнем шаге преобразования. На практике я бы сохранил буферы, перераспределяя их при необходимости, когда поступает больший буфер, чтобы вы не выделяли и не освобождали буферы при каждом обратном вызове делегата.

Что касается вашего последнего вопроса, я думаю, самый простой способ сделать это - ввести известный ввод и проверить, дает ли он вам правильный ответ. Вы можете воспроизвести синусоидальную волну в микрофон и проверить, что у вашего БПФ есть пик в правильном диапазоне частот, что-то в этом роде.

person Tark    schedule 31.12.2012
comment
Аудиоформат по умолчанию для микрофона iPhone — это один канал 16-битных целых чисел — откуда берется эта информация? Я обеспокоен тем, что такие предположения будут небезопасными в целом на разных устройствах. - person jtbandes; 01.01.2013
comment
Вы правы, и предположение на самом деле все равно было ложным, я обновил, чтобы проверить аудиоформат. Некоторые комментарии о настройках AVCapture по умолчанию: developer.apple.com/ библиотека/ios/#samplecode/ - person Tark; 02.01.2013

Я не предлагаю использовать AVFoundation по трем причинам:

  1. Я использовал его для некоторых своих приложений (morsedec, irtty), он хорошо работает на симуляторе и на некоторых аппаратных средствах, но в других совершенно не работает!
  2. у вас нет хорошего контроля частоты дискретизации формата.
  3. задержка может быть высокой.

Я предлагаю начать с примера кода aurioTouch от Apple. Чтобы сделать БПФ, вы можете перейти на платформу vDSP, используя кольцевой буфер (мне нравится https://github.com/michaeltyson/TPCircularBuffer ).

Надеюсь, это поможет

person jackdev23    schedule 08.02.2013