Ir al contenido

Bancos de filtros

Normas aplicables: IEC 61260ANSI S1.11ISO 266Referencias: Oppenheim y Schafer 2010

phonometry ofrece varios tipos de filtro, cada uno con su propia función de transferencia característica. Butterworth, Chebyshev II y Bessel sitúan sus puntos de −3 dB en los bordes de banda de ANSI S1.11, de modo que sus niveles por banda son directamente comparables. Los dos diseños de rizado constante (Chebyshev I, Elíptico) no: toman esos bordes como borde de rizado, lo que ensancha la banda y sesga todos los niveles por banda en unas décimas de decibelio fijas — ver el apartado 1, porque el sesgo es sistemático y no se promedia a cero.

Al terminar esta página sabrás diseñar un banco para cualquier fracción , leer todos los argumentos en una sola tabla, encadenar secciones de EQ RBJ, extraer las señales temporales por banda y filtrar con fase cero. El apartado 1 son las matemáticas sobre las que se apoya el diseño; si solo quieres llamar a la función, empieza por el apartado 2. En qué se diferencian las cinco arquitecturas es la Galería de arquitecturas de filtro, y qué significa «clase 1» para el resultado es Verificación de clase de filtros.

1. Bandas de fracción de octava: las matemáticas

Sección titulada «1. Bandas de fracción de octava: las matemáticas»

IEC 61260-1:2014 construye cada banda a partir de la razón de octava en base 10 (es decir, «una octava» no es exactamente 2). Para la fracción de banda , las frecuencias centrales y los bordes de banda siguen (5.2-5.5):

de modo que cada banda de tercio de octava abarca : diez bandas por década, y por eso las frecuencias nominales (25, 31,5, 40 …) se repiten escaladas por 10. phonometry diseña cada banda como una cascada SOS sobre esos bordes, pero dónde acaba cayendo el punto de −3 dB depende de qué entiende cada arquitectura por su frecuencia de diseño, y solo tres de las cinco lo ponen en y .

Las dos formas existen para que la frecuencia de referencia de 1000 Hz sea una frecuencia central de banda cuando es impar y un borde de banda cuando es par. Eso es lo que mantiene anidadas las fracciones impares (cada banda de octava son exactamente tres bandas de tercio de octava) mientras que las pares parten justo en las fronteras de octava; las fórmulas de los bordes son las mismas en ambos casos. La consecuencia práctica es fácil de pisar: un espectro con fraction=2, 12 o 24 no tiene banda de 1 kHz en absoluto, sus etiquetas nominales salen del redondeo a cifras significativas del Anexo E.3 y no de la serie preferente de ISO 266, y compararlo banda a banda con una tabla de tercios de octava no significa nada.

Dónde cae de verdad el punto de −3 dB de cada arquitectura

Sección titulada «Dónde cae de verdad el punto de −3 dB de cada arquitectura»

La frecuencia de diseño de SciPy para Butterworth es su punto de −3 dB, así que no necesita corrección. Chebyshev II se diseña por sus bordes de banda eliminada, obtenidos analíticamente desde los bordes de banda, y Bessel se normaliza con norm="mag" en lugar de con la normalización de fase por defecto de SciPy: las dos correcciones existen para que sus puntos de −3 dB caigan también en los bordes. A Chebyshev I y al Elíptico se les entregan los bordes de banda directamente, y para esas dos familias SciPy los lee como borde de la banda de paso de rizado constante: la respuesta ahí es la profundidad del rizado, no −3 dB. Medido sobre la banda de octava de 1 kHz a 48 kHz con orden 6 y el ripple=0.1 por defecto:

ArquitecturaRespuesta en y Ancho de banda de ruido / Desviación del nivel de banda
butter−3,01 dB1,003referencia
cheby2−3,01 dB1,003−0,02 dB
bessel−3,01 dB0,971+0,05 dB
cheby1−0,10 dB1,066+0,33 dB
ellip−0,10 dB1,052+0,25 dB

Las tres primeras columnas son la banda de octava de 1 kHz; la última es la diferencia mediana de nivel de banda respecto a Butterworth sobre un banco entero de tercios de octava analizando 20 s de ruido blanco. Esa última columna importa porque es un sesgo, no dispersión: no se reduce con el tiempo de promediación, es mayor que todo el corredor de banda de paso de clase 1 (±0,4 dB) y mueve todas las bandas a la vez, así que un espectro medido con cheby1 queda en bloque por encima de uno medido con butter. Conviene elegir una arquitectura por campaña y no mezclar nunca arquitecturas dentro de un mismo espectro. Subir el order reduce la desviación, porque una banda de transición más estrecha devuelve el punto de −3 dB hacia el borde de rizado: para cheby1 la mediana pasa de +0,76 dB con orden 4 a +0,33 dB con orden 6, +0,16 dB con orden 8 y +0,04 dB con orden 12. Bajar design.ripple hace lo contrario de lo que sugiere la intuición y hace la desviación mayor (+0,62 dB con ripple=0.02), porque a orden fijo una banda de paso más plana se paga con una caída más lenta.

