8.8 ANALYSE SPECTRALE

 

 

INTRODUCTION

 

Dès qu’un rayon lumineux parvient à l’œil ou qu’un son atteint l’oreille, il en découle un signal qui peut être interprété comme une fonction du temps y(t). Lorsqu’il s’agit d’une lumière monochromatique ou d’un son pur, cette fonction sera une oscillation sinusoïdale d’amplitude A et de fréquence f, donnée par l’expression [plus.. Si l’origine du temps ne correspond pas au début de l’oscillation, il faut encore rajouter un décalage de phase]:

y(t) = Asin(ω * t) where ω = 2 * π * f

Un signal plus complexe, comme une note tenue de manière constante par un instrument, est également périodique mais sans être une sinusoïde. Le célèbre mathématicien Joseph Fourier (1768-1830) a prouvé qu’il était toujours possible de représenter une fonction périodique quelconque par une somme d’oscillations sinusoïdales, appelée série de Fourier. Cette théorie constitue de ce fait un fondement primordial du développement des mathématiques modernes, de la physique et des sciences de l’ingénieur. L’analyse spectrale consiste à décomposer un signal en ses composantes sinusoïdales.  

CONCEPTS DE PROGRAMMATION:
Oscillation sinusoïdale, Séries de Fourier, Transformation de Fourier rapide (FFT = Fast Fourier Transform), spectre, sonogramme

 

 

SPECTRE D’UN SON, HARMONIQUES

 

Les composantes de fréquences sinusoïdales déterminent le timbre sonore d’une voix ou d’un instrument de musique. Un son purement périodique est constitué de la fondamentale et de ses harmoniques dont les fréquences sont des multiples entiers de la fondamentale. Si l’on représente graphiquement l’amplitude de chacune des différentes composantes de fréquences du son, on obtient son spectre que l’on peut déterminer. Un appareil permettant de déterminer le spectre d’un son est appelé analyseur de spectre. TigerJython est capable de déterminer le spectre d’un son à l’aide d’un célèbre algorithme appelé transformation de Fourier rapide (FFT = Fast Fourier Transform).

Pour effectuer une transformation de Fourier rapide, on passe à la fonction fft(samples, n) une liste contenant les valeurs échantillonnées à intervalles de temps réguliers. Le paramètre n permet d’indiquer que l’on ne prend que les n premières valeurs de la liste pour effectuer la FFT.

La fonction fft retourne une liste de nombres représentant l’amplitude de chacune desn/2 composantes de fréquences (normalisées) du son analysé. Chaque nombre ainsi obtenu par la fonction fft représente une fréquence présente dans le spectre du son analysé. La différence de fréquence entre chaque composante retournée est de r = fs / n, fs est la fréquence d’échantillonnage. La grandeur r est appelée résolution du spectre.

Ces n/2 valeurs de retour espacées d’une différence de fréquence r couvrent la plage de fréquence entre 0 et n/2*r = fs/2, ou, pour faire simple : La FFT permet de déterminer le spectre compris entre 0 et fs/2 pour une fréquence d’échantillonnage de fs. Pour prendre un exemple concret, l’échantillonnage à 44100 Hz d’un CD audio permet de couvrir une plage de fréquence de 0 à 22050 Hz, ce qui correspond à l’ensemble de la page de fréquence audibles par l’oreille humaine.

Afin de tester notre analyseur de spectre, nous allons commencer par utiliser le son "wav/doublesine.wav" présent dans la distribution de TigerJython. Celui-ci superpose deux sons sinusoïdaux et a été enregistré à une fréquence d’échantillonnage fs = 40,000 Hz. En prenant n = 10,000 valeurs d’échantillonnage, la fonction fft(samples, n) retourne 5'000 composantes de fréquences avec une résolution de r = 40,000 / 10,000 = 4 Hz dans le domaine de fréquence entre 0 et 20,000 Hz que l’on peut ensuite représenter à l’aide de barres verticales dans un GPanel.
 

from soundsystem import *
from gpanel import *

