使用 FFT 从音频中过滤 hit-hat 节拍

Posted

技术标签:

【中文标题】使用 FFT 从音频中过滤 hit-hat 节拍【英文标题】:Filtering hit-hat beat from an audio with FFT 【发布时间】:2020-12-02 14:34:56 【问题描述】:

所以我正在编写一个节拍检测算法,它工作起来很酷,但它会检测每个节拍(鼓、语音、踩镲等)。 而且我试图只采用踩镲节拍声音。 这是代码的一部分,我正在使用 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 来过滤它(因为踩镲的频率约为 300..3000 Hz)。 虽然,我的节拍算法检测语音、鼓和其他节拍。 如何解决它,我做错了什么?

【问题讨论】:

【参考方案1】:

如果我是你,我会以不同的方式处理它。您现在要做的是过滤可听范围内的频率,但您应该在听不见的范围内过滤,即节拍范围线。 IE。不是“给我低于 300 Hz(每秒少于 300 个周期)的频率”,而是“过滤 40 个周期 每分钟 到 200 个周期 每分钟 之间的频率>,即从 0.6 Hz 到 3.3 Hz 但是你不能为此分析声音信号。您需要先创建一个听不见的“峰值”信号:

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

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

至少需要 2 秒才能检测到高于 0.5 Hz 的频率 尺寸必须很大才能提高低频分辨率

使用这种方法,究竟是什么构成节拍并不重要:它可以是低音鼓,只是基本吉他或钢琴,即节拍制作乐器的频率无关紧要(根据你的方法,在哪里你过滤高频,“只有踩镲”的歌曲不会被检测到

【讨论】:

我正在从原始音频文件创建一个“唯一的踩镲”音频文件,所以我需要知道究竟是什么使节拍。我也不明白,我们在您分析非听觉信号的算法中做了什么。如果可以听到“踩镲”,为什么我们需要这样做? 0..80 间隔在我的情况下是 0..3000 Hz 间隔(44100 Hz 采样频率),这里是踩镲频率所在

以上是关于使用 FFT 从音频中过滤 hit-hat 节拍的主要内容,如果未能解决你的问题,请参考以下文章

从麦克风或音频文件中识别拨号音

SoundTouch实现音频变速变调

检测音频文件中的低频音

AEJoy —— 表达式之音频节拍计数器JS

使用 EZAudio 从 FFT 获得更精确的频率?

从歌曲中获取当前音频频率 - Java(也许使用 FFT?)