Un paso banda digital es una constelación de polos y ceros en el plano z: los ceros en DC y Nyquist, o cerca de ellos, sujetan la respuesta lejos de la banda (hasta el suelo de la banda atenuada, en los diseños de rizado constante), y los polos se agrupan justo dentro del círculo unidad en los ángulos que abarca la banda de paso. De ahí salen dos intuiciones. Primera, la selectividad es proximidad: cuanto más cerca del círculo unidad están los polos, más abrupta es la banda y más tiempo resuena el filtro (los picos de retardo de grupo de la sección 4 son esa resonancia, medida). Segunda, la estabilidad es un margen, no una propiedad de la arquitectura: un filtro IIR es estable solo mientras todos sus polos permanecen estrictamente dentro del círculo unidad, y una banda estrecha a una frecuencia de muestreo alta los empuja hacia fuera (radio del polo para un ancho de banda ) y los aprieta entre sí, hasta que los coeficientes en doble precisión ya no pueden representar sus posiciones con exactitud. Las secciones de segundo orden (SOS) desactivan la mitad del problema: cada par de polos conserva sus propios coeficientes, de modo que los errores de redondeo quedan localizados en lugar de acumularse en un único polinomio de orden alto. La otra mitad, la minúscula razón en sí, es lo que arregla el diezmado.

Tres paneles para la banda de tercio de octava de 25 Hz de un banco a 48 kHz. A la izquierda, la constelación en el plano z de la banda diseñada a la frecuencia completa: todos los polos colapsados sobre z = 1, con un recuadro ampliado que muestra los seis polos repartidos en menos de una milésima del radio y la anotación 1 menos r igual a 8,7e-05. En el centro, esa misma banda tal como la realiza de verdad el banco, a 96 Hz tras diezmar por 500: los polos aparecen claramente separados alrededor del círculo unidad, con 1 menos r igual a 4,6e-02. A la derecha, el mayor radio de polo de todas las bandas del banco en ejes log-log: el diseño a una sola frecuencia sube de forma sostenida desde 4e-05 a 12,5 Hz hasta 5e-02 a 20 kHz, mientras que el banco multitasa se mantiene fijo cerca de 5e-02 en todo el rangoTres paneles para la banda de tercio de octava de 25 Hz de un banco a 48 kHz. A la izquierda, la constelación en el plano z de la banda diseñada a la frecuencia completa: todos los polos colapsados sobre z = 1, con un recuadro ampliado que muestra los seis polos repartidos en menos de una milésima del radio y la anotación 1 menos r igual a 8,7e-05. En el centro, esa misma banda tal como la realiza de verdad el banco, a 96 Hz tras diezmar por 500: los polos aparecen claramente separados alrededor del círculo unidad, con 1 menos r igual a 4,6e-02. A la derecha, el mayor radio de polo de todas las bandas del banco en ejes log-log: el diseño a una sola frecuencia sube de forma sostenida desde 4e-05 a 12,5 Hz hasta 5e-02 a 20 kHz, mientras que el banco multitasa se mantiene fijo cerca de 5e-02 en todo el rango

La selectividad es proximidad, y el precio es el condicionamiento. A la frecuencia completa, los polos de la banda de 25 Hz quedan a menos de del círculo unidad y unos encima de otros; esa misma banda realizada a su frecuencia diezmada los tiene repartidos y 500 veces más lejos. El panel derecho es la decisión de diseño resumida en una curva: el banco multitasa mantiene todas las bandas en la misma región bien condicionada, sea cual sea su frecuencia central.

Cómo se midió esta figura

