2011-03-12 50 views
5

我目前正在創建一個應用程序,它可以從iPhone的麥克風實時預定義頻率(16780Hz)處的幅度。Core-audio,Goertzel算法不起作用

我有一個緩衝區中的聲音數據,我試圖使用Goertzel來處理它,這是一個爲此任務設計的算法。 Goertzel info。這是問題開始的地方。

當錄製的聲音比定義的(16780Hz)低得多(5000Hz)時,該算法的響應速度非常快。實際上,當記錄正確頻率的聲音時,其結果遠比正確的結果好。

這是我實現策爾的:

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

這將返回在數百個頻數比16780Hz低得多,而對於16780Hz的頻率,返回的數字要小得多。

我非常沮喪和幫助將不勝感激。

回答

3

只是一個猜測:

按照採樣定理,採樣率至少應爲您試圖測量頻率的兩倍。你的是,但幾乎沒有。 44.1kHz的採樣率是測量22kHz信號的外緣。 16kHz的信號非常接近極限,以至於混疊可能會導致波形分析出現問題。這裏有一張圖片來說明我的觀點: enter image description here

所以,我猜你需要更高的採樣率。爲什麼不嘗試在算法中運行純正的16kHz正弦波,看看它是否更好?如果測試數據中只有一個頻率,那麼混疊將不再成爲問題。如果你從正弦波得到更高的響應,那麼你可能只需要更高的採樣率。

+0

不幸的是,它一直只是正弦波,我一直在測試它:(還有什麼建議嗎?我嘗試了雙倍採樣率無果 – 123hal321 2011-03-12 18:50:32

+0

如果不能在iPhone上採樣率加倍,你必須重新讀取採樣率以確保它被接受,而且,你可能必須首先在會話屬性中設置採樣率,以防止它悄悄地故障轉移到它認爲應該是的原生採樣率:​​ – Vagrant 2011-03-12 19:20:05

+0

Float64 preferredSampleRate = audioFormat-> mSampleRate; XThrowIfError(AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate, 的sizeof(preferredSampleRate),preferredSampleRate)); – Vagrant 2011-03-12 19:22:24

2

看起來您的Goertzel濾波器中使用的諧振器是1極點諧振器的1度近似值。這將大大降低每步高相位角的精度和穩定性。使用更好的逼近trig函數的1-bin DFT可能在這樣的高頻率下工作得更好。

而iPhone麥克風的頻率響應可能會在如此高的頻率下降。

新增:

對於1斌DFT,試試這個在您的內循環:

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; 

注意,對於一個固定的頻率和採樣率trig函數可以在音頻回調之外預先計算並保存在數組或查找表中。如果你不這樣做一些優化,在你的音頻回調中調用多個雙精度超越函數可能會太慢和/或浪費大量的電池電量,但是可以在典型的快速PC上模擬OK。

DFT定義爲一個長度,該長度是二進制頻率Freq週期的精確整數值,但其他長度可用於包含所謂的頻譜「泄漏」和/或扇形誤差的不同量的近似值。濾波器頻率響應的寬度將與DFT長度大致成反比。此外,頻率越接近Fs/2,爲了避免複雜的圖像混疊,DFT所需的時間越長,可能需要多個長度爲N * Fs /(Fs/2 - Freq)的週期纔是更好的長度。您可能需要保存或排隊樣本以獲得適當的長度(而不僅僅是使用音頻回調給定的緩衝區長度)。

+0

你可以給我一個鏈接或指針如何做到這一點請/執行。 ty – 123hal321 2011-03-12 23:25:57

+1

嘗試用您希望的濾波器頻率的正弦波和餘弦波將您的數據陣列相乘,對2個矢量進行求和,然後計算2D或複數幅度。 – hotpaw2 2011-03-13 01:41:59

+0

林非常抱歉,但請你能給我一個例子,我的嘗試都是災難性的,我非常需要這個工作。謝謝 – 123hal321 2011-03-13 07:22:38