用FFT过滤音频中的热门节拍



所以我正在编写一个节拍检测算法,它很酷,但它可以检测每一个节拍(鼓、声音、嗨帽等(。我试着只听到一个嗨帽节拍的声音。这是代码的一部分,我正在使用FFT并尝试对其进行过滤:

for (int channel = 0; channel < numChannels; ++channel) {
for (int j = k * smallbuf_samples; j < (k + 1) * smallbuf_samples; ++j) {
smallbuffer[channel].push_back(bigbuffer[channel][j]);
}
}
fftw_complex x[smallbuf_samples];
fftw_complex y[smallbuf_samples];
for (int i = 0; i < smallbuf_samples; ++i) {
x[i][REAL] = smallbuffer[0][i];
x[i][IMAG] = smallbuffer[1][i];
}
fftw_plan plan = fftw_plan_dft_1d(smallbuf_samples, x, y, FFTW_FORWARD, FFTW_ESTIMATE);
fftw_execute(plan);
fftw_destroy_plan(plan);
fftw_cleanup();
std::vector<double> b;
for (int i = 80; i < smallbuf_samples; ++i) {
y[i][REAL] = 0;
y[i][IMAG] = 0;
}
for (int i = 0; i < smallbuf_samples; ++i) {
b.push_back(y[i][REAL] * y[i][REAL] + y[i][IMAG] * y[i][IMAG]);
}
for (int i = 0; i < smallbuf_samples / very_smallbuf_samples; ++i) {
double sum = 0;
int j;
for (j = i*(i+1)/2 * 108/13 + 22/13; j < (i+1)*(i+2)/2 * 108/13 + 22/13 && j < smallbuf_samples; ++j) {
sum += b[j];
}
Es[k].push_back((float) (j - (i*(i+1)/2 * 108/13 + 22/13)) / (float) smallbuf_samples * sum);
}
for (int channel = 0; channel < numChannels; ++channel) {
smallbuffer[channel].clear();
}

所以,正如你所看到的,我通过将所有高于80的y样本指数设置为0来过滤它(因为hi-hat的频率大约是300..3000Hz(。尽管如此,我的节拍算法可以检测声音、鼓和其他节拍。如何修复它,我做错了什么?

如果我是你,我会采取不同的方式。你现在要做的是过滤一个听得见的范围内的频率,但你应该过滤一个听不见的范围,即节拍范围线。即不是";给我小于300Hz(小于每秒300个周期(的频率";,但是";将每分钟40个周期之间的频率过滤到,例如,每分钟200个周期,这是从0.6 Hz到3.3 Hz的,但您无法分析音频信号。你需要创建一个听不见的";峰值";信号优先:

  • 通过信号,只取峰值,构建第二个信号(它将听不见,因为频率太低,即使你能听到它,它对你的耳朵也没有任何意义(
  • 用FFT分析得到的信号,设置为较低的频率范围(比如说,比你用来分析音频信号的20-20000慢128倍,所以你有0.15-150赫兹的结果(
  • 将其滤波至0.6至3 Hz
  • 找到这个范围内最大的峰值(或者最小的峰值——在这里你需要进行实验(。这将是你的节拍。将其乘以60将Hz转换为BPM

当然,FFT的窗口必须比音频信号慢得多,这里必须是:

  • 至少2秒以检测0.5 Hz以上的频率
  • 尺寸必须很大才能在较低频率下提高分辨率

使用这种方法,节拍的确切组成并不重要:它可以是低音鼓,也可以是基础吉他或钢琴,也就是说,节拍制作工具的频率无关紧要(使用这种方法过滤高频,"只有高帽子"的歌曲将不会被检测到