Tp2
I. M-Channel filter banks
1. Definition
A filter bank is a set of filters applied in parallel to an input signal. Each filter in the bank
is designed to extract or manipulate a specific portion of the signal's frequency
spectrum.
2. Types of filter banks
There are two main categories of filter banks:
2.1 analysis Filter bank
➢ The input signal 𝑥(𝑛) is divided into several frequency bands using bandpass
filters.
➢ Each output corresponds to a particular frequency band.
➢ Used for audio compression (e.g. MP3), signal analysis, voice recognition.
Example: decomposition of an audio signal A signal 𝑥(𝑛) passes through 8 filters that
extract the bands 0-500 Hz, 500-1000 Hz, etc.
2.2 Synthesis filter bank
After processing (e.g. modification, compression), the signals from the sub-bands are
recombined to reconstruct a final signal.
Example: Reconstruction after MP3 compression
The filtered sub-bands are recombined to reconstruct the original sound.
II. Two channel perfection reconstruction filter banks
In a 2-channel filter bank, the input signal is separated into a high-pass signal and a low-
pass signal, respectively d(n) and a(n) respectively in figure 3. We call H0(z) and H1(z)
the low-pass and high-pass filters of the analysis filter and F0(z) and F1(z) the low-pass
and high-pass filters of the synthesis filter. X(z) is the Z-transform of the x(n) signal. The
expression of the output of the filter bank is given by :
̂
X(z) = (F0(z) H0(z) + F1(z) H1(z)) X(z)
Figure 3 : 2-channel filter bank
The reconstruction will be perfect to within one delay, if we have the property
F0(z) H0(z) + F1(z) H1(z) = z−k
III. 2-Channel Multicadence filter bank
The analysis filters H0(z) and H1(z) have templates corresponding to Figure 4, then it is
possible to introduce critical sampling by decimating by two the output of the analysis
filters.
Figure 4
The structure obtained is shown in Figure 5.
Analysis filters
Step 1: Filtering We apply the convolution x(n) * h0(n) to obtain the low frequency signal
: a(n) = x(n) * h0(n)
And the convolution x(n) * h1(n) to obtain the high frequency signal: d(n) = x(n) * h1(n)
Step 2: Decimation (keep one sample out of 2) : adecimated; ddecimated
Reconstruction Synthesis filters
Step 3: Interpolation (reset zeros) : ainterp; dinterp
Step 4: Synthesis filtering (F0 and F1 applied):
y0(n) = ainterp * f0(n) ; y1(n) = dinterp * f1(n)
Step 5: Addition of the reconstructed signals : x̂(n) = y0(n) + y1(n)
2.1 Definition of Analysis and Synthesis Filters
A perfect filter bank respects the perfect reconstruction relation:
- Analysis filters :
✓ H0(z) : Low-pass filter
✓ H1(z) : High-pass filter
H1(z)= H0(-z) and h1(n)= (-1)nh0(n)
- Synthesis filters (reconstruction) :
✓ F0(z) = Low-pass reconstruction filter
✓ F1(z) = High-pass reconstruction filter
̂(z) = F0(z) Y0(z) + F1(z) Y1(z)
X
where Y0(z) and Y1(z) are the signals obtained after filtering and decimation.
2.2 Relationship between synthesis and analysis filters
1. Perfect reconstruction condition
A perfect filter bank must satisfy the condition: H0(z) F0(z) + H1(z) F1(z) = 0
The synthesis filters are thus linked to the analysis filters by :
F0(z) = −H1(−z) F1(z) = H0(−z)
Or F0(z) = H1(−z) F1(z) = −H0(−z)
if we also impose H1(z) = H0(−z) => F0(z) = H0(z) F1(z) = −H0(−z) figure 6
Figure 6: Quadrature mirror filter bank
2. Time interpretation
If the filters are symmetric FIR, then:
F0(z) = H0(z) F1(z) = −H0(−z) => f0(n) = h0(n) f1(n) = −(-1)nf0(n)
QMF filter : H1(z) = H0(-z), F0(z) = H0(z), F1(z) = - H0(-z)
𝐻0(𝑧) = 𝑃0(𝑧2) + 𝑧−1𝑃1(𝑧2)
𝐻1(𝑧) = 𝑃0(𝑧2) − 𝑧−1𝑃1(𝑧2)
𝐹0(𝑧) = 𝑃0(𝑧2) + 𝑧−1𝑃1(𝑧2)
𝐹1(𝑧) = −[𝑃0(𝑧2) − 𝑧−1𝑃1(𝑧2)]
After application of the noble identities
Program 1
#[Link]
import numpy as np
import [Link] as sp
import sounddevice as snd
import [Link] as wav
import [Link] as plt
#---------------------Lecture du signal audio--------------------------------#
fe,x = [Link]('[Link]')
[Link](x, fe)
#----------------------Generation du banc de filtre--------------------------#
N=8
f1 = [Link](8*N, [0,0.0625, 0.0627, 0.5], [1,0], [1, 100])
f2 = [Link](8*N, [0, 0.0625, 0.0627, 0.125, 0.127, 0.5], [0, 1, 0], [100, 1, 100])
f3 = [Link](8*N, [0, 0.125, 0.127, 0.1875, 0.1877, 0.5], [0, 1, 0], [100, 1, 100])
f4 = [Link](8*N, [0, 0.1875, 0.1877, 0.25, 0.27, 0.5], [0, 1, 0], [100, 1, 100])
f5 = [Link](8*N, [0, 0.25, 0.27, 0.3125, 0.3127, 0.5], [0, 1, 0], [100, 1, 100])
f6 = [Link](8*N, [0, 0.3125, 0.3127, 0.375, 0.377, 0.5], [0, 1, 0], [100, 1, 100])
f7 = [Link](8*N, [0, 0.375, 0.377, 0.4375, 0.4377, 0.5], [0, 1, 0], [100, 1, 100])
f8 = [Link](8*N, [0, 0.4375, 0.4377, 0.5], [0, 1], [100, 1])
## Calcul des reponses en frequence
w1,H1 = [Link](f1,1,fs=fe); w2,H2 = [Link](f2,1,fs=fe)
w3,H3 = [Link](f3,1,fs=fe); w4,H4 = [Link](f4, 1,fs=fe)
w5,H5 = [Link](f5, 1,fs=fe); w6,H6 = [Link](f6, 1,fs=fe)
w7,H7 = [Link](f7, 1,fs=fe); w8,H8 = [Link](f8,1,fs=fe)
## Visualisation des repones impulsionnelles et frequentielles
[Link](1)
[Link](f1, 'b', f2, 'g',f3, 'r',f4, 'c',f5, 'm',f6, 'y',f7, 'k',f8, 'b--')
[Link](('Lowpass', 'Bandpass 1', 'Bandpass 2', 'Bandpass 3', 'Bandpass 4', 'Bandpass
5', 'Bandpass 6',
'Highpass'), loc='upper right')
[Link]('Reponse impulsionnelle')
[Link](2)
[Link](w1,20*np.log10([Link](H1))); [Link](w2,20*np.log10([Link](H2)))
[Link](w3,20*np.log10([Link](H3))); [Link](w4,20*np.log10([Link](H4)))
[Link](w5,20*np.log10([Link](H5))); [Link](w6,20*np.log10([Link](H6)))
[Link](w7,20*np.log10([Link](H7)));[Link](w8,20*np.log10([Link](H8)))
[Link](('Lowpass', 'Bandpass 1', 'Bandpass 2', 'Bandpass 3', 'Bandpass 4', 'Bandpass
5', 'Bandpass 6',
'Highpass'), loc='upper right')
[Link]('Frequence');[Link]('Amplitude en dB') ;[Link]('Reponses frequentielles')
#--------------------Analyse par bancs de filtre-----------------------------#
## Filtrage par le banc de filtres
filtered1 = [Link](f1,1,x)
filtered2 = [Link](f2,1,x)
filtered3 = [Link](f3,1,x)
filtered4 = [Link](f4,1,x)
filtered5 = [Link](f5,1,x)
filtered6 = [Link](f6,1,x)
filtered7 = [Link](f7,1,x)
filtered8 = [Link](f8,1,x)
## Ecoute du son filtre par les 8 filtres
#a=input('appuyer sur une touche')
#[Link](filtered1, fe); a=input('appuyer sur une touche')
#[Link](filtered2, fe) ;a=input('appuyer sur une touche')
#[Link](filtered3, fe); a=input('appuyer sur une touche')
#[Link](filtered4, fe); a=input('appuyer sur une touche')
#[Link](filtered5, fe); a=input('appuyer sur une touche')
#[Link](filtered6, fe); a=input('appuyer sur une touche')
#[Link](filtered7, fe); a=input('appuyer sur une touche')
#[Link](filtered8, fe); a=input('appuyer sur une touche')
# Decimation
filteredds1 = filtered1[::N]
filteredds2 = filtered2[::N]
filteredds3 = filtered3[::N]
filteredds4 = filtered4[::N]
filteredds5 = filtered5[::N]
filteredds6 = filtered6[::N]
filteredds7 = filtered7[::N]
filteredds8 = filtered8[::N]
#--------------------Reconstruction par bancs de filtre-----------------------#
##Interpolation
filteredus1 = [Link](len(x))
filteredus2 = [Link](len(x))
filteredus3 = [Link](len(x))
filteredus4 = [Link](len(x))
filteredus5 = [Link](len(x))
filteredus6 = [Link](len(x))
filteredus7 = [Link](len(x))
filteredus8 = [Link](len(x))
filteredus1[::N] = filteredds1
filteredus2[::N] = filteredds2
filteredus3[::N] = filteredds3
filteredus4[::N] = filteredds4
filteredus5[::N] = filteredds5
filteredus6[::N] = filteredds6
filteredus7[::N] = filteredds7
filteredus8[::N] = filteredds8
# Filtrage par le banc de filtres
filteredsyn1 = [Link](f1,1,filteredus1)
filteredsyn2 = [Link](f2,1,filteredus2)
filteredsyn3 = [Link](f3,1,filteredus3)
filteredsyn4 = [Link](f4,1,filteredus4)
filteredsyn5 = [Link](f5,1,filteredus5)
filteredsyn6 = [Link](f6,1,filteredus6)
filteredsyn7 = [Link](f7,1,filteredus7)
filteredsyn8 = [Link](f8,1,filteredus8)
## Sommation des reponses
recons
=filteredsyn1+filteredsyn2+filteredsyn3+filteredsyn4+filteredsyn5+filteredsyn6+filtere
dsyn7+filteredsyn8
a=input('appuyer sur une touche')
[Link](recons, fe)
[Link](3)
[Link](x)
[Link](recons)
[Link]()
Questions:
1 What is the role of [Link]() in the code?
2 Why is [Link](x, fe) used?
3 What does filtered1 = [Link](f1,1,x) do?
4 Why does the code use filtered1[::N] for decimation?
5 Why are the zero arrays (filteredus1 = [Link](len(x))) created before interpolation?
6 What is the purpose of [Link](x) and [Link](recons) at the end?
7 Why is the same set of filters used for both analysis (lfilter(f,1,x)) and synthesis
(lfilter(f,1,filteredus)) instead of separate synthesis filters?
8 How could the reconstruction process be improved to achieve a perfect signal match?
Program 2
import numpy as np
import [Link] as signal
import [Link] as wav
import sounddevice as sd
import [Link] as plt
# Charger le fichier audio
fs, signal_audio = [Link]("[Link]")
signal_audio = signal_audio.astype(np.float32) # Normalisation si necessaire
# Parametres
M = 4 # Nombre de canaux
N = 128 # Ordre du filtre FIR (augmente pour une meilleure precision)
# Nouvelle definition des bandes pour fs = 22050 Hz
bands = [(1, 2750), (2750, 5500), (5500, 8250), (8250, 11024)] # Evite fs/2
# Conception des filtres FIR passe-bande
filtres = []
for (f_low, f_high) in bands:
if f_low <= 0:
f_low = 1 # Eviter la frequence 0
if f_high >= fs / 2:
f_high = fs / 2 - 1 # Eviter Nyquist
taps = [Link](N, [f_low, f_high], pass_zero=False, fs=fs) # Passe-bande
[Link](taps)
# Fonction d'analyse (filtrage et decimation)
def analyse_banque_de_filtres(signal_entree, filtres, M):
sous_bandes = []
for h in filtres:
y = [Link](h, 1.0, signal_entree)
y_decime = y[::M] # Decimation par M
sous_bandes.append(y_decime)
return sous_bandes
# Fonction de synthese (interpolation et sommation)
def synthese_banque_de_filtres(sous_bandes, filtres, M):
signal_reconstruit = [Link](len(sous_bandes[0]) * M)
for i, h in enumerate(filtres):
y_interpole = [Link](len(signal_reconstruit))
y_interpole[::M] = sous_bandes[i] # Interpolation
y_filtre = [Link](h, 1.0, y_interpole)
signal_reconstruit += y_filtre
return signal_reconstruit
# Application de la banque de filtres
sous_bandes = analyse_banque_de_filtres(signal_audio, filtres, M)
# Reconstruction
signal_reconstruit = synthese_banque_de_filtres(sous_bandes, filtres, M)
# Sauvegarde du signal reconstruit
[Link]("signal_reconstruit.wav", fs, signal_reconstruit.astype(np.int16))
# Ecoute du signal original
print("Lecture du signal original...")
[Link](signal_audio, fs)
[Link]()
# Ecoute du signal reconstruit apres filtrage
print("Lecture du signal reconstruit...")
[Link](signal_reconstruit, fs)
[Link]()
# Affichage des resultats
[Link](figsize=(12, 6))
[Link](2, 1, 1)
[Link](signal_audio[:5000], label='Signal Original')
[Link]()
[Link](2, 1, 2)
[Link](signal_reconstruit[:5000], label='Signal Reconstruit', linestyle='dashed')
[Link]()
[Link]()
Questions:
1 What is the main function of analyse_banque_de_filtres()?
2 Why is signal_audio converted to np.float32 after reading the audio file?
3 What is the role of [Link]() in the code?
4 Why is decimation applied after filtering in analyse_banque_de_filtres()?
5 Why is f_high >= fs / 2 replaced with fs / 2 - 1 in the filter band definition?
6 What are the effects of using a FIR filter order that is too low or too high in this code?
7 What happens if fs is not properly chosen concerning the defined bands?