Los dos bancos salen de la tabla de parámetros del apartado 2, uno con el design por defecto y otro con design=FilterDesign(resample=False). Las posiciones de los polos se leen directamente del banco: bank.sos[idx] es la cascada SOS de la banda idx y scipy.signal.sos2zpk la convierte en ceros, polos y ganancia, de modo que la distancia al círculo unidad es 1 - abs(poles).max(). Para la banda de 25,1 Hz de un banco de tercios de octava a 48 kHz esa distancia es 8.73e-05 a la frecuencia completa y 4.62e-02 a la frecuencia diezmada que el banco usa realmente (bank.factor[idx] vale 500, así que la banda corre a 96 Hz). SciPy lanza BadCoefficients al leer el diseño a la frecuencia completa: ese aviso es el argumento de este apartado, escrito por la biblioteca que se lo encontró.

Una banda de tercio de octava de 25 Hz a 48 kHz abarca unos 5,8 Hz, el 0,024 % de Nyquist, con coeficientes tan rígidos que se vuelven numéricamente inestables. El banco lo evita filtrando las bandas bajas a una frecuencia diezmada:

Diezmado multitasa: las bandas altas se filtran a la frecuencia de entrada y las bajas tras un paso bajo antialiasing y diezmado, para que las secciones SOS se mantengan numéricamente sanasDiezmado multitasa: las bandas altas se filtran a la frecuencia de entrada y las bajas tras un paso bajo antialiasing y diezmado, para que las secciones SOS se mantengan numéricamente sanas

Diezmar por reescala el problema: el mismo ancho de banda de 5,8 Hz se vuelve veces mayor respecto al nuevo Nyquist, el radio de los polos se separa del círculo unidad y los coeficientes SOS regresan a un rango bien condicionado. El precio es una contabilidad que el banco paga internamente: antes de cada etapa de diezmado debe ejecutarse un paso bajo antialiasing, porque una componente por encima del nuevo Nyquist que se pliegue hacia abajo cae dentro de las bandas bajas que se están midiendo, y ningún filtro posterior puede eliminarla.

El banco protege sus propias etapas de diezmado, pero solo puede analizar lo que la cadena de captura le entrega:

  • Plegado en el ADC. La energía por encima de que llega al conversor sin un filtro antialiasing analógico se pliega dentro del rango de análisis y es indistinguible del sonido real dentro de banda. Las tarjetas de sonido lo filtran internamente; las cadenas de instrumentación a medida pueden no hacerlo.
  • Remuestreo barato. Convertir una grabación de 44,1 kHz a 48 kHz con un remuestreador de baja calidad deja imágenes que sesgan las bandas más altas. Usa un remuestreador polifásico (scipy.signal.resample_poly) o, más sencillo, analiza a la frecuencia nativa: todas las funciones de phonometry aceptan fs directamente.
  • Bandas cerca de Nyquist. Una banda cuyo borde superior se acerca a no puede realizar su respuesta de diseño: la transformada bilineal comprime allí el eje de frecuencias (el mismo efecto que los filtros de ponderación contrarrestan con high_accuracy). Mantén el borde de la banda superior holgadamente por debajo de Nyquist o sube fs, y deja que verify_filter_class informe del margen restante.

Un nivel de banda no se lee de una señal, se estima a partir de ella, y cuanto más estrecha es la banda más tarda esa estimación. El ancho de banda de una banda de de octava es , que para tercios de octava vale , y de ahí se siguen dos consecuencias.

El filtro tiene que asentarse: una banda resuena durante unas pocas veces , así que ese trozo del principio del registro es transitorio y no nivel, y hay que descartarlo (alrededor de un segundo en la banda de 12,5 Hz, doce milisegundos a 1 kHz). Y la estimación arrastra un error aleatorio de unos dB con sonido de tipo ruido, así que una desviación típica de 0,4 dB exige :

Banda más baja informada [Hz] para una desviación típica de 0,4 dB
12,5 Hz2,935 s
125 Hz293,5 s
1 kHz2310,4 s

La regla que se sigue cabe en una línea: la banda más baja que pienses informar fija la duración del registro de todo el banco. Analizar un fragmento de 1 s hasta 12,5 Hz e informar el resultado con un decimal no es una medida de esa banda, y ni octave_filter ni OctaveFilterBank te avisan. La ley general se deduce en Correlación, retardo y envolvente; esa misma aritmética aplicada por trama en vez de por registro es la nota sobre window_time de Niveles integrados y estadísticos.

Las matemáticas de banda anteriores son comunes a todas las arquitecturas. Cómo difieren realmente las arquitecturas, la comparación en el cruce a −3 dB, la galería completa de respuestas en octava y tercio de octava y los ejemplos de uso por arquitectura, hasta el crossover Linkwitz-Riley, es la Galería de arquitecturas de filtro.

2. Parámetros de octave_filter() / OctaveFilterBank

Sección titulada «2. Parámetros de octave_filter() / OctaveFilterBank»

