Les consignes sont simples. Suivez-les pas à pas dans l'ordre, et vous n'aurez pas d'ennuis
1/ Lire un MP3
Pour commencer, vous devez créer un programme qui lit un fichier MP3. Vous n'avez qu'à reprendre la chanson
"Home" du groupe Hype que nous avons utilisée dans le chapitre sur FMOD pour illustrer le fonctionnement de la lecture d'une musique. Si vous avez bien suivi ce chapitre, il ne vous faudra pas plus de quelques minutes pour arriver à le faire

Je vous conseille au passage de placer le MP3 dans le dossier de votre projet.
2/ Activer le module DSP de FMOD
Je vous avais dit dans le chapitre sur FMOD que nous n'avions pas vu toutes les possibilités de cette librairie. Elle peut lire aussi des CD, effectuer des enregistrements, des modifications sur le son, etc etc...
Nous allons ici nous intéresser à un de ses modules, appelé DSP.
A quoi servent les fonctions du module DSP ?
Eh bien ce sont justement elles qui nous donnent des informations sur le son, afin que nous puissions réaliser notre visualisation spectrale.
Il va falloir activer ce module.
Pourquoi ce module n'est-il pas activé par défaut ?
En fait, cela demande pas mal de calculs supplémentaires à l'ordinateur. Comme on n'en a pas toujours besoin, le module DSP est désactivé par défaut. Heureusement, l'activer est très simple :
Code : C1 | FSOUND_DSP_SetActive(FSOUND_DSP_GetFFTUnit(), 1);
|
Le premier paramètre doit toujours être le résultat renvoyé par la fonction
FSOUND_DSP_GetFFTUnit().
Quant au second paramètre, c'est un booléen :
- Si on envoie VRAI (1), le module DSP est activé.
- Si on envoie FAUX (0), le module DSP est désactivé.
Par conséquent, si on veut désactiver le module DSP juste avant la fin du programme, il suffit d'écrire :
Code : C1 | FSOUND_DSP_SetActive(FSOUND_DSP_GetFFTUnit(), 0);
|
3/ Récupérer les données spectrales du son
Pour comprendre comment la visualisation spectrale fonctionne, il est indispensable que je vous explique un peu comment ça marche à l'intérieur (rapidement hein, pas dans le détail, sinon ça va se transformer en cours de maths

).
Un son peut être découpé en fréquences. Certaines fréquences sont basses, d'autres moyennes, et d'autres hautes.
Ce que nous allons faire dans notre visualisation, c'est afficher la quantité de chacune de ces fréquences sous formes de barres. Plus la barre est grande, plus la fréquence est utilisée.
Sur la gauche de la fenêtre, nous faisons donc apparaître les basses fréquences, et sur la droite les hautes fréquences.
Mais comment récupérer les quantités de chaque fréquence ?
FMOD nous mâche le travail

