But de ce tp: répondre aux questions suivantes en
complétant le code fourni.
Question 0
Commencez par jouer auprintemps.wav sur la carte son avec votre
programme.
Question 1
Ecrivez une fonction permettant de sommer deux signaux.
Question 2
Ecrivez une fonction permettant de multiplier un signal par une
constante.
Question 3
Ecrivez une fonction permettant de générer un signal
sinusoïdal dont on précise la fréquence et
l'amplitude.
Question 4
Ecrivez une fonction permettant de concaténer deux signaux.
Remarques:
1) faites des expériences avec vos fonctions: écoutez vos
sinusoïdes, mixez-les, manipulez auprintemps.wav...
2) il serait mieux (pourquoi ?) de faire toutes les opérations
en considérant les voix gauches et droites comme des tableaux de
double et non d'int, et faire la conversion au dernier moment.
Faites-le en modifiant Sound.java.
Séance 2
- vendredi 2 juin 2006
Question 0
Terminez les questions de la séance précédente.
Un signal sonore s est représenté par N
échantillons. On supposera ce signal périodique (de
période N). Il est possible de l'interpoler par un
polynôme trigonométrique sous la forme:
s(n) = 1/N \sum_{k=0}^{N-1} S(k) * exp(2*i*pi*n*k/N)
où :
S(k) = \sum_{n=0}^{N-1} s(n) exp(-2*i*pi*k*n/N)
La suite obtenue S est appelée transformée
de Fourier
du signal s.
La formule pour calculer s à partir de S est appelée transformée de Fourier inverse.
Question 1
Que dire de la transformée de Fourier S si le signal s est
réel ? (ce qui est notre cas)
Plus précisément, quel est le conjugué de S(k) ?
On suppose que N est impair (pour des raisons de commodités
d'écriture).
Montrez que:
s(n) = 1/N * [ S(0) + 2 * \sum_{k=1}^{(N-1)/2} Re(
S(k)exp(2*i*pi*n*k/N) ) ]
A quoi correspond S(0) ? A quels termes correspondent les hautes
fréquences du signal ?
Ecrivez une fonction qui calcule la transformée de Fourier
d'un signal, et une autre qui calcule la transformée de Fourier
inverse.
Vérifiez (en écoutant le signal par exemple) que la
composition des deux opérations est bien l'identité (aux
erreurs d'arrondis près).
Question 2
Comment se transforme le signal sonore lorsque vous enlevez les
termes correspondant aux hautes fréquences ou aux basses
fréquences dans la transformée de Fourier ?
Question 3
On va synthétiser des signaux bruités à partir de
"auprintemps.wav" (ou tout autre fichier wav de votre choix).
Nous nous intéresserons à deux grandes familles de bruit:
- bruit impulsionnel: on remplace une proportion p des
échantillons par une valeur tirée uniformément
dans un certain intervalle.
- bruit gaussien additif: on ajoute à chaque échantillon
une valeur donnée comme réalisation d'une variable
aléatoire gaussienne.
Créez deux fonctions permettant de bruiter un signal (bruit
impulsionnel ou bruit gaussien). La classe Random
permet de générer des nombres aléatoires,
uniformément ou selon une distribution gaussienne.
Remarque: une variable aléatoire gaussienne Y de moyenne
m et d'écart-type v est obtenue à partir d'une variable
aléatoire gaussienne X de moyenne 0 et variance 1 par: Y=m+v*X.
Ecoutez les signaux bruités avec vos fonctions pour
différentes valeurs des paramètres.
Question 4
On s'intéresse dans cette question à des filtres
permettant de "débruiter" le signal sonore. On
considérera deux familles de filtres.
- filtres linéaires: on remplace un échantillon du signal
par une combinaison linéaire (dont la somme des poids vaut 1)
d'échantillons voisins. Il s'agit d'une convolution du signal.
- filtres médians: on remplace un échantillon du signal
par la valeur médiane des échantillons voisins.
Écrivez des fonctions permettant de calculer le signal
filtré, en particulier en fonction de la fenêtre d'analyse.
Sachant que F(s*n) = F(s).F(n) (où s désigne le signal, n
le noyau de convolution, * la convolution circulaire, . le produit
terme à terme, et F la transformée de Fourier), quel
effet à une convolution par un noyau gaussien ou un noyau
uniforme sur les fréquences du signal original ?
On rappelle que la transformée de Fourier d'une gaussienne de
variance v est une gaussienne de variance proportionnelle à 1/v.
Expérimentez le débruitage avec ces noyaux.
Séance 3
- vendredi 9 juin 2006
Question 1
Dans cette première question, vous allez
réaliser un traceur de "fonctions" pour visualiser le spectre de
vos signaux.
Créez une classe Graphe
qui étend JPanel:
public class Graphe extends JPanel
(il faut importer java.awt.Graphics et javax.swing.JPanel)
Le constructeur de cette classe admet un tableau unidimensionnel de
doubles (qui correspond à la liste des valeurs que l'on veut
représenter). Ce tableau est stocké dans un attribut tab de la classe Graphe (qui sera
un tableau de même taille).
Ensuite on écrit (surcharge) la méthode paintComponent de prototype:
public void paintComponent(Graphics g)
de manière à tracer le graphe.
Il suffit de tracer des lignes entre les points de coordonnées
(i,tab[i]) à l'aide de la méthode drawLine:
g.drawLine(a,b,c,d); permet de tracer un segment entre les points
(pixels) de coordonnées (a,b) et (c,d).
Ici getSize().width et getSize().height donnent les
longueurs et largeur du JPanel sur lequel on trace le graphique.
Il faudra faire attention au fait que l'axe des ordonnées est
ici inversé par rapport à un graphe de fonction
classique...
Comment inverser les axes ?
Voilà pour la classe Graphe.
Cette classe s'utilise dans le programme principal à l'aide de
la suite d'instructions:
Graphe fig = new Graphe(tab_a_tracer); // tab_a_tracer est un tableau
de doubles
JFrame frame = new JFrame("Mon graphique");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // exit lorsque
l'on ferme la fenetre
frame.getContentPane().add(fig);
frame.setSize(400, 300); // taille de la fenetre, que vous pouvez
redimensionner
frame.setVisible(true);
Ecrivez donc une fonction TracerGraphe
prenant en argument un tableau de double et traçant le graphique
correspondant.
Testez.
Question 2
Vous avez pu vous rendre compte lors de la séance
précédente (question 1) que l'implantation naïve de
la transformée de Fourier est inutilisable dans la pratique (ie
pour des sons un peu longs).
Elle nécessite de l'ordre de O(N^2) opérations, où
N est la longueur du signal analysé. On dispose de la transformée de Fourier rapide
(FFT), qui nécessite de l'ordre de O(N.log(N))
opérations. (Voir les explications au tableau)
Voici les fonctions qui correspondent à
la transformée de Fourier rapide (fft) et à la
transformée de Fourier inverse rapide (ifft). La
transformée de Fourier inverse se déduit facilement de la
transformée de Fourier par conjugaison.
Attention: les fonctions qui
implémentent la fft et la ifft nécessitent des signaux
dont la taille est une puissance de 2. Modifiez le signal
analysé
de manière à satisfaire cette condition (en ajoutant des
0) avant d'appeler fft et ifft.
D'autre part, les coefficients du signal et de la transformée de
Fourier sont supposés complexes: ils sont
représentées comme des double[N][2]: modifiez donc votre
code
pour rester compatibles avec ce format de données.
On remarque que l'implentation proposée n'est pas
très efficace du point de vue de la gestion de la mémoire
(pourquoi ?) et ne permet pas de traiter des signaux trop longs. On se
contentera de faire l'analyse du signal sur des morceaux de 4096
échantillons mis bout-à-bout.
A quoi ressemble le graphe des modules des coefficients de Fourier d'un
signal du genre sin(a*2*pi/4096) (si le signal a pour longueur 4096) ?
Reprenez les questions 1 et 2 de la séance 2 avec les fonctions
fft et ifft, en découpant le signal en morceaux de 4096
échantillons.
Vous pouvez expérimenter en traçant les graphes des
modules des coefficients de Fourier, ou aussi en comparant le graphe du
signal original et celui du signal modifié.
Question 3
Dans le prolongement de la question 4 de la séance 2,
expérimentez les effets de la convolution du signal par des
filtres laissant passer les basses fréquences (dits filtres passe-bas) du type [1 1
1]/3 ou les hautes fréquences (dits filtres passe-haut) du type [-1 0
1]/2.
Faites des essais sur des signaux de différentes natures (voix,
musique...), et ajoutez du bruit plus ou moins fort.
Séance 4
- vendredi 16 juin 2006
Voici la correction de la question 1 de la séance 3: une classe Graphe.java et une fonction
à ajouter à votre code pour utiliser cette classe. Cette
fonction permet de tracer le graphe représentant un tableau de
doubles (si vous travaillez avec des tableaux d'entiers, il suffit de
modifier l'appel à la fonction afficher
ainsi que le constructeur de Graphe.java).
Attention à commenter dans votre code les lignes relatives
à l'envoi du son sur le casque audio (surtout la ligne
System.exit(0); ) sinon rien n'est affiché...
Remarque: la question 3 est
indépendante des questions 1 et 2.
Question 1
On reprend ici la question 2 de la séance
précédente. Il s'agit d'utiliser la transformée de
Fourier rapide. Les fonctions fft et ifft fournies admettent comme
argument des tableaux à deux lignes, qui correspondent
respectivement aux parties réelles et imaginaires. Pour calculer
la fft d'un signal (réel), il faut donc d'abord le copier dans
un tableau à deux lignes dont les éléments de la
deuxième ligne sont nuls.
A quoi ressemble le graphe des modules des coefficients de Fourier
d'une sinusoïde ?
Correction:
int N = 256; // la taille du signal est bien une puissance de 2
double[][] signal = new double [N][2];
double[] signalbis = new double [N];
for (int i=0; i<N; i++) {
signal[i][0] = (double)Math.sin(8*2*i*Math.PI/N); //
creation de la sinusoide
signalbis[i] = signal[i][0]; // creation du tableau
unidimensionnel associe
}
double[][] fsignal = fft(signal); // calcul de la transformee de fourier
double tab[]=new double[N];
for (int i=0;i<N;i++)
tab[i]=fsignal[i][0]*fsignal[i][0]+fsignal[i][1]*fsignal[i][1];
// carre du module
afficher(tab); // affichage du signal: agrandissez la taille de la
fenetre
afficher(signalbis); // affichage du module de la transformee de
Fourier: agrandissez
Fin de la correction
Vous observez deux pics sur le module de la transformée de
Fourier. Est-ce normal ?
Rappel: sin(x) = [exp(ix) - exp (-ix)] / (2i) ...
Question 2
On considère le signal issu du fichier auprintemps.wav (ou tout
autre fichier wav échantillonné sur 44.1kHz et 16 bits,
à moins que vous ne modifiez les paramètres
adéquats dans les routines de lecture...)
Vous appliquerez les traitements suivants sur des morceaux successifs
de taille N=4096 (pour des raisons de débordement mémoire
dans le calcul récursif de la fft).
Annulez (ou diminuez) les coefficients de la transformée de
Fourier qui correspondent au basses fréquences. Ces coefficients
sont répartis symétriquement
autour du coefficient d'indice 0 (car la transformée de Fourier
est périodique), donc annulez les coefficients d'indices 0, 1,
N-1, etc).
Qu'entendez-vous ?
Faites de même pour les hautes fréquences,
réparties symétriquement autour du coefficient d'indice
N/2.
Question 3
On reprend la question 3 de la séance 3 (indépendant de
la fft, en particulier il est inutile de découper le signal en
tranches de taille 4096).
Effectuez une convolution du signal sonore par [1 1
1]/3.
Ceci signifie que vous créez un nouveau signal s2 à
partir du signal s1:
s2[i] = ( s1[i-1] + s1[i] + s1[i+1] ) / 3;
Qu'entendez-vous ? Que se passe-t-il si la taille de la fenêtre
de convolution augmente ? (par exemple taille 7, 21, etc au lieu de 3)
Expérimentez.
Effectuez une convolution du signal sonore par [-1 0
1]/2.
Cette fois: s2[i] = ( s1[i-1] - s1[i+1] ) / 2;
Mêmes questions.
Question 4
Reprenez les questions 2 et 3 avec un signal d'entrée
bruité (cf question 3 de la séance 2).
Expérimentez aussi le filtre médian (question 4 de la
séance 2).
A l'issue de ces quatre séances, merci de m'envoyer un jar de
l'ensemble de votre oeuvre !
Lectures conseillées pour aller plus loin: C. Gasquet et P. Witomski, Analyse
de Fourier et applications, Masson, 1990.
Tout cours de mathématiques du signal.
Les fichiers auprintemps.wav et Sound.java sont fournis par Guillaume
Bonfante.
Contact:
Frédéric Sur, ATER au département d'informatique.