Los argumentos de cada día van primero y por posición — x, fs, fraction, order, limits — y todo lo demás se agrupa en cuatro dataclases congeladas que se pasan por palabra clave: design (FilterDesign), calibration (LevelCalibration), block_processing (BlockProcessing) y response_plot (ResponsePlot). Al ser congeladas son hashables, que es lo que permite que dos grupos de opciones iguales acierten en la misma caché de diseño. La tabla de abajo nombra cada opción por el grupo en el que viaja.

Tres puntos de llamada comparten esta tabla, y no aceptan las mismas opciones, así que la columna Dónde nombra aquel al que pertenece cada una: octave_filter() es la función de un solo uso, OctaveFilterBank(...) es el constructor y bank.filter() es el método por llamada.

ParámetroDóndeTipoUnidadesRango / por defectoNotas
xfunción, .filter()array 1D o 2Dunidades digitalesno vacío2D es [channels, samples]
fsfunción, constructorintHz> 0
fractionfunción, constructorintpor defecto 1; habitual 3; cualquier Bandas por octava =
orderfunción, constructorintpor defecto 6Orden SOS por banda
limitsfunción, constructorlista [lo, hi]Hzpor defecto [12, 20000]Rango de análisis
design.filter_typefunción, constructorstr'butter' (por defecto), 'cheby1', 'cheby2', 'ellip', 'bessel'Consulta la Galería de arquitecturas de filtro
design.ripple / design.attenuationfunción, constructorfloatdBripple defecto 0.1; attenuation defecto 72.0Rizado de banda de paso / atenuación en banda atenuada (cheby/ellip); cheby2 necesita attenuation para clase 1, ya que scipy fija su suelo de rizado constante en exactamente este valor
design.resamplefunción, constructorboolpor defecto TrueFiltra cada banda a una frecuencia diezmada (multitasa)
response_plot.showfunción, constructorboolpor defecto FalseDibuja la respuesta del banco (requiere matplotlib)
response_plot.filefunción, constructorstr o Nonepor defecto NoneGuarda la gráfica de respuesta del banco en esta ruta
calibration.factorfunción, constructorfloatpor defecto 1.0Escala la entrada a pascales (consulta la guía de Calibración)
calibration.dbfsfunción, constructorboolpor defecto FalseReferencia los niveles a fondo de escala digital en vez de 20 µPa
block_processing.stateful / .steady_icsolo constructorboolpor defecto FalseEstado en streaming; consulta Procesado por bloques
sigbandsfunción, .filter()boolpor defecto FalseDevuelve también las señales temporales por banda
modefunción, .filter()str'rms' (por defecto) o 'peak'Estadístico por banda devuelto
nominalfunción, .filter()boolpor defecto FalseDevuelve etiquetas nominales (p. ej. 1000) en vez de las frecuencias centrales exactas
detrendfunción, .filter()boolpor defecto TrueElimina el offset DC de cada banda antes del nivel (mejora la precisión en graves)
zero_phasesolo .filter()boolpor defecto FalseFiltrado adelante-atrás (offline); ver el apartado 5
calculate_levelsolo .filter()boolpor defecto TrueCon False devuelve las señales de banda sin calcular niveles (usar con sigbands=True)

El reparto no es arbitrario: la octave_filter() de un solo uso diseña un banco y lo usa una vez, así que solo lleva las opciones de tiempo de diseño. zero_phase y calculate_level se deciden en cada llamada y por eso viven en OctaveFilterBank.filter()octave_filter(x, fs, zero_phase=True) lanza TypeError —, mientras que el estado de streaming queda fijado al construir el banco.

mode tiene exactamente dos valores. 'rms' da el nivel medio energético de la banda sobre todo el registro, que es lo que toda norma entiende por «nivel de banda». 'peak' da de la mayor muestra en valor absoluto dentro de la banda, contra la misma referencia de 20 µPa; es un pico instantáneo de banda, no una lectura de retención de pico, está limitado en banda y sin ponderar, así que no es el pico ponderado C de la guía de Niveles, y en señales impulsivas lo domina la propia resonancia del filtro: pertenece al diagnóstico de transitorios, no a la comunicación de niveles. No hay modo 'sum': un total entre bandas es una suma energética que hace quien llama, 10*np.log10(np.sum(10**(spl/10))), nunca una media aritmética de decibelios.

verify_filter_class(bank) comprueba el banco diseñado contra los límites de aceptación de la Tabla 1 de IEC 61260-1 e informa de la clase (1, 2 o None si queda fuera de ambas) con los márgenes por banda.

