🖥️ Spectre d’un signal musical

Notebook Capytale de cet exercice : 3411-11693103

Dans cet exercice, on étudie le spectre d’un signal musical réel ainsi que les effets d’une limitation de spectre sur son écoute.

Cet exercice peut être fait avec votre musique favorite au format WAV. À défaut, vous pouvez télécharger une musique libre de droits au lien suivant : https://www.free-stock-music.com/fsm-team-escp-enlia-take-me-to-the-moon.html

La première étape consiste à charger votre fichier dans Python grâce aux instructions suivantes.

from scipy.io import wavfile

fe, son = wavfile.read('nom_de_votre_fichier.wav')

fe contient alors la fréquence d’échantillonnage du signal, et son contient les échantillons du signal audio.

La plupart des fichiers musicaux sont stéréo et contiennent donc deux canaux. La variable son est alors un tableau numpy avec deux colonnes, une pour chaque canal.

1/ Définir une variable signal qui contient le signal d’un seul canal (par exemple le canal gauche) du fichier audio.

Coup de pouce 1
Conventionnellement, on note s[i,j] pour la valeur de s à la i-ème ligne et la j-ème colonne.
Coup de pouce 2
On peut utiliser la notation [:]
Corrigé
signal = son[:, 0]

2/ Tracer le signal en fonction du temps.

Coup de pouce 1
La première étape est de définir le tableau des temps à partir de la fréquence d’échantillonnage et du nombre de points du signal.
Coup de pouce 2
On peut utiliser la fonction np.arange(N) qui génère un tableau de N valeurs allant de 0 à N-1.
Corrigé

La première étape est de recréer le tableau des temps. La période d’échantillonnage est l’inverse de la fréquence d’échantillonnage. Les temps sont des valeurs espacées de la période d’échantillonnage et sont en même nombre que les points du signal.

import numpy as np
Te = 1 / fe
t = np.arange(len(signal)) * Te

On peut ensuite tracer le signal en fonction du temps grâce à la bibliothèque matplotlib.

import matplotlib.pyplot as plt
plt.figure("Signal")
plt.clf()
plt.plot(t, signal)
plt.xlabel('Temps (s)')
plt.ylabel('Amplitude')
plt.show()

Pour écouter le signal audio dans Python, on peut utiliser la bibliothèque IPython.display comme suit :

from IPython.display import Audio, display
display(Audio(signal, rate=fe))

3/ Calculer et tracer le spectre du signal audio en utilisant les fonctions np.fft.rfft et np.fft.rfftfreq. Le spectre sera stocké dans un tableau nommé spectre.

Coup de pouce 1
Quand on trace un spectre, on ne prend que son module, obtenu avec la fonction np.abs.
Corrigé

Le spectre est calculé avec

spectre = np.fft.rfft(signal)
fréquences = np.fft.rfftfreq(len(signal), Te)

On peut ensuite tracer le spectre en utilisant matplotlib.

plt.figure("Spectre")
plt.clf()
plt.plot(fréquences, np.abs(spectre), label='signal original')
plt.legend()
plt.xlabel('Fréquence (Hz)')
plt.ylabel('Amplitude')
plt.show()

Les grandes ondes ont des bandes de 9⁠ ⁠kHz de large.

4/ Calculer la fréquence maximale d’un signal émis sur les grandes ondes.

Coup de pouce 1
Quel est le lien entre la largeur de bande et la fréquence maximale du signal ?
Corrigé

La largeur de bande est le double de la fréquence maximale d’où

𝑓max=Δ𝑓2=4,5 kHz

5/ Pour simuler l’effet de ce filtrage, définir un tableau spectre_filtré qui correspond à spectre pour les fréquences inférieures à 4,5⁠ ⁠kHz et nul pour les fréquences supérieures. spectre_filtré aura autant d’éléments que spectre.

Coup de pouce 1
On peut copier le spectre original puis mettre à zero les valeurs correspondant aux fréquences que l’on ne souhaite pas conserver.
Coup de pouce 2
Copier le spectre initial avec np.copy. Parcourir le spectre original et mettre à zero pour les fréquences supérieures à 4,5⁠ ⁠kHz.
Corrigé
spectre_filtré = np.copy(spectre)
for i in range(len(spectre)):
    if fréquences[i] > 9e3/2:
        spectre_filtré[i] = 0

6/ Recomposer le signal temporel à partir du spectre filtré en utilisant np.fft.irfft. Écouter le son correspondant et le comparer au son non filtré.

Corrigé
signal_filtré = np.fft.irfft(spectre_filtré)
display(Audio(signal_filtré, rate=fe))

L’altération du signal est perceptible, il parait moins précis et n’est pas fidèle à l’original.

7/ Reprendre les questions précédentes pour une diffusion sur la bande FM, avec un spectre audio limité à 15⁠ ⁠kHz.

Corrigé
spectre_filtré_FM = np.copy(spectre)
for i in range(len(spectre)):
    if fréquences[i] > 15e3:
        spectre_filtré_FM[i] = 0


signal_filtré_FM = np.fft.irfft(spectre_filtré_FM)
display(Audio(signal_filtré_FM, rate=fe))

L’altération du signal est imperceptible (par moi en tout cas), la diffusion en FM préserve mieux l’intégrité du signal.