def showSpectrum(text):
    makeGPanel(-2000, 22000, -0.2, 1.2)
    drawGrid(0, 20000, 0, 1.0, 10, 5, "blue")
    title(text)
    lineWidth(2)
    r = fs / n # Resolution
    f = 0
    for i in range(n // 2): 
        line(f, 0, f, a[i])
        f += r

fs = 40000 # Sampling frequency
n = 10000 # Number of samples
samples = getWavMono("wav/doublesine.wav")
openMonoPlayer(samples, fs)
play()
a = fft(samples, n)
showSpectrum("Audio Spectrum")
Hihliht proram code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Comme vous pouvez l’imaginer et l’entendre, le spectre contient deux composantes de fréquences différentes : 500 Hz et 1.5 kHz présentant un rapport d’amplitude de 1 pour 1/2. L’analyseur de spectre révèle également quelques autres composantes de fréquences perturbatrices. La fréquence 0 correspond à une composante de signal constante (offset).

On dispose à présent d’un analyseur de spectre permettant d’examiner les fondamentales et les harmoniques de divers instruments de musiques, de voix humaines ou animales. On trouve un enregistrement de flûte ("wav/flute.wav") et de hautbois ("wav/oboe.wav") tout prêts dans la distribution de TigerJython et l’on voit immédiatement qu’ils présentent des caractéristiques sonores très différentes.

 

 

 

 

SPECTRES DE FONCTIONS QUELCONQUES

 

D’après le théorème de Fourier, toute fonction périodique de fréquence f peut être représentée comme une superposition de fonctions sinusoïdales de fréquences f, 2*f, 3*f, etc. (Séries de Fourier).

Nous pouvons sans problème déterminer expérimentalement l’amplitude des composantes de fréquences avec notre analyseur de Fourier. Dans le cas présent, on considère une onde carrée de fréquence f = 1 kHz. La fonction prédéfinie square(A, f, t) retourne la valeur A durant la première moitié de la période et la valeur -A durant la deuxième moitié.

Dans le programme suivant, on choisit une fréquence d’échantillonnage de fs = 40 kHz et l’on détermine les échantillons sonores sur une durée de 3 secondes (120'000 valeurs). On joue ensuite le son capturé et on n’utilise que 10'000 valeurs pour afficher le spectre.


 

from soundsystem import *
from gpanel import *

def showSpectrum(text):
    makeGPanel(-2000, 22000, -0.2, 1.2)
    drawGrid(0, 20000, 0, 1.0, 10, 5, "blue")
    title(text)
    lineWidth(2)
    r = fs / n # Resolution
    f = 0
    for i in range(n // 2): 
        line(f, 0, f, a[i])
        f += r  

n = 10000
fs = 40000 # Sampling frequency
f = 1000 # Signal frequency

samples = [0] * 120000  # sampled data for 3 s
t = 0
dt = 1 / fs # sampling period
for i in range(120000):
   samples[i] = square(1000, f, t)
   t += dt

openMonoPlayer(samples, 40000)
play()
a = fft(samples, n)
showSpectrum("Spectrum Square Wave")
Hihliht proram code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

L’expérience montre que le spectre d’une fonction rectangulaire est constitué des multiples impairs de la fréquence fondamentale et que les amplitudes de ces composantes de fréquences sont les termes de la suite 1, 1/3, 1/5, 1/7, etc. Il n’est cependant pas possible de déterminer par l’expérience que les composantes spectrales s’étendent à l’infini d’un point de vue théorique.

 

 

SONOGRAMME

 

La transformation de Fourier rapide est un outil idéal pour enregistrer le comportement spectral d’un son variant dans le temps tel qu’un mot parlé. Bien entendu, le signal n’est dans ce cas plus périodique mais on peut faire l’hypothèse qu’il est périodique par morceaux. C’est la raison pour laquelle l’algorithme FFT est souvent utilisé, à intervalles réguliers de 2.5 ms, sur de courts blocs de signaux de 100 ms par exemple. On obtient de ce fait un nouveau spectre toutes les 2.5 ms qui peut être représenté par des bandes verticales colorées dans un sonogramme.

In your proram, you start at the beinnin of the samplin values and analyze a block lenth of  2000 values. You bein the next block 50 samples later, etc. In Python, you can do this with a slice operation

samples[k * 50:]   where k = 0, 1, 2,...

This results in a sonoram, for example for the spoken word "harris", located in the distribution of TierJython as "wav/harris.wav".


 

from soundsystem import *
from gpanel import *

def toColor(z):
    w = int(450 +  300 * z)
    c = X11Color.wavelengthToColor(w)
    return c

def drawSonogram():
    makeGPanel(0, 190, 0, 1000)
    title("Sonogramm of 'Harris'")
    lineWidth(4)
    # Analyse blocks every 50 samples
    for k in range(191):
        a = fft(samples[k * 50:], n)
        for i in range(n // 2):
            setColor(toColor(a[i]))
            point(k, i)

fs = 20000 # Sampling freq->spectrum 0..10 kHz
n = 2000 #  Size of block for analyser

samples = getWavMono("wav/harris.wav")
openMonoPlayer(samples, fs)
play()
drawSonogram()
Hihliht proram code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Le sonogramme produit présente sur l’axe vertical les fréquences entre 0 et 10 kHz et, horizontalement, l’évolution temporelle du spectre de 0 à 190 * 50 / 20000 = 0.475 s.

Pour effectuer la conversion entre les valeurs numériques et les couleurs, on utilise la fonction X11Color.wavelengthToColor() qui permet de convertir des longueurs d’onde du spectre visible compris entre 380 et 780 nm en une couleur affichable à l’écran.

À la fin du mot, lorsque le « s » sifflant est prononcé, les hautes composantes spectrales sont clairement visibles, alors que les fondamentales sont complètement absentes.

 

 

SPECTRE DE LA LUMIÈRE

 

La lumière peut également être décomposée de manière spectrale pour déterminer la longueur d’onde de ses différentes composantes. Les longueurs d’onde du spectre visible s’étendent entre 380 nm et 780 nm.

Il est fort probable que vous connaissiez déjà l’analyseur de spectre pour la lumière, à savoir le prisme. Celui-ci fonctionne en réfractant davantage les longueurs d’ondes bleues (380 nm) que les rouges (780 nm), selon la loi de la réfraction.

 

Le programme suivant simule la transition d’un rayon lumineux blanc provenant du vide et pénétrant dans le verre en montrant le chemin emprunté par les différentes couleurs au moyen d’un agrandissement.

from gpanel import *

# K5 glass
B = 1.5220
C = 4590  # nanometer^2
# Cauchy equation for refracting index
def n(wavelength):
    return B + C / (wavelength * wavelength)

makeGPanel(-1, 1, -1, 1)
title("Refracting at the K5 glass")
bgColor("black")
setColor("white")
line(-1, 0, 1, 0)


lineWidth(4)
line(-1, 1, 0, 0)
lineWidth(1)

sineAlpha = 0.707

for i in range(51):
     wavelength = 380 + 8 * i
     setColor(X11Color.wavelengthToColor(wavelength))
     sineBeta = sineAlpha / n(wavelength)
     x = (sineBeta - 0.45) * 100 - 0.5  # magnification
     line(0, 0, x, -1)
Hihliht proram code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Afin d’obtenir un beau graphique, la lumière est davantage réfractée que dans la réalité.

 

 

EXERCISES

 

1.


Utiliser l’analyseur spectral développé dans ce chapitre pour étudier d’autres instruments ou voix en visualisant leur fondamentale et leurs harmoniques. Essayer d’interpréter les résultats en fonction du caractère sonore de l’instrument en question.


2.


En plus de la fonction globale square(A, f, t), on dispose également des fonctions sine(a, f, t), triangle(A, f, t) et sawtooth(A, f, t). Tentez de prédire l’aspect du spectre d’une onde triangulaire ou d’une onde en dent de scie. Vous pouvez également analyser la superposition d’ondes sinusoïdales générées à l’aide de la fonction sine().


3.


À l’aide de sonogrammes, analyser les différences que présentent différentes voix féminines et masculines prononçant le même mot.