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 le chapitre sur FMOD, 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/ 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 cela fonctionne à l'intérieur (pas dans le détail non plus, 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 forme de barres. Plus la barre est grande, plus la fréquence est utilisée (figure suivante).
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. On peut faire appel à la fonction
FMOD_Channel_GetSpectrum. Son prototype est le suivant :
Code : C | FMOD_RESULT FMOD_Channel_GetSpectrum(
FMOD_CHANNEL * channel,
float * spectrumarray,
int numvalues,
int channeloffset,
FMOD_DSP_FFT_WINDOW windowtype
);
|
Et voici ses paramètres :
- Le canal sur lequel la musique est jouée. Donc a priori il faut récupérer un pointeur vers ce canal.
- Un tableau de float. Il faut que ce tableau soit déjà alloué, statiquement ou dynamiquement, pour permettre à FMOD de le remplir correctement.
- La taille du tableau. Cette taille doit obligatoirement être une puissance de 2, par exemple 512.
- Ce paramètre sert à définir à quelle sortie on s'intéresse. Par exemple si vous êtes en stéréo, 0 veut dire gauche, et 1 veut dire droite.
- Ce paramètre est un peu plus complexe, et ne nous intéresse pas vraiment dans ce cours. On se contentera de lui donner la valeur FMOD_DSP_FFT_WINDOW_RECT.
Rappel : le type floatest 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.
En clair, on déclare notre tableau de
float :
Code : C
Ensuite, lorsque la musique est en train d'être jouée, on demande à FMOD de remplir le tableau du spectre en faisant par exemple :
Code : C | FMOD_Channel_GetSpectrum(canal, spectre, 512, 0, FMOD_DSP_FFT_WINDOW_RECT);
|
On peut ensuite parcourir ce tableau pour obtenir les valeurs de chacune des fréquences :
Code : C | 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 20 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
float 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 à
FMOD_Channel_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 tendre 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êmes.
Pour ce faire, il faut suivre ces étapes méthodiquement dans l'ordre :
- Faites appel à la fonction SDL_LockSurfacepour 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, copiez-la simplement 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 : C | 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 : C | 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*/
|
Avec cette base vous devriez pouvoir réaliser des dégradés du vert au rouge (un indice : il faut utiliser des boucles

).