手动重新实现Biquad IIR Filter的vDSP

编程入门 行业动态 更新时间:2024-10-27 06:25:33
手动重新实现Biquad IIR Filter的vDSP_deq22(Reimplement vDSP_deq22 for Biquad IIR Filter by hand)

我将目前使用苹果专用(Accelerate)vDSP函数vDSP_deq22的过滤器移植到Android(Accelerate不可用)。 滤波器组是一组带通滤波器,每个带通滤波器都返回各自频带的均方根值。 目前代码(ObjectiveC ++,改编自NVDSP)如下所示:

- (float) filterContiguousData: (float *)data numFrames:(UInt32)numFrames channel:(UInt32)channel { // Init float to store RMS volume float rmsVolume = 0.0f; // Provide buffer for processing float tInputBuffer[numFrames + 2]; float tOutputBuffer[numFrames + 2]; // Copy the two frames we stored into the start of the inputBuffer, filling the rest with the current buffer data memcpy(tInputBuffer, gInputKeepBuffer[channel], 2 * sizeof(float)); memcpy(tOutputBuffer, gOutputKeepBuffer[channel], 2 * sizeof(float)); memcpy(&(tInputBuffer[2]), data, numFrames * sizeof(float)); // Do the processing vDSP_deq22(tInputBuffer, 1, coefficients, tOutputBuffer, 1, numFrames); vDSP_rmsqv(tOutputBuffer, 1, &rmsVolume, numFrames); // Copy the last two data points of each array to be put at the start of the next buffer. memcpy(gInputKeepBuffer[channel], &(tInputBuffer[numFrames]), 2 * sizeof(float)); memcpy(gOutputKeepBuffer[channel], &(tOutputBuffer[numFrames]), 2 * sizeof(float)); return rmsVolume; }

如此处所示,deq22通过递归函数对给定的输入向量执行双二阶滤波器。 这是从文档中描述的功能:

A =:单精度实数输入向量 IA =:步伐为A. B =:5个单精度输入(滤波器系数),步长为1。 C =:单精度实数输出向量。 IC =:步长C. N =:要生产的新输出元素的数量。

这就是我迄今为止的内容(它在Swift中,就像我已经在Android上运行的其他代码库一样):

// N is fixed on init to be the same size as buffer.count, below // 'input' and 'output' are initialised with (N+2) length and filled with 0s func getFilteredRMSMagnitudeFromBuffer(var buffer: [Float]) -> Float { let inputStride = 1 // hardcoded for now let outputStride = 1 input[0] = input[N] input[1] = input[N+1] output[0] = output[N] output[1] = output[N+1] // copy the current buffer into input input[2 ... N+1] = buffer[0 ..< N] // Not sure if this is neccessary, just here to duplicate NVDSP behaviour: output[2 ... N+1] = [Float](count: N, repeatedValue: 0)[0 ..< N] // Again duplicating NVDSP behaviour, can probably just start at 0: var sumOfSquares = (input[0] * input[0]) + (input[1] * input[1]) for n in (2 ... N+1) { let sumG = (0...2).reduce(Float(0)) { total, p in return total + input[(n - p) * inputStride] * coefficients[p] } let sumH = (3...4).reduce(Float(0)) { total, p in return total + output[(n - p + 2) * outputStride] * coefficients[p] } let filteredFrame = sumG - sumH output[n] = filteredFrame sumOfSquares = filteredFrame * filteredFrame } let meanSquare = sumOfSquares / Float(N + 2) // we added 2 values by hand, before the loop let rootMeanSquare = sqrt(meanSquare) return rootMeanSquare }

虽然这个滤波器给deq22提供了一个不同的幅度输出,并且它似乎有一个周期性的wurbling循环“噪声”(具有恒定的输入音调,频率的幅度上升和下降)。

我已经检查确保每个实现之间的系数数组是相同的。 实际上,每个滤波器似乎都“起作用”,因为它可以获得正确的频率(并且只有那个频率),这就是这种抽运,而且RMS幅度输出比vDSP要低很多,通常是几个数量级:

Naive | vDSP 3.24305e-06 0.000108608 1.57104e-06 5.53645e-05 1.96445e-06 4.33506e-05 2.05422e-06 2.09781e-05 1.44778e-06 1.8729e-05 4.28997e-07 2.72648e-05

任何人都可以看到我的逻辑问题?

编辑:这里是一个440Hz音调的结果的GIF视频。 各种绿色的酒吧是个别的过滤带。 第三频段(如图所示)是调谐到440Hz的频段。

正如预期的那样,NVDSP版本仅显示与输入音量成比例的恒定(非波动)幅度读数。

I'm porting a filterbank that currently uses the Apple-specific (Accelerate) vDSP function vDSP_deq22 to Android (where Accelerate is not available). The filterbank is a set of bandpass filters that each return the RMS magnitude for their respective band. Currently the code (ObjectiveC++, adapted from NVDSP) looks like this:

- (float) filterContiguousData: (float *)data numFrames:(UInt32)numFrames channel:(UInt32)channel { // Init float to store RMS volume float rmsVolume = 0.0f; // Provide buffer for processing float tInputBuffer[numFrames + 2]; float tOutputBuffer[numFrames + 2]; // Copy the two frames we stored into the start of the inputBuffer, filling the rest with the current buffer data memcpy(tInputBuffer, gInputKeepBuffer[channel], 2 * sizeof(float)); memcpy(tOutputBuffer, gOutputKeepBuffer[channel], 2 * sizeof(float)); memcpy(&(tInputBuffer[2]), data, numFrames * sizeof(float)); // Do the processing vDSP_deq22(tInputBuffer, 1, coefficients, tOutputBuffer, 1, numFrames); vDSP_rmsqv(tOutputBuffer, 1, &rmsVolume, numFrames); // Copy the last two data points of each array to be put at the start of the next buffer. memcpy(gInputKeepBuffer[channel], &(tInputBuffer[numFrames]), 2 * sizeof(float)); memcpy(gOutputKeepBuffer[channel], &(tOutputBuffer[numFrames]), 2 * sizeof(float)); return rmsVolume; }

As seen here, deq22 implements a biquad filter on a given input vector via a recursive function. This is the description of the function from the docs:

A =: Single-precision real input vector IA =: Stride for A. B =: 5 single-precision inputs (filter coefficients), with stride 1. C =: Single-precision real output vector. IC =: Stride for C. N =: Number of new output elements to produce.

This is what I have so far (it's in Swift, like the rest of the codebase that I already have running on Android):

// N is fixed on init to be the same size as buffer.count, below // 'input' and 'output' are initialised with (N+2) length and filled with 0s func getFilteredRMSMagnitudeFromBuffer(var buffer: [Float]) -> Float { let inputStride = 1 // hardcoded for now let outputStride = 1 input[0] = input[N] input[1] = input[N+1] output[0] = output[N] output[1] = output[N+1] // copy the current buffer into input input[2 ... N+1] = buffer[0 ..< N] // Not sure if this is neccessary, just here to duplicate NVDSP behaviour: output[2 ... N+1] = [Float](count: N, repeatedValue: 0)[0 ..< N] // Again duplicating NVDSP behaviour, can probably just start at 0: var sumOfSquares = (input[0] * input[0]) + (input[1] * input[1]) for n in (2 ... N+1) { let sumG = (0...2).reduce(Float(0)) { total, p in return total + input[(n - p) * inputStride] * coefficients[p] } let sumH = (3...4).reduce(Float(0)) { total, p in return total + output[(n - p + 2) * outputStride] * coefficients[p] } let filteredFrame = sumG - sumH output[n] = filteredFrame sumOfSquares = filteredFrame * filteredFrame } let meanSquare = sumOfSquares / Float(N + 2) // we added 2 values by hand, before the loop let rootMeanSquare = sqrt(meanSquare) return rootMeanSquare }

The filter gives a different magnitude output to deq22 though, and seems to have a cyclic wurbling circular 'noise' in it (with a constant input tone, that frequency's magnitude pumps up and down).

I've checked to ensure the coefficients arrays are identical between each implementation. Each filter actually seems to "work" in that it picks up the correct frequency (and only that frequency), it's just this pumping, and that the RMS magnitude output is a lot quieter than vDSP's, often by orders of magnitude:

Naive | vDSP 3.24305e-06 0.000108608 1.57104e-06 5.53645e-05 1.96445e-06 4.33506e-05 2.05422e-06 2.09781e-05 1.44778e-06 1.8729e-05 4.28997e-07 2.72648e-05

Can anybody see an issue with my logic?

Edit: here is a gif video of the result with a constant 440Hz tone. The various green bars are the individual filter bands. The 3rd band (shown here) is the one tuned to 440Hz.

The NVDSP version just shows a constant (non-fluctuating) magnitude reading proportional to the input volume, as expected.

最满意答案

好的,行sumOfSquares = filteredFrame * filteredFrame应该是a += ,而不是赋值。 所以只计算最后一帧,解释很多;)

如果你想在Swift中进行一些biquad过滤,请随意使用它。 MIT之前的许可证就像NVDSP一样。

Ok, the line sumOfSquares = filteredFrame * filteredFrame should be a +=, not an assignment. So only the last frame was being calculated, explains a lot ;)

Feel free to use this if you want to do some biquad filtering in Swift. MIT Licence like NVDSP before it.

更多推荐

本文发布于:2023-08-05 11:44:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1432302.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:IIR   Biquad   vDSP   Filter

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!