order es el orden de cada par de secciones paso banda, así que fija con qué rapidez caen las faldas y, por tanto, cuánto se cuela una banda vecina en el nivel de una banda. El orden 6 es el valor por defecto porque supera la máscara de IEC 61260-1 con holgura para Butterworth, y la aritmética merece verse: a 48 kHz sobre un banco Butterworth de tercios de octava, el orden 2 se queda a 27,03 dB de la clase 1 en la banda atenuada lejana, mientras que los órdenes 4, 6, 8 y 10 pasan todos con un margen de clase 1 de exactamente +0,40 dB.

Que el margen sea idéntico no es casualidad, y es la mitad útil de la regla. A partir del orden 4 la restricción que manda se ha desplazado de la banda atenuada a la banda de paso, donde un diseño máximamente plano se queda en 0 dB de desviación y el corredor de clase 1 es de ±0,4 dB: subir más el orden compra profundidad en la banda atenuada, pero ni un decibelio de margen de clase. Y cuesta: cada par de polos de más alarga la resonancia (los picos de retardo de grupo del apartado 4 crecen con el orden), que es la razón por la que el trabajo con transitorios y los espectrogramas de ventana corta prefieren órdenes bajos. La aritmética del margen en sí es Verificación de clase de filtros.

Secciones biquad de ecualización según el RBJ Audio EQ Cookbook (Bristow-Johnson): campana (peaking), shelving grave/agudo, paso bajo/alto, paso banda (pico a 0 dB constante o faldas constantes), rechazo de banda (notch) y paso todo, cada una parametrizada por fs, f0, gain_db y uno de q, bw (ancho de banda en octavas) o slope, exactamente como los define el cookbook. Las secciones se encadenan en una cascada SOS numéricamente robusta, y el diseño es exacto en forma cerrada: una campana pasa exactamente gain_db en f0 y exactamente 0 dB en DC y Nyquist, los shelving caen exactamente en gain_db en su extremo, y el paso todo tiene magnitud unidad en todas las frecuencias (solo gira la fase).

import numpy as np
from phonometry import EQSection, ParametricEQ
fs = 48000
rng = np.random.default_rng(1)
x = rng.standard_normal(fs) # un segundo de ruido
eq = ParametricEQ(fs, [
EQSection("lowshelf", 100.0, gain_db=4.0),
EQSection("peaking", 1000.0, gain_db=-6.0, bw=1.0), # recorte de 1 octava
EQSection("highshelf", 8000.0, gain_db=3.0),
])
y = eq.filter(x) # aplicar la cascada
res = eq.response() # resultado congelado con la cascada SOS
axes = res.plot(language="es") # magnitud + fase de la cascada

Para procesado por bloques pasa stateful=True (la misma convención que WeightingFilter); el atajo de un solo uso es parametric_eq(x, fs, sections).

Dos paneles apilados dibujados por el propio objeto resultado. Arriba, la magnitud de la cascada de tres secciones del fragmento: más 4 dB por debajo de 100 Hz del shelving grave, una campana de menos 6 dB y una octava de ancho a 1 kHz, y más 3 dB por encima de 8 kHz del shelving agudo, con cada sección individual dibujada en gris claro por debajo de la línea gruesa de la cascada. Abajo, la fase de esa misma cascada en grados, oscilando unos veinte grados a cada lado de cero en torno a las frecuencias de los shelving y de la campanaDos paneles apilados dibujados por el propio objeto resultado. Arriba, la magnitud de la cascada de tres secciones del fragmento: más 4 dB por debajo de 100 Hz del shelving grave, una campana de menos 6 dB y una octava de ancho a 1 kHz, y más 3 dB por encima de 8 kHz del shelving agudo, con cada sección individual dibujada en gris claro por debajo de la línea gruesa de la cascada. Abajo, la fase de esa misma cascada en grados, oscilando unos veinte grados a cada lado de cero en torno a las frecuencias de los shelving y de la campana

Las tres secciones del fragmento (en gris) y la cascada que suman; el panel inferior es la fase que cuesta esa magnitud. Cualquier resultado de la biblioteca se redibuja así: el res.plot() de arriba produjo exactamente esta figura.

