我正在用 c89 编写一个简单的 dsp 库。使用此版本的语言以移植到旧机器是一个目标。我正在对我的库进行单元测试,想测量每个滤波器的振幅响应。我创建了以下使用 FFTW 库计算振幅响应的方法。
#include "algaec.h"
#include <fftw3.h>
void algae__biquad_compute_amplitude_response(algae__sample_t *amplitude_response,
const size_t number_of_bins,
algae__biquad_t *filter,
const algae__frequency_t sample_rate,
const size_t blocksize) {
algae__sample_block_empty(amplitude_response, blocksize);
enum complex { RE, IM };
const size_t N = 2 * number_of_bins;
fftw_complex *in;
fftw_complex *out;
fftw_plan p;
in = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
p = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
algae__sample_t impulse[blocksize];
algae__sample_block_empty(impulse, blocksize);
impulse[0] = 1;
algae__biquad_process(filter, impulse, impulse, blocksize);
size_t idx;
for (idx = 0; idx < N; idx++) {
in[idx][RE] = impulse[idx];
in[idx][IM] = 0;
}
fftw_execute(p);
for (idx = 0; idx < number_of_bins; idx++) {
amplitude_response[idx] =
(sqrt(out[idx][RE] * out[idx][RE] + out[idx][IM] * out[idx][IM]));
}
fftw_destroy_plan(p);
fftw_free(in);
fftw_free(out);
}
我还想测试一个单极 IIR 滤波器。为此,我现在需要定义以下内容:
void algae__onepole_compute_amplitude_response(algae__sample_t *amplitude_response,
const size_t number_of_bins,
algae__onepole_t *filter,
const algae__frequency_t sample_rate,
const size_t blocksize) {
algae__sample_block_empty(amplitude_response, blocksize);
enum complex { RE, IM };
const size_t N = 2 * number_of_bins;
fftw_complex *in;
fftw_complex *out;
fftw_plan p;
in = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
p = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
algae__sample_t impulse[blocksize];
algae__sample_block_empty(impulse, blocksize);
impulse[0] = 1;
algae__onepole_process(filter, impulse, impulse, blocksize);
size_t idx;
for (idx = 0; idx < N; idx++) {
in[idx][RE] = impulse[idx];
in[idx][IM] = 0;
}
fftw_execute(p);
for (idx = 0; idx < number_of_bins; idx++) {
amplitude_response[idx] =
(sqrt(out[idx][RE] * out[idx][RE] + out[idx][IM] * out[idx][IM]));
}
fftw_destroy_plan(p);
fftw_free(in);
fftw_free(out);
}
最终我会有更多的过滤器类型。我想测试所有这些的振幅响应,以确保我正确地实现了它们。如果我使用的是 C++,我可以使用模板使
algae__compute_amplitude_response
和 algae__process
在过滤器类型方面通用化,并让编译器自动生成正确的实现,同时我只需要维护一个代码路径。有什么办法可以避免在这里重复我自己吗?
到目前为止,我已经研究了以下选项。他们似乎都没有吸引力......
process
方法和一个通用的compute_amplitude_response
方法......我不完全清楚这将如何工作并担心它会对我的生产代码的可读性产生什么影响。process
方法来识别结构的类型,然后重定向到正确的方法......为了测试,这会影响我的生产代码的结构(不一定会破坏交易,但有点难过)。它还会影响我的生产代码的实现和性能,因为联合结构将占用与其最大成员相同的内存量。对于单极滤波器与双二阶滤波器,这是 2 个浮点数和 9 个浮点数之间的区别。我在这里缺少任何选项吗?-
您可以使用函数指针来做到这一点,但首先您需要确保相关函数具有相同的签名类型。
首先,更改
algae__onepole_process
和 algae__biquad_process
,使每个参数的第一个参数具有 void *
类型,即:
void algae__onepole_process(void *filter, algae__sample_t *impulse1,
algae__sample_t *impulse2, size_t blocksize);
void algae__biquad_process(void *filter, algae__sample_t *impulse1,
algae__sample_t *impulse2, size_t blocksize);
这意味着您需要将每个中的
filter
参数复制到适当类型的指针。
然后创建一个函数来完成上述两个操作,将
filter
参数的类型更改为 void *
并为要调用的过程函数添加函数指针:
void algae__generic_compute_amplitude_response(algae__sample_t *amplitude_response,
const size_t number_of_bins,
void (*process)(void *, algae__sample_t *, algae__sample_t *, size_t),
void *filter,
const algae__frequency_t sample_rate,
const size_t blocksize) {
然后将使用函数指针而不是特定函数:
process(filter, impulse, impulse, blocksize);