Maintenant que le module DSP est activé, on peut faire appel à la fonction
FSOUND_DSP_GetSpectrum().
Il n'y a aucun paramètre à lui donner. En revanche, elle vous renvoie quelque chose de très intéressant :
un pointeur vers un tableau de 512 float.
Rappel : le type float est un type décimal, au même titre que double. La différence entre les deux vient du fait que double est plus précis que float, mais dans notre cas le type float est suffisant. C'est celui utilisé par FMOD ici, donc c'est celui que nous devrons utiliser nous aussi.
Il vous faudra donc déclarer un pointeur vers float. La fonction
FSOUND_DSP_GetSpectrum() vous renvoie le pointeur sur le tableau,
donc sur le premier élément.
En clair, on déclare le pointeur :
Code : C
Et lorsque la musique est en train d'être jouée et que le module DSP est activé, on peut récupérer l'adresse du tableau de 512 float que nous a créé FMOD :
Code : C1 | spectre = FSOUND_DSP_GetSpectrum();
|
On peut ensuite parcourir ce tableau pour obtenir les valeurs de chacune des fréquences :
Code : C1
2
3
4
5
6
7 | spectre[0] // Fréquence la plus basse (à gauche)
spectre[1]
spectre[2]
...
spectre[509]
spectre[510]
spectre[511] // Fréquence la plus haute (à droite)
|
Chaque fréquence est un nombre décimal compris entre 0 (rien) et 1 (maximum). Votre travail va consister à afficher une barre plus ou moins grande en fonction de la valeur que contient chaque case du tableau.
Par exemple, si la valeur est 0.5, vous devrez tracer une barre dont la hauteur correspondra à la moitié de la fenêtre.
Si la valeur est 1, elle devra faire toute la hauteur de la fenêtre.
Généralement, les valeurs sont assez faibles (plutôt proches de 0 que de 1). Je recommande de multiplier par 4 toutes les valeurs pour mieux voir le spectre.
ATTENTION : si vous faites ça, vérifiez que vous ne dépassez pas 1 (arrondissez à 1 s'il le faut). Si vous vous retrouvez avec des valeurs supérieures à 1, vous risquez d'avoir des problèmes pour tracer les barres verticales par la suite !
Mais les barres doivent bouger au fur et à mesure du temps non ? Comme le son change tout le temps, il faut mettre à jour le graphique. Comment faire ?
Bonne question. En effet, le tableau de 512 floats que vous renvoie FMOD
change toutes les 25 ms (pour être à jour par rapport au son actuel). Il va donc falloir dans votre code que vous relisiez le tableau de 512 floats (en refaisant appel à
FSOUND_DSP_GetSpectrum()) toutes les 25 ms, puis que vous mettiez à jour votre graphique en barres.
Relisez le chapitre sur la gestion du temps en SDL pour vous rappeler comment faire

Vous avez le choix entre une solution à base de GetTicks ou à base de callbacks. Faites ce qui vous paraît le plus facile.
4/ Réaliser le dégradé
Dans un premier temps, vous pouvez réaliser des barres de couleur unie. Vous pourrez donc créer des surfaces. Il devra y avoir 512 surfaces : une pour chaque barre. Chaque surface fera donc 1 pixel de large et la hauteur des surfaces variera en fonction de l'intensité de chaque fréquence.
Toutefois, je vous propose ensuite d'effectuer une amélioration : la barre doit fondre vers le rouge lorsque le son devient de plus en plus intense. En clair, la barre doit être verte en bas et rouge en haut.
Mais... une surface ne peut avoir qu'une seule couleur si on utilise SDL_FillRect(). On ne peut pas faire de dégradé !
En effet. On pourrait certes créer des surfaces de 1 pixel de large et 1 pixel de haut pour chaque couleur du dégradé, mais ça ferait vraiment beaucoup de surfaces à gérer et ce ne serait pas très optimisé !
Comment fait-on pour dessiner pixel par pixel ?
Je ne vous l'ai pas appris auparavant car cette technique ne méritait pas un chapitre entier. Vous allez voir en effet que ce n'est pas bien compliqué.
En fait,
la SDL ne propose aucune fonction pour dessiner pixel par pixel. Mais on a le droit de l'écrire nous-même
Pour ce faire, il faut suivre ces étapes méthodiquement dans l'ordre :
- Faites appel à la fonction SDL_LockSurface pour annoncer à la SDL que vous allez modifier la surface manuellement. Cela "bloque" la surface pour la SDL et vous êtes le seul à y avoir accès tant que la surface est bloquée.
Ici, je vous conseille de ne travailler qu'avec une seule surface : l'écran. Si vous voulez dessiner un pixel à un endroit précis de l'écran, vous devrez donc bloquer la surface ecran :
Code : C
- Vous pouvez ensuite modifier le contenu de chaque pixel de la surface. Comme la SDL ne propose aucune fonction pour faire ça, il va falloir l'écrire nous-même dans notre programme.
Cette fonction, je vous la donne. Je la tire de la documentation de la SDL. Elle est un peu compliquée car elle travaille sur la surface directement et gère toutes les profondeurs de couleurs (bits par pixel) possibles. Pas besoin de la retenir ou de la comprendre, faites simplement un copier-coller dans votre programme pour pouvoir l'utiliser :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | void setPixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
int bpp = surface->format->BytesPerPixel;
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
|
Elle est simple à utiliser. Envoyez les paramètres suivants :
- Le pointeur vers la surface à modifier (cette surface doit préalablement avoir été bloquée avec SDL_LockSurface)
- La position en abscisse du pixel à modifier dans la surface (x).
- La position en ordonnée du pixel à modifier dans la surface (y).
- La nouvelle couleur à donner à ce pixel. Cette couleur doit être au format Uint32, vous pouvez donc la générer à l'aide de la fonction SDL_MapRGB() que vous connaissez bien maintenant

- Enfin, lorsque vous avez fini de travailler sur la surface, il ne faut pas oublier de la débloquer en appelant SDL_UnlockSurface.
Code : C1 | SDL_UnlockSurface(ecran);
|
Code résumé d'exemple
Si on résume, vous allez voir que c'est tout simple.
Ce code dessine un pixel rouge au milieu de la surface
ecran (donc au milieu de la fenêtre).
Code : C1
2
3 | SDL_LockSurface(ecran); /* On bloque la surface */
setPixel(ecran, ecran->w / 2, ecran->h / 2, SDL_MapRGB(ecran->format, 255, 0, 0)); /* On dessine un pixel rouge au milieu de l'écran */
SDL_UnlockSurface(ecran); /* On débloque la surface*/
|
Débrouillez-vous avec ça pour réaliser les dégradés du vert au rouge.
Un indice : il faut utiliser des boucles 
(quoi vous aviez deviné tout seul ?
)
Alors, comment vous avez trouvé le sujet ?

Il est pas bien difficile à appréhender, il faut juste faire quelques calculs surtout pour la réalisation du dégradé. C'est du niveau de tout le monde, il faut juste réfléchir un petit peu.
Certains mettent plus de temps que d'autres pour trouver la solution. Si vous avez du mal, ce n'est pas bien grave. Ce qui compte c'est de finir par y arriver.
Quel que soit le projet dans lequel vous vous lancez, vous aurez forcément des petits moments où il ne suffit pas de savoir programmer, il faut aussi
être logique et bien réfléchir.
Je vous donne le code complet ci-dessous. Il est suffisamment commenté.
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 | #include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>
#include <FMOD/fmod.h>
#define LARGEUR_FENETRE 512 /* DOIT rester à 512 impérativement car il y a 512 barres (correspondant aux 512 floats) */
#define HAUTEUR_FENETRE 400 /* Vous pouvez la faire varier celle-là par contre :o) */
#define RATIO (HAUTEUR_FENETRE / 255.0)
#define DELAI_RAFRAICHISSEMENT 25 /* Temps en ms entre chaque mise à jour du graphe. 25 ms est la valeur minimale. */
void setPixel(SDL_Surface *surface, int x, int y, Uint32 pixel);
int main(int argc, char *argv[])
{
SDL_Surface *ecran = NULL;
SDL_Event event;
int continuer = 1, hauteurBarre = 0, tempsActuel = 0, tempsPrecedent = 0, i = 0, j = 0;
float *spectre = NULL;
/* Initialisation de FMOD
----------------------
On charge FMOD, la musique, on active le module DSP et on lance la lecture
de la musique */
FSOUND_Init(44100, 4, 0);
FSOUND_STREAM* musique = FSOUND_Stream_Open("Hype_Home.mp3", 0, 0, 0);
if (musique == NULL)
{
fprintf(stderr, "Impossible d'ouvrir la musique");
exit(EXIT_FAILURE);
}
FSOUND_DSP_SetActive(FSOUND_DSP_GetFFTUnit(), 1);
FSOUND_Stream_Play(FSOUND_FREE, musique);
/* Initialisation de la SDL
------------------------
On charge la SDL, on ouvre la fenêtre et on écrit dans sa barre de titre.
On récupère au passage un pointeur vers la surface ecran, qui sera la seule
surface utilisée dans ce programme */
SDL_Init(SDL_INIT_VIDEO);
ecran = SDL_SetVideoMode(LARGEUR_FENETRE, HAUTEUR_FENETRE, 32, SDL_SWSURFACE | SDL_DOUBLEBUF);
SDL_WM_SetCaption("Visualisation spectrale du son", NULL);
/* Boucle principale */
while (continuer)
{
SDL_PollEvent(&event); /* On doit utiliser PollEvent car il ne faut pas attendre d'évènement
de l'utilisateur pour mettre à jour la fenêtre*/
switch(event.type)
{
case SDL_QUIT:
continuer = 0;
break;
}
/* On efface l'écran à chaque fois avant de dessiner le graphe (fond noir */
SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 0, 0, 0));
/* Gestion du temps
-----------------
On compare le temps actuel par rapport au temps précédent (dernier passage dans la boucle)
Si ça fait moins de 25 ms (DELAI_RAFRAICHISSEMENT), alors on attend le
temps qu'il faut pour qu'au moins 25 ms se soit écoulées. On met ensuite à
jour tempsPrecedent avec le nouveau temps */
tempsActuel = SDL_GetTicks();
if (tempsActuel - tempsPrecedent < DELAI_RAFRAICHISSEMENT)
{
SDL_Delay(DELAI_RAFRAICHISSEMENT - (tempsActuel - tempsPrecedent));
}
tempsPrecedent = SDL_GetTicks();
/* Dessin du spectre sonore
------------------------
C'est la partie la plus intéressante. Il faut réfléchir un peu à la façon
de dessiner pour y arriver, mais c'est tout à fait faisable (la preuve ^^ )
On récupère le pointeur vers le tableau de 512 floats via FSOUND_DSP_GetSpectrum()
On travaille ensuite pixel par pixel sur la surface ecran pour dessiner les barres.
On fait une première boucle pour parcourir la fenêtre en largeur.
La seconde boucle parcourt la fenêtre en hauteur pour dessiner chaque barre.
*/
spectre = FSOUND_DSP_GetSpectrum(); /* On récupère le pointeur vers le tableau de 512 floats */
SDL_LockSurface(ecran); /* On bloque la surface ecran car on va directement modifier ses pixels */
/* BOUCLE 1 : on parcourt la fenêtre en largeur (pour chaque barre verticale) */
for (i = 0 ; i < LARGEUR_FENETRE ; i++)
{
/* On calcule la hauteur de la barre verticale qu'on va dessiner.
spectre[i] nous renvoie un nombre entre 0 et 1 qu'on multiplie par 4 pour zoomer
afin de voir un peu mieux (comme je vous avais dit). On multiplie ensuite
par HAUTEUR_FENETRE pour que la barre soit agrandie par rapport à la taille
de la fenêtre */
hauteurBarre = spectre[i] * 4 * HAUTEUR_FENETRE;
/* On vérifie que la barre ne dépasse pas la hauteur de la fenêtre
Si tel est le cas on coupe la barre au niveau de la hauteur de la fenêtre */
if (hauteurBarre > HAUTEUR_FENETRE)
hauteurBarre = HAUTEUR_FENETRE;
/* BOUCLE 2 : on parcourt en hauteur la barre verticale pour la dessiner */
for (j = HAUTEUR_FENETRE - hauteurBarre ; j < HAUTEUR_FENETRE ; j++)
{
/* On dessine chaque pixel de la barre à la bonne couleur.
On fait simplement varier le rouge et le vert, chacun dans un sens différent
j ne varie pas entre 0 et 255 mais entre 0 et HAUTEUR_FENETRE. Si on veut l'adapter
proportionnellement à la hauteur de la fenêtre, il suffit de faire le calcul
j / RATIO, où RATIO vaut (HAUTEUR_FENETRE / 255.0)
J'ai dû réfléchir 2-3 minutes pour trouver le bon calcul à faire, mais c'est
du niveau de tout le monde. Il suffit de réfléchir un tout petit peu ^^ */
setPixel(ecran, i, j, SDL_MapRGB(ecran->format, 255 - (j / RATIO), j / RATIO, 0));
}
}
SDL_UnlockSurface(ecran); /* On a fini de travailler sur l'écran, on débloque la surface */
SDL_Flip(ecran);
}
/* Le programme se termine.
On désactive le module DSP, on libère la musique de la mémoire
et on ferme FMOD et SDL */
FSOUND_DSP_SetActive(FSOUND_DSP_GetFFTUnit(), 0);
FSOUND_Stream_Close(musique);
FSOUND_Close();
SDL_Quit();
return EXIT_SUCCESS;
}
/* La fonction setPixel permet de dessiner pixel par pixel dans une surface */
void setPixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
int bpp = surface->format->BytesPerPixel;
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
|
Vous devriez obtenir un résultat correspondant à la capture d'écran que je vous avais montrée au début du chapitre :
Bien entendu, il vaut mieux une animation pour voir le résultat. C'est donc ce que je vous propose ci-dessous
Notez que la compression a réduit la qualité du son et le nombre d'images par seconde.
Le mieux est encore de télécharger le programme complet (avec son code source) pour tester chez soi. Vous pourrez ainsi apprécier le programme dans les meilleures conditions
Il faut impérativement que le fichier "Hype_Home.mp3" soit placé dans le dossier du programme pour qu'il fonctionne (sinon il s'arrêtera de suite).
Si vous n'avez toujours pas la chanson en question, vous pouvez
la télécharger ou bien récupérer une autre chanson à vous et la renommer en "Hype_Home.mp3".