Respuestas en magnitud de la familia de biquads del RBJ Audio EQ Cookbook: campana, shelving, paso bajo/alto, paso banda y rechazo de bandaRespuestas en magnitud de la familia de biquads del RBJ Audio EQ Cookbook: campana, shelving, paso bajo/alto, paso banda y rechazo de banda
Mostrar el código de esta figura
import matplotlib.pyplot as plt
from phonometry import EQSection, ParametricEQ
fs = 48000
familia = [
EQSection("peaking", 1000.0, gain_db=6.0, q=1.4),
EQSection("lowshelf", 125.0, gain_db=6.0),
EQSection("highshelf", 4000.0, gain_db=-6.0),
EQSection("lowpass", 10000.0),
EQSection("highpass", 50.0),
EQSection("bandpass", 500.0, q=2.0),
EQSection("notch", 2000.0, q=6.0),
]
fig, ax = plt.subplots(figsize=(10, 6))
for section in familia:
res = ParametricEQ(fs, [section]).response(f_min=20.0, f_max=20000.0)
ax.semilogx(res.frequencies, res.magnitude_db,
label=f"{section.filter_type} @ {section.f0:g} Hz")
ax.set(xlim=(20, 20000), ylim=(-27, 9),
xlabel="Frecuencia [Hz]", ylabel="Magnitud [dB]")
ax.grid(True, which="both", alpha=0.3)
ax.legend(loc="lower center", ncols=2, fontsize=9)
plt.show()

Todo lo anterior es diseño. Demostrar que un banco diseñado cumple una clase de prestaciones de IEC 61260-1, banda a banda y con su margen en decibelios, es Verificación de clase de filtros: la máscara de aceptación de la Tabla 1, la clase 0 más estricta de la edición retirada de 1995, qué aporta una clase en una medición y la ficha de conformidad acreditada.

sigbands=True devuelve las señales temporales por banda junto a los niveles, y es lo que hace visible el comportamiento en fase y en transitorio, y lo que deja al descubierto el retardo de grupo que cada arquitectura paga por su selectividad. La estabilidad en sí es asunto del apartado 1, en Polos, ceros y estabilidad; lo que este apartado añade es la prueba, porque un filtro cuya respuesta al impulso decae a cero es un filtro estable.

import numpy as np
from phonometry import filters
# 1. Generar una señal (suma de 250 Hz y 1000 Hz)
fs = 48000
t = np.linspace(0, 0.5, int(fs * 0.5), endpoint=False)
y = np.sin(2 * np.pi * 250 * t) + np.sin(2 * np.pi * 1000 * t)
# 2. Comparar arquitecturas (Butterworth vs Chebyshev II)
spl_b, freq, xb_butter = filters.octave_filter(
y, fs=fs, fraction=1, sigbands=True,
design=filters.FilterDesign(filter_type='butter'))
spl_c2, _, xb_cheby2 = filters.octave_filter(
y, fs=fs, fraction=1, sigbands=True,
design=filters.FilterDesign(filter_type='cheby2'))
# 'xb_butter' y 'xb_cheby2' contienen las señales por banda en el dominio del tiempo
Descomposición por bandas en el tiempo comparando Butterworth y Chebyshev II, incluida la respuesta al impulsoDescomposición por bandas en el tiempo comparando Butterworth y Chebyshev II, incluida la respuesta al impulso

El panel superior es la entrada, los intermedios son sus señales de banda con Butterworth (línea continua) y Chebyshev II (discontinua), y el inferior es la respuesta al impulso de la banda de 1 kHz de cada uno. Las dos respuestas al impulso decaen a cero — esa es la comprobación de estabilidad —, pero la resonancia de Chebyshev II dura visiblemente más, que es el precio en transitorio de su falda más abrupta.

Mostrar el código de esta figura
import matplotlib.pyplot as plt
import numpy as np
from phonometry import filters
fs = 48000
t = np.linspace(0, 0.5, int(fs * 0.5), endpoint=False)
y = np.sin(2 * np.pi * 250 * t) + np.sin(2 * np.pi * 1000 * t)
bank_b = filters.OctaveFilterBank(fs=fs, fraction=1, order=6, limits=[100.0, 2000.0])
bank_c = filters.OctaveFilterBank(fs=fs, fraction=1, order=6, limits=[100.0, 2000.0],
design=filters.FilterDesign(filter_type="cheby2"))
_, freq, xb_butter = bank_b.filter(y, sigbands=True)
_, _, xb_cheby2 = bank_c.filter(y, sigbands=True)
# La figura publicada lleva dos paneles más: la entrada arriba y, abajo, la
# banda de 1 kHz de un impulso unitario a través de cada banco.
impulse = np.zeros_like(t)
impulse[0] = 1.0
_, _, imp_butter = bank_b.filter(impulse, sigbands=True)
_, _, imp_cheby2 = bank_c.filter(impulse, sigbands=True)
one_k = int(np.argmin(np.abs(np.asarray(freq) - 1000.0)))
fig, axes = plt.subplots(len(freq) + 2, 1, figsize=(9, 2 * (len(freq) + 2)),
sharex=True)
axes[0].plot(t, y)
axes[0].set_title("Señal original (250 Hz + 1000 Hz)")
for ax, fc, xb, xc in zip(axes[1:-1], freq, xb_butter, xb_cheby2):
ax.plot(t, xb, label="Butterworth")
ax.plot(t, xc, "--", label="Chebyshev II")
ax.set_title(f"Banda de {fc:.0f} Hz")
axes[-1].plot(t, imp_butter[one_k], label="Butterworth")
axes[-1].plot(t, imp_cheby2[one_k], "--", label="Chebyshev II")
axes[-1].set_title("Respuesta al impulso, banda de 1 kHz")
axes[1].legend()
axes[0].set_xlim(0, 0.04)
axes[-1].set_xlabel("Tiempo [s]")
plt.tight_layout()
plt.show()

