continuity problem when applying IIR filters on continuous timeframes
I want to apply a FIR or IIR filter on a continuous block/timeframe of 1024 samples (example: low-pass filter).
Possible applications:
Real-time audio processing, such as EQing. At precise time, there are only the next 1024 samples in the buffer. The next sample to be processed is not yet available (live).
As suggested, make a cutoff time-varying filter in this answer
I’ve tried this here :
import numpy as np
from scipy.io import wavfile
from scipy.signal import butter, lfilter, filtfilt, firwin
sr, x = wavfile.read('input.wav')
x = np.float32(x)
y = np.zeros_like(x)
N = 1024 # buffer block size = 23ms for a 44.1 Khz audio file
f = 1000 # cutoff
pos = 0 # position
while True:
b, a = butter(2, 2.0 * f / sr, btype='low')
y[pos:pos+N] = filtfilt(b, a, x[pos:pos+N])
pos += N
f -= 1 # cutoff decreases of 1 hz every 23 ms, but the issue described here also present with constant cutoff!
print f
if pos+N > len(x):
break
y /= max(y) # normalize
wavfile.write('out_fir.wav', sr, y)
I tried:
Both use the Butterworth filter or FIR (replace the previous line with
b, a = firwin(1000, cutoff=f, fs=sr), 1.0
).All with >lfilter and
filtfilt
( The advantage of the latter is that the filter can be applied forward and backward, which solves the phase problem).
But the question is:
At the boundary of each timeframe output, there is a continuity problem, which causes the audio signal to be severely distorted.
How to solve this discontinuous problem? The windowing+OverlapAdd approach came to mind, but there are definitely simpler ways.
Solution
As mentioned in @sobek comment, of course it is necessary to specify initial conditions to allow continuity. This is done via lfilter's
zi
parameter.
The problem is solved by changing the main loop:
while True:
b, a = butter(2, 2.0 * f / sr, btype='low')
if pos == 0:
zi = lfilter_zi(b, a)
y[pos:pos+N], zi = lfilter(b, a, x[pos:pos+N], zi=zi)
pos += N
f -= 1
if pos+N > len(x):
break
This seems to work even if the filter’s cutoff value (and therefore a
and b
) is modified on each iteration.