小块音频的低通滤波

信息处理 声音的 低通滤波器
2022-02-11 14:53:00

我有一个脚本(Python),可以将 44.1 kHz 实时音频切割成 10 ms 的块,处理每个块,然后将这些块缝合在一起而不会添加任何伪影。我正在尝试创建一个对每个块进行操作的低通滤波器,但是我尝试过的每个滤波器都会引入不需要的伪像。

我尝试过创建各种类型的过滤器,包括 IIR(Butterworth)和 FIR(Hann、Hamming、矩形窗口等)和 Remez。尝试将滤波器应用于输入信号的方法包括直接传递函数、卷积和前向后向滤波器(即 scipy.signal.filtfilt)。我也尝试过移动平均方法,但我不知道在块的端点附近做什么。

有没有专为这种应用设计的过滤器?

INPUT WAVE (lots of harmonic content)

|<-- 1 CHUNK  -->|
:                :                :                :
:                :                :                :
: -----       ---:-       -----   :   -----       -:
:|     |     |   : |     |     |  :  |     |     | :
:|     |     |   : |     |     |  :  |     |     | :
:|     |     |   : |     |     |  :  |     |     | :
:       -----    :  -----       --:--       -----  :
:                :                :                :
        V                V                 V
        V                V                 V
     FILTER            FILTER            FILTER
        V                V                 V
        V                V                 V
:                :                :                :
:   ---         -:-         ---   :     ---        :
:  /   \       / : \       /   \  :    /   \       :
: /     \     /  :  \     /     \ :   /     \     /:
:/       \   /   :   \   /       \:  /       \   / :
:         ---    :    ---         :--         ---  :
:                :                :                :

OUTPUT WAVE (less harmonic content)

编辑:根据@hotpaw2 的回答,我希望先尝试 IIR 方法(我是这方面的新手,但我的直觉告诉我先尝试 IIR)。出于讨论的目的,让我们定义这些数组:

B = transfer function numerator coefficients
A = transfer function denominator coefficients
I0 = previous input array (same size)
I1 = current input array (441 samples)
R0 = previous output, post-filtered (DO WE NEED?)
R1 = current output, desired post-filtered result array

我是仅从 I0 生成初始条件,还是需要将 R0 计算在内?换一种方式问,这是我正在尝试的 Python 代码:

initCon = scipy.signal.filtic(B, A, I0)

一旦我有了这组初始条件,它们是直接应用于过滤器,还是我需要对它们做更多的事情?同样,Python 代码:

R1, _ = scipy.signal.lfilter(B, A, I1, z = initCon)

就目前的代码而言,它可以执行,但有很多非频率嗡嗡声。

编辑2:只是想我会用正弦波输入发布滤波器的输出。很明显,每 441 个样本(每个块的开头)都会出现问题: 失真正弦波

3个回答

过滤数据块通常会产生比块大小适合的更多数据。如果您丢弃这些添加的数据,则会产生跨块的伪影。

对于 FIR 滤波器,您需要在滤波前用至少滤波器的脉冲响应长度填充每个块或块 (>= N+M-1)。然后使用重叠添加或重叠保存(FFT 快速卷积算法)来处理来自添加填充的卷积的所有额外结果(通常将它们传递到下一个或多个块)。否则,您要么在每个块中获得循环卷积伪影,要么在将过滤器应用于每个块时丢弃部分过滤器响应。

对于 IIR 过滤器,您需要在块之间传递过滤器内部状态,而不是从零或其他默认的 IIR 初始状态重新开始。该状态仍然从过滤结果中携带能量,如果将其丢弃,则会在块之间出现故障。

这篇文章解决了我的问题: https ://stackoverflow.com/questions/58014131/implementing-blockwise-low-pass-iir-filter-in-python

在帖子中,Warren Weckesser 将我引向他自己关于该主题的论文,在那里我读到 SOS 过滤器是我需要的解决方案,如果我要使用 IIR 过滤器。同一篇论文有一个 Python 代码示例,它帮助我理解了如何正确处理初始条件。

这有效,但我不知道为什么!初始条件是将 Z (我不明白 Z 的目的,以及为什么 lfiltic() 根本不起作用)与前一个结果中的最后几个值相乘的结果。请注意,最后两个元素的顺序相反,这可能说明了一些问题。

Bparam, Aparam = signal.iirfilter(2, 0.020, btype = 'lowpass', analog =
       False, ftype = 'butter')       # 2nd order Butterworth coefficients

Z = signal.lfilter_zi(Bparam, Aparam) # Part of the init conditions calc

IC = Z * (prevSignal[::-1])[0:2]      # Reverse prevSignal and then grab
                                      #   only the last two elements

filteredSignal, _ = signal.lfilter(Bparam, Aparam, inputSignal, zi = IC)
                                      # Result is continuous and clear

prevSignal = filteredSignal           # Save for the next pass

但是,这仅适用于二阶低通滤波器。如果我更改顺序(例如第 4 顺序),工件会显着地重新出现。是否有一种更一般的形式实际上适用于更高阶的过滤器?