El retardo de grupo de la banda de octava de 1 kHz muestra el compromiso directamente: Bessel se mantiene casi plano en la banda de paso (los transitorios sobreviven), mientras que Chebyshev I y el Elíptico pagan su caída abrupta con fuertes picos de retardo en los bordes de banda.

Retardo de grupo de la banda de 1 kHz para las cinco arquitecturas: Bessel casi plano, Chebyshev y elíptico con picos en los bordesRetardo de grupo de la banda de 1 kHz para las cinco arquitecturas: Bessel casi plano, Chebyshev y elíptico con picos en los bordes
Mostrar el código de esta figura
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import group_delay
from phonometry import filters
fs = 48000
w = np.logspace(np.log10(500), np.log10(2000), 1024)
fig, ax = plt.subplots(figsize=(9, 5))
for ftype in ("butter", "cheby1", "cheby2", "ellip", "bessel"):
bank = filters.OctaveFilterBank(fs, fraction=1, order=6, limits=[800, 1200],
design=filters.FilterDesign(filter_type=ftype))
idx = int(np.argmin(np.abs(np.array(bank.freq) - 1000)))
fsd = fs / bank.factor[idx]
# Retardo de grupo de una cascada SOS = suma del de sus secciones
gd = sum(group_delay((sec[:3], sec[3:]), w=w, fs=fsd)[1]
for sec in bank.sos[idx])
ax.semilogx(w, gd / fsd * 1000, label=ftype)
ax.set(xlim=(500, 2000), xlabel="Frecuencia [Hz]",
ylabel="Retardo de grupo [ms]")
ax.grid(True, which="both", alpha=0.3)
ax.legend()
plt.show()

Para análisis offline puedes eliminar por completo el retardo de grupo: OctaveFilterBank.filter(…, zero_phase=True) filtra cada banda hacia delante y hacia atrás (scipy.signal.sosfiltfilt), manteniendo las señales por banda alineadas con la entrada. Es una opción por llamada del banco, no un argumento de la octave_filter() de un solo uso. La atenuación efectiva se duplica y la banda de paso efectiva se estrecha, reduciendo el nivel de banda ancha medido en ~0,2 a 0,3 dB por banda (un tono puro dentro de banda no se ve afectado); prefiere el filtrado hacia delante cuando el SPL absoluto de banda debe coincidir con el convenio de un solo paso, y reserva la fase cero para cuando importa la envolvente temporal (p. ej. decaimiento de reverberación). La opción es incompatible con el procesado por bloques (stateful).

import numpy as np
from phonometry import filters
fs = 48000
t = np.linspace(0, 0.5, int(fs * 0.5), endpoint=False)
y = np.sin(2 * np.pi * 250 * t) + np.sin(2 * np.pi * 1000 * t)
bank = filters.OctaveFilterBank(fs=48000, fraction=3)
spl, freq, xb = bank.filter(y, sigbands=True, zero_phase=True)
Filtrado causal frente a fase cero de una ráfaga: la salida de fase cero queda alineada con la entradaFiltrado causal frente a fase cero de una ráfaga: la salida de fase cero queda alineada con la entrada

El filtrado causal retrasa la ráfaga según el retardo de grupo del filtro; el filtrado de fase cero la mantiene alineada con la entrada.

