Core-audio, алгоритм Goertzel не работает - PullRequest
5 голосов
/ 12 марта 2011

В настоящее время я создаю приложение, которое вычисляет величину на предопределенной частоте (16780 Гц) в режиме реального времени с микрофона iPhone.

У меня есть звуковые данные в буфере, и я пытаюсь их обработать, используя Goertzel, алгоритм, разработанный для этой задачи. Информация о Гертцеле . Вот где начинается проблема.

Алгоритм дает очень положительные результаты, когда звук записывается с частотой, намного меньшей (5000 Гц), чем заданная (16780 Гц). На самом деле результат гораздо более положительный, чем тот, который получается при записи звука правильной частоты.

Вот моя реализация goertzel:

double goertzel(unsigned short *sample, int sampleRate, double Freq, int len )
{

double realW = 2.0 * cos(2.0 * M_PI * Freq / sampleRate);
double imagW = 2.0 * sin(2.0 * M_PI * Freq / sampleRate);
double d1 = 0;
double d2 = 0;
int z;
double y;
for (int i = 0; i < len; i++) {
    y=(double)(signed short)sample[i] +realW * d1 - d2;
    d2 = d1;
    d1 = y;
}
double rR = 0.5 * realW *d1-d2;
double rI = 0.5 * imagW *d1-d2;

return (sqrt(pow(rR, 2)+pow(rI,2)))/len;
} /* end function goertzel */

Вот как я могу получить аудио, если оно вообще имеет отношение

-(void)startListeningWithFrequency:(float)frequency;
{
OSStatus status;
//AudioComponentInstance audioUnit;
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;

AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
status = AudioComponentInstanceNew( inputComponent, &audioUnit);
checkStatus(status);

UInt32 flag = 1;
status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,kInputBus, &flag, sizeof(flag));
checkStatus(status);

AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate         = 44100.00;//44100.00;
audioFormat.mFormatID           = kAudioFormatLinearPCM;
audioFormat.mFormatFlags        = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
audioFormat.mFramesPerPacket    = 1;
audioFormat.mChannelsPerFrame   = 1;
audioFormat.mBitsPerChannel     = 16;
//  float
audioFormat.mBytesPerPacket     = 2;
audioFormat.mBytesPerFrame      = 2;

status = AudioUnitSetProperty(audioUnit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Output,
                              kInputBus,
                              &audioFormat, 
                              sizeof(audioFormat));
checkStatus(status);
//status = AudioUnitSetProperty(audioUnit, 
//                            kAudioUnitProperty_StreamFormat, 
//                            kAudioUnitScope_Input, 
//                            kOutputBus, 
//                            &audioFormat, 
//                            sizeof(audioFormat));
checkStatus(status);
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = recordingCallback;
callbackStruct.inputProcRefCon = self;
status = AudioUnitSetProperty(audioUnit, 
                              kAudioOutputUnitProperty_SetInputCallback,
                              kAudioUnitScope_Global,
                              kInputBus, &callbackStruct, sizeof(callbackStruct));
checkStatus(status);
/*  UInt32 shouldAllocateBuffer = 1;
AudioUnitSetProperty(audioUnit, kAudioUnitProperty_ShouldAllocateBuffer, kAudioUnitScope_Global, 1, &shouldAllocateBuffer, sizeof(shouldAllocateBuffer));
*/
status = AudioOutputUnitStart(audioUnit);

}
static OSStatus recordingCallback(void *inRefCon, 
                              AudioUnitRenderActionFlags *ioActionFlags, 
                              const AudioTimeStamp *inTimeStamp, 
                              UInt32 inBusNumber, 
                              UInt32 inNumberFrames, 
                              AudioBufferList *ioData) {
AudioBuffer buffer;

buffer.mNumberChannels = 1;
buffer.mDataByteSize = inNumberFrames * 2;
//NSLog(@"%d",inNumberFrames);
buffer.mData = malloc( inNumberFrames * 2 );

// Put buffer in a AudioBufferList
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0] = buffer;



