Problema de continuidad al aplicar un filtro IIR en marcos temporales sucesivos

Me gustaría aplicar un filtro FIR o IIR (ejemplo: filtro de paso bajo) en bloques / marcos de tiempo sucesivos de 1024 muestras cada uno.

Posibles aplicaciones:

  • Procesamiento de audio en tiempo real , como EQing. En un momento preciso, solo tenemos las siguientes 1024 muestras en un búfer. Las siguientes muestras a procesar todavía no están disponibles (en tiempo real).

  • haga un filtro variable de tiempo de corte dividiendo la señal de entrada en bloques, como se sugiere en esta respuesta .

Intenté esto aquí

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) 

Lo intenté:

  • ambos con un filtro Butterworth o un FIR (reemplace la línea anterior por b, a = firwin(1000, cutoff=f, fs=sr), 1.0 )

  • ambos con lfilter y filtfilt (este último tiene la ventaja de aplicar el filtro hacia adelante y hacia atrás, y esto resuelve los problemas de la fase),

Pero aquí está el problema:

** En los límites de cada salida de marcos de tiempo, hay un problema de continuidad, que hace que la señal de audio sea muy distorsionada.

¿Cómo resolver este problema de discontinuidad? Pensé en el método de ventanas + OverlapAdd, pero seguramente debe haber una forma más fácil.

introduzca la descripción de la imagen aquí

Como lo menciona @sobek en un comentario, es necesario, por supuesto, especificar las condiciones iniciales para permitir la continuidad. Esto se hace con el parámetro zi de lfilter .

El problema se resuelve cambiando el bucle principal mediante:

 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 

Esto parece funcionar incluso si el corte del filtro (y, por lo tanto, la a y la b ) se modifica en cada iteración.