Mostrar el código de esta figura
import matplotlib.pyplot as plt
import numpy as np
from phonometry import filters
fs = 48000
t = np.linspace(0, 0.15, int(fs * 0.15), endpoint=False)
x = np.zeros_like(t) # ráfaga de 250 Hz a mitad de trama
start, end = int(0.05 * fs), int(0.10 * fs)
x[start:end] = np.sin(2 * np.pi * 250 * t[start:end]) * np.hanning(end - start)
bank = filters.OctaveFilterBank(fs=fs, fraction=1, order=6, limits=[200.0, 300.0])
_, _, fwd = bank.filter(x, sigbands=True, calculate_level=False)
_, _, zp = bank.filter(x, sigbands=True, calculate_level=False,
zero_phase=True)
fig, ax = plt.subplots(figsize=(9, 4.5))
ax.plot(t, x, color="gray", alpha=0.5, label="Ráfaga de entrada (250 Hz)")
ax.plot(t, fwd[0], label="Causal (retardo de grupo)")
ax.plot(t, zp[0], "--", label="zero_phase=True (alineado)")
ax.set(xlabel="Tiempo [s]", ylabel="Amplitud")
ax.legend()
plt.show()
  • Cubierto

    La matemática de bordes de banda de la IEC 61260-1:2014 (apartados 5.2-5.5) y la serie de frecuencias preferentes de la ISO 266:1997 tras nominal_frequencies. La biblioteca diseña cada banda como una cascada SOS en cinco arquitecturas (Butterworth, Chebyshev I/II, elíptico, Bessel), más el ParametricEQ del RBJ Audio EQ Cookbook, con la descomposición por bandas y el filtrado de fase cero.

  • No cubierto

    Las respuestas comparadas de las cinco arquitecturas, la galería completa de octava y tercio de octava y los ejemplos de uso por arquitectura, incluido el crossover Linkwitz-Riley, son la Galería de arquitecturas de filtro. Las máscaras de aceptación de clase de la Tabla 1 y su verificación se han trasladado a Verificación de clase de filtros. Cerca de Nyquist, la transformada bilineal deforma el eje de frecuencia y el banco no tiene corrección para ello (a diferencia de la opción high_accuracy de WeightingFilter): mantén el borde superior de banda con margen cómodo respecto a Nyquist, o sube fs, y confirma el margen con verify_filter_class.

¿Cómo se definen las frecuencias centrales y los bordes de banda de fracción de octava?

Sección titulada «¿Cómo se definen las frecuencias centrales y los bordes de banda de fracción de octava?»

IEC 61260-1:2014 (apartados 5.2-5.5) construye cada banda a partir de la razón de octava en base 10 , de modo que una octava no es exactamente 2. Las frecuencias centrales siguen para impar y para par — de modo que 1 kHz es una frecuencia central de banda en las fracciones impares y un borde de banda en las pares — y los bordes son y en ambos casos; cada banda de tercio de octava abarca , diez bandas por década.

  • American National Standards Institute. (2004). Specification for octave-band and fractional-octave-band analog and digital filters (ANSI S1.11-2004). Acoustical Society of America. El convenio de bordes de banda sobre el que cada banco de esta guía sitúa sus puntos de −3 dB.
  • Bristow-Johnson, R. (2021). Audio EQ Cookbook. Nota de grupo de trabajo del W3C (ed. R. Toy), 8 de junio de 2021. Las recetas de coeficientes biquad y la parametrización por Q / ancho de banda / pendiente de shelving detrás de ParametricEQ (sección 3).
  • International Electrotechnical Commission. (2014). Electroacoustics — Octave-band and fractional-octave-band filters — Part 1: Specifications (IEC 61260-1:2014). Las matemáticas de bordes de banda de la sección 1 (frecuencias centrales y bordes de banda en base 10, apartados 5.2-5.5) y las etiquetas nominales de banda de todos los bancos que se diseñan aquí.
  • International Organization for Standardization. (1997). Acoustics — Preferred frequencies (ISO 266:1997). La serie de frecuencias preferentes en la que se basan las etiquetas nominales de banda que devuelve nominal_frequencies.
  • Oppenheim, A. V. y Schafer, R. W. (2010). Discrete-time signal processing (3.ª ed.). Pearson. La teoría de polos y ceros, estabilidad y multitasa condensada en la sección 1: cascadas SOS, transformada bilineal y diezmado (ISBN 978-0-13-198842-2).
  • Smith, J. O. (s. f.). Introduction to digital filters with audio applications. Center for Computer Research in Music and Acoustics (CCRMA), Stanford University. Libro en línea gratuito y complementario sobre diseño y análisis de filtros digitales, de la geometría de polos y ceros a la estabilidad.