Projet Java Traitement du son – égaliseur

publicité
Cours "Introduction à l’informatique"
6 juin 2008
Projet Java
Traitement du son – égaliseur
Le but des séances thématiques vise à réaliser une application permettant de
lire des fichiers audio et d’y appliquer des transformations basiques (égaliseur,
effets, ...). Dans un premier temps, on travaille sur le modèle (opérations sur les
fichiers audio). Dans la dernière séance, on mettra au point l’interface graphique
(vue et contrôleur).
Séance 1
Voici quelque fichiers son wave : auprintemps.wav http://www.loria.fr/
~canon/java/auprintemps.wav, bo.wav http://www.loria.fr/~canon/java/
bo.wav, salmonDance.wav http://www.loria.fr/~canon/java/salmonDance.
wav.
But de ce tp : répondre aux questions suivantes en complétant le code fourni
Sound.java.
Question 1
Commencer par jouer auprintemps.wav sur la carte son avec votre programme. Il s’agit simplement de tester votre configuration et d’importer correctement les fichiers son (import -> file system -> fichiers à importer).
Question 2
Écrire une fonction permettant de sommer deux signaux. Il est impératif de
ne pas comprendre les détails du fonctionnement du programme pour cela. Il
suffit de savoir qu’un signal est encodé dans un tableau d’octets (byte) et qu’il
est nécessaire d’en extraire les deux composantes (voix droite et gauche) pour
le manipuler. Ensuite, chaque élément du tableau représente une valeur qui sera
transmise à la carte son. Pour l’addition, il vous faut donc décomposer le signal
1
(en voix droite et gauche), additionner les deux tableaux (valeur par valeur) et
reformer le tableau de byte initial (grâce à la primitive mix ). La méthode doit
donc prendre deux arguments (deux tableaux de bytes) et renvoyer un tableau
de même type. S’inspirer des signatures des fonctions existantes dans le code.
Question 3
Écrire une fonction permettant de multiplier un signal par une constante (on
augment ainsi l’amplitude du signal et donc le volume du son).
Question 4
Écrire une fonction permettant de générer un signal sinusoïdal dont on précise la fréquence et l’amplitude (utiliser la méthode Math.sin http: // java.
sun. com/ j2se/ 1. 4. 2/ docs/ api/ java/ lang/ Math. html# sin( double) ). Pour
préciser la fréquence, il faut partir des donnés suivantes : il y a 44100 échantillons par seconde et la période d’un sinus est 2π. Le spectre audible se situe
entre 200 Hz et 20k Hz.
Question 5
Écrire une fonction permettant de concaténer deux signaux (concaténer signifie que l’on juxtapose l’un après l’autre les deux signaux).
Question 6
Réaliser les effets écho et chorus décrits sur cette page http://en.wikipedia.
org/wiki/Sound_effect#Techniques. Écrire pour cela deux fonctions en précisant la durée du décalage et le nombre d’écho en argument.
Remarques :
1. faites des expériences avec vos fonctions : écoutez vos sinusoïdes, mixez-les,
manipulez les fichiers sonores. . .
2. il serait mieux (pourquoi ?) de faire toutes les opérations en considérant
les voix gauches et droites comme des tableaux de doubles et non d’entiers,
et faire la conversion au dernier moment.
Séance 2
Avant d’entamer cette partie, il faut finir au moins un effet sonore parmi les
suivants : addition d’un signal sinusoïdal à un signal sonore lu, chorus, écho,
inversion du signal (lecture arrière), . . .
Il sera possible de compléter la liste des effets implémentés par la suite.
2
Fig. 1 – Signal temporel
Question 1
Récupérer la bibliothèque numérique flanagan sur cette page http://www.
ee.ucl.ac.uk/~mflanaga/java/. Elle vous permettra de réaliser la FFT. Lire
la documentation associée http://www.ee.ucl.ac.uk/~mflanaga/java/FourierTransform.
html (partie constructor et méthodes setData, getTransformedDataAsComplex,
transform et inverse). Vous aurez également besoin de la classe Complex http:
//www.ee.ucl.ac.uk/~mflanaga/java/Complex.html et des méthodes timesEquals et getReal.
Pour importer le jar externe au projet : Project -> Properties -> Java Build
Path -> Libraries -> Add Externel Jar.
Un signal sonore s est représenté par N échantillons. On supposera ce signal
périodique (de période N). On calcul sa transformé de Fourier S par :
S(k) =
N
−1
X
s(i) exp(
i=0
−2iπki
)
N
On passe donc du signal temporel (figure 1) au spectre fréquentiel (figure 2) :
Que dire de la transformée de Fourier S si le signal est réel (ce qui est notre
cas) ? Plus précisément, quel est le conjugué de S(k) ? À quoi correspond S(0) ?
Question 2
Réaliser la transformé de Fourier d’un signal (voix droite et gauche) puis
calculer la transformé inverse et vérifier que cette composition des 2 fonctions
respecte bien l’identité (en écoutant le signal par exemple). La procédure générale consiste à d’abord convertir les tableaux d’entiers en tableaux de complexes
3
Fig. 2 – Spectre fréquentiel
puis à calculer la transformé de Fourier. On réalise ensuite l’opération inverse,
en calculant la transformé inverse puis en convertissant le tableau de complexes
en tableau d’entiers.
Question 3
À partir de ce qui vient d’être réalisé, il est désormais possible de réaliser
des opérations sur le spectre fréquentiel du signal. Il est rappelé que le spectre
obtenu couvre les fréquences jusqu’à la fréquence de 22,05kHz (ce qui est la
moitié de la fréquence d’échantillonage). Réaliser une fonction qui prend un
tableau d’octets en argument et retourne un tableau de type similaire dont les
signaux sonores contenus ne contiennent pas de fréquence supérieur à 1kHz.
Tester en écoutant successivement le signal initial, puis celui altéré.
Question 4
Réaliser une fonction qui égalise le spectre fréquentiel suivant des valeurs
données (comme les égaliseurs de lecteur multimédia standard). On considère
donc plusieurs plages de fréquences et pour chacune, on définit un coefficient
qui sera appliqué à toute les valeurs de la transformé de Fourier afin d’amplifier
ou d’atténuer certaine composantes du signal.
Il sera par exemple possible d’ajouter un signal sinusoïdal au signal sonore
puis de filtrer la plage de fréquences dans laquelle se situe le sinus afin de restaurer en partie le signal d’origine.
Question 5
Revenir sur la séance précédente et finir les effets qui vous semblent les plus
intéressants. Ne pas hésiter à en proposer d’autre.
4
Fig. 3 – Interface et fonctionnalités minimales
Nettoyer le code de manière à disposer d’un maximum de fonctions travaillant sur des tableaux de bytes (en les convertissant en interne en tableau de
réels).
Améliorer l’égaliseur en proposant des plages de fréquence non-linéaire (10100, 100-500, 500-1500, 1500-7000, 7000-20000, par exemple) soit de façon prédéfinie, soit logarithmiquement. Proposer une solution pour que la pondération
des fréquences voisines de plages distinctes soient moins abruptes. Découper le
signal pour réaliser la transformé morceau par morceau et gagner ainsi en temps
de traitement.
Séance 3
Remarques
Eclipse limite la quantité de mémoire disponible et cela peut entraîner des
erreurs à l’exécution. Une exception en rapport avec le heap space est alors
levée. Pour contourner le problème, il faut ajouter un argument : Run -> Run...
-> Onglet “Arguments” -> VM arguments -> ajouter “-Xmx512M” (sans les
guillemets).
Une nouvelle classe Sound Sound.java est disponible. Elle permet de stopper la lecture et sera en outre indispensable à la réalisation d’une application
graphique utilisable.
Question 1
Créer une classe visuelle GUI qui permettra de lancer la lecture d’un fichier
sonore et de sélectionner les effets sonores qui lui sont appliqués. Les fonctionnalités minimales exigées sont illustrées par l’exemple de la figure 3.
Pour la manipulation de la classe GUI, il faudra que la fonction main comporte les lignes suivantes :
JFrame gui = new GUI() ;
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) ;
5
Fig. 4 – Interface et fonctionnalités normales
gui.setVisible(true) ;
Toutes les autres opérations (application d’un effet ou non) se fera dans les
méthodes de la classe GUI.
Question 3
S’il n’y a pas d’effet ou de filtrage quelconques, revenir aux séances précédentes pour compléter le modèle.
Question 4
Une réalisation raisonable devrait ressembler à la figure 4.
Question 5
Il s’agit désormais de réaliser un traceur de "fonctions" pour visualiser le
spectre de vos signaux.
Créer 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) ;
6
qui 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 fen
frame.getContentPane().add(fig) ;
frame.setSize(400, 300) ; // taille de la fenetre, que vous pouvez redimensionner
frame.setVisible(true) ;
Écrire une fonction TracerGraphe prenant en argument un tableau de double et
traçant le graphique correspondant. Tester.
7
Téléchargement