核心音频,Goertzel 算法不起作用
Posted
技术标签:
【中文标题】核心音频,Goertzel 算法不起作用【英文标题】:Core-audio, Goertzel algorithm not working 【发布时间】:2011-03-12 18:08:33 【问题描述】:我目前正在创建一个应用程序,它可以通过 iPhone 的麦克风实时计算预定义频率 (16780Hz) 的幅度。
我在缓冲区中有声音数据,我尝试使用 Goertzel(一种为此任务设计的算法)对其进行处理。 Goertzel info。这就是问题的开始。
当录制的声音的频率 (5000Hz) 远低于定义的频率 (16780Hz) 时,该算法会做出非常积极的响应。事实上,结果比录制正确频率的声音时产生的结果要积极得多。
这是我的 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);
对于远低于 16780Hz 的频率,这将返回数百个数字,而对于 16780Hz 的频率,则返回小得多的数字。
我非常沮丧,我们将不胜感激。
【问题讨论】:
【参考方案1】:只是猜测:
根据 Nyquist-Shannon 采样定理,采样率应至少是您尝试测量的频率的两倍。而你的是,但只是勉强。 44.1kHz 的采样率是测量 22kHz 信号的外沿。 16kHz 的信号足够接近混叠可能导致波形分析出现问题的极限。这是一张图片来说明我的观点:
所以,我猜您需要更高的采样率。您为什么不尝试通过算法运行纯 16kHz 正弦波,看看它是否做得更好?如果您在测试数据中只有一个频率,那么混叠将不是问题。如果您从正弦波中获得更高的响应,那么您可能只需要更高的采样率。
【讨论】:
不幸的是,我一直在用正弦波对其进行测试:(。还有其他建议吗?我尝试将采样率加倍无济于事 加倍采样率可能不适用于 iPhone,如果它不能采样那么高。您必须重新读取采样率以确保它被接受。此外,您可能必须先在会话属性中设置采样率,以防止它静默故障转移到它认为应该是本机采样率: 另外,您可以尝试更大的块大小(每个块的样本数量更多)以获得更好的分辨率。再次在会话属性中:kAudioSessionProperty_PreferredHardwareIOBufferDuration (Float32 time in seconds) 最后一点:如果您在模拟器中运行此代码,无论您做什么,它都会默默地将采样率设置为 44100。 我对那个特定的算法不是很熟悉。但是您可以通过手动制作正弦波而不是从硬件读取它来测试麦克风频率响应问题。【参考方案2】:看起来您的 Goertzel 滤波器中使用的谐振器是 1 极谐振器的 1 度近似值。这将大大降低每步高相位角的精度和稳定性。使用对三角函数更好近似的 1-bin DFT 在如此高的频率下可能会更好地工作。
iPhone 麦克风的频率响应可能会在如此高的频率下滚降。
添加:
对于 1-bin 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;
请注意,对于固定频率和采样率,可以在音频回调之外预先计算触发函数并保存在数组或查找表中。如果您不进行这样的优化,在音频回调中调用多个双精度超越函数可能会太慢和/或浪费大量电池电量,但可能会在典型的快速 PC 上模拟 OK。
定义 DFT 的长度是 bin 频率 Freq 的精确整数周期,但其他长度将适用于包含不同数量的所谓频谱“泄漏”和/或扇形误差的近似值。滤波器频率响应的宽度与 DFT 长度大致成反比。此外,频率越接近 Fs/2,DFT 需要越长以避免复杂的图像混叠,也许多个长度为 N*Fs/(Fs/2 - Freq) 的周期会是更好的长度。您可能需要保存或排队样本以获得适当的长度(而不仅仅是使用音频回调给您的缓冲区长度)。
【讨论】:
您能否给我发送一个链接或指针,说明如何执行此操作/实施。 ty 尝试将数据数组乘以所需滤波器频率的正弦波和余弦波,将 2 个向量相加,然后计算 2D 或复数幅度。 非常抱歉,但请你给我举个例子,我的尝试都是灾难性的,我迫切需要这个工作。谢谢以上是关于核心音频,Goertzel 算法不起作用的主要内容,如果未能解决你的问题,请参考以下文章