OSStatus status;
status = AudioUnitRender(audioUnit, 
                         ioActionFlags, 
                         inTimeStamp, 
                         inBusNumber, 
                         inNumberFrames, 
                         &bufferList);  
checkStatus(status);
//double g = calculateGoertzel((const char *)(&bufferList)->mBuffers[0].mData,16789.0,96000.0);
UInt16 *q = (UInt16 *)(&bufferList)->mBuffers[0].mData;
int N = sizeof(q)/sizeof(UInt16);
double Qr,Qi;
double theta = 2.0*M_PI*16780/44100;
double g = goertzel(q,44100,16780,N);

NSLog(@"goertzel:%f", g);
}

Возвращает числа в сотнях для частоты, намного меньшей, чем 16780 Гц, а для частот 16780 Гц - гораздо меньшие.

Я очень расстроен, и помощь будет принята с благодарностью.

Ответы [ 2 ]

3 голосов
/ 12 марта 2011

Похоже, что резонатор, используемый в вашем фильтре Гертцеля, является приближением 1-й степени к 1-полюсному резонатору.Это значительно снизит точность и стабильность при больших фазовых углах на шаг.1-лотковый ДПФ, использующий лучшее приближение к функциям триггера, может работать лучше на таких высоких частотах.

И частотная характеристика микрофона iPhone, вероятно, снижается на таких высоких частотах.

ДОБАВЛЕНО:

Для 1-лоткового ДПФ попробуйте это во внутреннем цикле:

d1 += (double)sample[i] * cos(2.0*M_PI*i*Freq/sampleRate);
d2 += (double)sample[i] * sin(2.0*M_PI*i*Freq/sampleRate);

Затем верните:

dR = d1;
dI = d2;
magnitude = sqrt(dR*dR + dI*dI) / (double)len;

Обратите внимание, что для фиксированной частоты и частоты дискретизацииТриггерные функции могут быть предварительно рассчитаны вне звукового обратного вызова и сохранены в массиве или поисковой таблице.Если вы не выполняете какую-либо оптимизацию, подобную этой, вызов нескольких трансцендентных функций двойной точности внутри вашего звукового обратного вызова может быть слишком медленным и / или тратить много энергии аккумулятора, но может имитировать ОК на типичном быстром ПК.

DFT определен для длины, которая является точным целым числом периодов частоты бина Freq, но другие длины будут работать для приближений, содержащих различное количество так называемых спектральных «утечек» и / или ошибок скальпинга.Ширина частотной характеристики фильтра будет примерно обратно пропорциональна длине ДПФ.Кроме того, чем ближе частота к Fs / 2, тем длиннее должен быть DFT, чтобы избежать сложного наложения изображений, возможно, лучшая длина будет иметь несколько периодов длины N * Fs / (Fs / 2 - Freq).Вам может потребоваться сохранить или поставить в очередь сэмплы, чтобы получить подходящую длину (а не просто использовать длину буфера, предоставленную вам аудио обратным вызовом).

3 голосов
/ 12 марта 2011

Всего лишь предположение:

Согласно теореме выборки Найквиста-Шеннона, частота дискретизации должна быть как минимум вдвое больше частоты, которую вы пытаетесь измерить.И ваш, но едва.Частота дискретизации 44,1 кГц является внешним краем для измерения сигналов 22 кГц.Сигнал 16 кГц достаточно близок к тому пределу, что наложение может вызвать проблемы с вашим анализом волн.Вот иллюстрация, иллюстрирующая мою точку зрения: enter image description here

Итак, я думаю, вам нужна более высокая частота дискретизации.Почему бы вам не попробовать запустить синусоидальную волну с частотой 16 кГц по алгоритму, чтобы понять, будет ли она лучше?Псевдоним не будет проблемой, если у вас есть только одна частота в тестовых данных.Если вы получаете более высокий отклик от синусоидальной волны, то вам, вероятно, просто нужна более высокая частота дискретизации.

...