Apprenez à programmer en C !

Apprenez à programmer en C !

Mis à jour le mercredi 4 juin 2014

Depuis que nous avons découvert la SDL, nous avons appris à placer des images dans la fenêtre, à faire interagir l'utilisateur avec le clavier et la souris, à écrire du texte, mais il manque clairement un élément : le son !

Ce chapitre va combler ce manque. Parce que les possibilités offertes par la SDL en matière d'audio sont très limitées, nous allons découvrir ici une bibliothèque spécialisée dans le son : FMOD.

Installer FMOD

Pourquoi FMOD ?

Vous le savez maintenant : la SDL n'est pas seulement une bibliothèque graphique. Elle permet aussi de gérer le son via un module appelé SDL_audio. Alors que vient faire dans ce chapitre une bibliothèque externe qui n'a rien à voir comme FMOD ?

C'est en fait un choix que j'ai fait après de nombreux tests. J'aurais pu vous expliquer comment gérer le son en SDL mais j'ai préféré ne pas le faire.
Je m'explique.

Pourquoi j'ai évité SDL_audio

La gestion du son en SDL est « bas niveau ». Trop à mon goût. Il faut effectuer plusieurs manipulations très précises pour jouer du son. C'est donc complexe et je ne trouve pas ça amusant. Il y a bien d'autres bibliothèques qui proposent de jouer du son simplement.

Autre détail important, la SDL ne permet de jouer que des sons au format WAV. Le format WAV est un format de son non compressé. Une musique de 3 minutes dans ce format prend plusieurs dizaines de Mo, contrairement à un format compressé comme MP3 ou Ogg qui occupe beaucoup moins d'espace (2 à 3 Mo).

En fait, si on y réfléchit bien, c'était un peu pareil avec les images. La SDL ne gère que les BMP (images non compressées) à la base. On a dû installer une bibliothèque supplémentaire (SDL_image) pour pouvoir lire d'autres images comme les JPEG, PNG, GIF, etc.

Eh bien figurez-vous qu'il y a une bibliothèque équivalente pour le son : SDL_mixer. Elle est capable de lire un grand nombre de formats audio, parmi lesquels les MP3, les Ogg, les Midi… Et pourtant, là encore j'ai évité de vous parler de cette bibliothèque. Pourquoi ?

Pourquoi j'ai évité SDL_mixer

SDL_mixer est une bibliothèque qu'on ajoute en plus de la SDL, à la manière de SDL_image. Elle est simple à utiliser et lit beaucoup de formats audio différents. Toutefois, après mes tests, il s'est avéré que la bibliothèque comportait des bugs gênants en plus d'être relativement limitée en fonctionnalités.

C'est donc pour cela que je me suis ensuite penché sur FMOD, une bibliothèque qui n'a certes rien à voir avec la SDL, mais qui a l'avantage d'être puissante et réputée.

Télécharger FMOD

Si je vous raconte tout ça, c'est pour vous expliquer que le choix de FMOD n'est pas anodin. C'est tout simplement parce que c'est la meilleure bibliothèque gratuite que j'ai pu trouver.
Elle est aussi simple à utiliser que SDL_mixer, avec un avantage non négligeable : elle n'est pas buggée.

FMOD permet en outre de réaliser plusieurs effets intéressants que SDL_mixer ne propose pas, comme des effets sonores 3D.

Il existe plusieurs versions de FMOD, en particulier celle destinée à une utilisation sous des OS dits habituels (Linux, Windows, Mac…), elle s'appelle FMOD Ex Programmers API.

Téléchargez donc la version de FMOD Ex correspondant à votre OS. Prenez la version dite « stable ».
Vérifiez en particulier si vous avez un OS 32 bits ou 64 bits (sous Windows, faites un clic droit sur « Ordinateur », puis « Propriétés » pour le savoir).

Télécharger FMOD

Installer FMOD

L'installation fonctionne sur le même principe que les autres bibliothèques, comme la SDL.

Le fichier que vous avez téléchargé est normalement un exécutable (sous Windows), ou une archive (.dmg sous Mac, .tar.gz sous Linux). Je vais détailler ici la procédure sous Windows, mais cela fonctionne sur le même principe sous Mac OS X et Linux.

  1. Installez FMOD Ex sur votre disque. Les fichiers dont nous avons besoin seront placés dans un répertoire similaire à celui-ci : C:\Program Files\FMOD SoundSystem\FMOD Programmers API Win32\api.

  2. Dans ce dossier, vous trouverez la DLL de FMOD Ex (fmodex.dll) à placer dans le répertoire de votre projet. L'autre DLL, fmodexL.dll, sert à effectuer du débogage. Nous n'en ferons pas ici. Retenez surtout que c'est le fichier fmodex.dll que vous devrez livrer avec votre programme.

  3. Dans le dossier api/inc, vous trouverez les .h. Placez-les à côté des autres .h dans le dossier de votre IDE. Par exemple Code Blocks/mingw32/include/fmodex (j'ai créé un dossier spécial pour FMOD comme pour SDL).

  4. Dans le dossier api/lib, récupérez le fichier qui correspond à votre compilateur. Un fichier texte doit vous indiquer quel fichier vous devez prendre :

    • Si vous utilisez Code Blocks, donc le compilateur mingw, copiez libfmodex.a dans le dossier lib de votre IDE.

    • Dans le cas de Code Blocks, c'est le dossier CodeBlocks/mingw32/lib ;

    • Si vous utilisez Visual C++, récupérez le fichier fmodex_vc.lib.

  5. Enfin, et c'est peut-être le plus important, il y a un dossier documentation dans le répertoire de FMOD Ex. Normalement, des raccourcis ont été créés dans le menu Démarrer vers cette documentation. Gardez un œil dessus, car nous ne pourrons pas découvrir toutes les fonctionnalités de FMOD Ex dans ce cours. Vous en aurez très certainement besoin dans peu de temps.

Il reste à configurer notre projet. Là encore, c'est comme les autres fois : vous ouvrez votre projet avec votre IDE favori et vous ajoutez le fichier .a (ou .lib) à la liste des fichiers que le linker doit récupérer.
Sous Code Blocks (j'ai l'impression de me répéter), menu Project > Build Options, onglet Linker, cliquez sur Add et indiquez où se trouve le fichier .a. Si on vous demande « Keep as a relative path ? », je vous conseille de répondre non, mais de toute manière cela devrait fonctionner dans les deux cas.

FMOD Ex est installé, voyons rapidement de quoi il est constitué.

Initialiser et libérer un objet système

La librairie FMOD Ex est disponible pour les deux langages C et C++.
Sa particularité c'est que les développeurs de cette librairie ont gardé une certaine cohérence de syntaxe dans les deux langages. Le premier avantage est que si vous apprenez à manipuler FMOD Ex en C, vous savez le faire en C++ à 95%.

Inclure le header

Avant toute chose, vous avez dû le faire instinctivement maintenant mais ça ne coûte rien de le préciser, il faut inclure le fichier .h de FMOD.

#include <fmodex/fmod.h>

J'ai placé ce fichier dans un sous-dossier fmodex. Adaptez cette ligne en fonction de la position du fichier si c'est différent chez vous.
Si vous êtes sous Linux, l'installation se fait automatiquement dans le dossier fmodex.

Créer et initialiser un objet système

Un objet système est une variable qui nous servira tout au long du programme à définir des paramètres de la librairie.
Rappelez-vous qu'avec SDL par exemple, il fallait initialiser la lib explicitement avec une fonction. Ici, le mode d'emploi est un petit peu différent : au lieu d'initialiser toute la librairie, on travaille avec un objet dont le rôle est de définir le comportement de celle-ci.

Pour créer un objet système, il suffit de déclarer un pointeur sur le type FMOD_SYSTEM. Par exemple :

FMOD_SYSTEM *system;

Pour allouer dans la mémoire cet objet système, on utilise la fonction FMOD_System_Create dont voici le prototype :

FMOD_RESULT FMOD_System_Create(
  FMOD_SYSTEM **  system
);

Remarquons que cette fonction prend en paramètre un pointeur sur un pointeur de FMOD_SYSTEM.
Les plus agiles d'entre vous auront remarqué que lors de la déclaration du pointeur sur FMOD_SYSTEM, il n'a pas été alloué avec malloc() ou une autre fonction. C'est justement la raison pour laquelle la fonction FMOD_System_Create prend un tel paramètre pour, entre autres, allouer le pointeur system.

Concrètement, après avoir déclaré notre objet système, il suffit de faire :

FMOD_SYSTEM *system;
FMOD_System_Create(&system);

Voilà, maintenant on dispose d'un objet système alloué, il ne reste plus qu'à l'initialiser. Pour ce faire, on utilise la fonction FMOD_System_Init dont le prototype est :

FMOD_RESULT FMOD_System_Init(
  FMOD_SYSTEM *  system,
  int  maxchannels, 
  FMOD_INITFLAGS  flags, 
  void *  extradriverdata
);
  • Le paramètre system est le paramètre qui nous intéresse le plus, car c'est le pointeur qu'on veut initialiser.

  • Le paramètre maxchannels est le nombre maximum de canaux que devra gérer FMOD. En d'autres termes, c'est le nombre maximal de sons qui pourront être joués en même temps. Tout dépend de la puissance de votre carte son ; on conseille généralement une valeur de 32 (ce sera suffisant pour la plupart des petits jeux). Pour info, FMOD peut théoriquement gérer jusqu'à 1024 canaux différents, mais à ce niveau ça risque de beaucoup faire travailler votre ordinateur !

  • Le paramètre flag ne nous intéressera pas énormément dans ce cours ; on se contentera de lui donner la valeur FMOD_INIT_NORMAL.

  • Le paramètre extradriverdata ne nous intéressera pas non plus, et on lui donnera comme valeur NULL.

Par exemple, pour déclarer, allouer et initialiser un objet système, on fera comme suit :

FMOD_SYSTEM *system;
FMOD_System_Create(&system);
FMOD_System_Init(system, 2, FMOD_INIT_NORMAL, NULL);

Maintenant, nous disposons d'un objet système prêt à l'emploi.

Fermer et libérer un objet système

On ferme puis on libère un objet système avec deux fonctions :

FMOD_System_Close(system);
FMOD_System_Release(system);

Ai-je vraiment besoin de commenter ce code ?

Les sons courts

Nous commencerons par étudier les sons courts.
Un « son court », comme je l'appelle, est un son qui dure généralement quelques secondes (parfois moins d'une seconde) et qui est généralement destiné à être joué régulièrement.

Quelques exemples de sons courts :

  • un bruit de balle ;

  • un bruit de pas ;

  • un tic-tac (pour faire stresser le joueur avant la fin d'un compte à rebours) ;

  • des applaudissements ;

  • etc.

Bref, ça correspond à tous les sons qui ne sont pas des musiques.
Généralement, ces sons sont tellement courts qu'on ne prend pas la peine de les compresser. On les trouve donc le plus souvent sous le format WAV non compressé.

Trouver des sons courts

Avant de commencer, il serait bien de connaître quelques sites qui proposent des banques de sons. En effet, tout le monde ne veut pas forcément enregistrer les sons chez soi.

Ça tombe bien, le net regorge de sons courts, généralement au format WAV.
Où les trouver ? Ça peut paraître bête, on n'y pense pas forcément (et pourtant on devrait), mais Google est notre ami. Au hasard, je tape « Free Sounds », ce qui signifie « sons gratuits » en anglais, et boum… des centaines de millions de résultats !

Rien qu'avec les sites de la première page, vous devriez trouver votre bonheur.
Personnellement, j'ai retenu FindSounds.com, un moteur de recherche pour sons. Je ne sais pas si c'est le meilleur, mais en tout cas il est bien complet.

En recherchant « gun », on trouve des tonnes de sons de tir de fusil ; en tapant « door » on trouve des bruits de porte (figure suivante), etc.

FindSounds.com propose des sons de porte

Les étapes à suivre pour jouer un son

La première étape consiste à charger en mémoire le son que vous voulez jouer.
Il est conseillé de charger tous les sons qui seront fréquemment utilisés dans le jeu dès le début du programme. Vous les libérerez à la fin. En effet, une fois que le son est chargé en mémoire, sa lecture est très rapide.

Le pointeur

Première étape : créer un pointeur de type FMOD_SOUND qui représentera notre son.

FMOD_SOUND *tir = NULL;
Charger le son

Deuxième étape : charger le son avec la fonction FMOD_System_CreateSound. Elle prend… 5 paramètres :

  • Un objet système dont on a parlé précédemment.

  • Bien sûr cet objet doit être prêt à l'emploi (déclaré, alloué et initialisé).

  • Le nom du fichier son à charger. Il peut être de format WAV, MP3, OGG, etc. Toutefois, il vaut mieux charger des sons courts (quelques secondes maximum) plutôt que des sons longs. En effet, la fonction chargera et décodera tout le son en mémoire, ce qui peut prendre de la place si le son est une musique !

  • Le troisième paramètre est un paramètre flag.

  • Il nous intéresse particulièrement ici, car c'est grâce à lui qu'on pourra dire à FMOD que le son qu'on veut jouer est un son court. Pour ceci, on utilisera la valeur FMOD_CREATESAMPLE.

  • Le quatrième paramètre ne nous intéresse pas, et on mettra NULL comme valeur.

  • Le dernier paramètre est du type FMOD_SOUND ** sound, et c'est ce pointeur-là qu'on utilisera par la suite pour jouer notre son.

  • En quelque sorte, on peut considérer que ce pointeur pointera à l'avenir vers notre son.

Voici un exemple de chargement :

FMOD_System_CreateSound(system, "pan.wav", FMOD_CREATESAMPLE, 0, &tir);

Ici, je charge le son pan.wav. Le pointeur tir fera référence à ce son par la suite.

Si tout se passe bien, la fonction renvoie la valeur FMOD_OK. Sinon, c'est qu'il y a eu un problème lors de l'ouverture du fichier audio (fichier corrompu ou inexistant par exemple).

Jouer le son

Vous voulez jouer le son ? Pas de problème avec la fonction FMOD_System_PlaySound !
Il suffit de lui donner un objet système prêt à l'emploi, un numéro de canal sur lequel jouer ainsi que le pointeur sur le son, et d'autres paramètres qui ne nous intéressent pas et qu'on mettra à NULL ou à 0. Pour le numéro de canal, ne vous prenez pas la tête et envoyez FMOD_CHANNEL_FREE pour laisser FMOD gérer ça.

FMOD_System_PlaySound(system, FMOD_CHANNEL_FREE, tir, 0, NULL);
Libérer le son de la mémoire

Lorsque vous n'avez plus besoin du son, vous devez le libérer de la mémoire.
Il n'y a rien de plus simple, il suffit d'indiquer le pointeur à libérer avec la fonction FMOD_Sound_Release

FMOD_Sound_Release(tir);

Exemple : un jeu de tir

Le mieux maintenant est de résumer tout ce qu'on a vu dans un cas concret de programme écrit en SDL.
Il n'y avait rien de compliqué et, normalement, vous ne devriez avoir aucune difficulté à réaliser cet exercice.

Le sujet

Votre mission est simple : créer un jeu de tir.
Bon, on ne va pas réaliser un jeu complet ici, mais juste la gestion du viseur. Je vous ai justement fait un petit viseur sous Paint (figure suivante).

Le viseur

Bref, voilà les objectifs :

  • Fond de fenêtre : noir.

  • Pointeur de la souris : invisible.

  • L'image du viseur est blittée à la position de la souris lorsqu'on la déplace. Attention : il faut que le CENTRE de l'image soit placé au niveau du pointeur de la souris.

  • Quand on clique, le son pan.wav doit être joué.

Ça peut être le début d'un jeu de tir.
Trop facile ? Ok, alors à vous de jouer !

La correction

Voici le code complet :

#include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <fmodex/fmod.h>

int main(int argc, char *argv[])
{
    SDL_Surface *ecran = NULL, *viseur = NULL;
    SDL_Event event;
    SDL_Rect position;
    int continuer = 1;


    FMOD_SYSTEM *system;
    FMOD_SOUND *tir;
    
    FMOD_RESULT resultat;

    /* Création et initialisation d'un objet système */
    FMOD_System_Create(&system);
    FMOD_System_Init(system, 1, FMOD_INIT_NORMAL, NULL);

    /* Chargement du son et vérification du chargement */
    resultat = FMOD_System_CreateSound(system, "pan.wav", FMOD_CREATESAMPLE, 0, &tir);
    if (resultat != FMOD_OK)
    {
        fprintf(stderr, "Impossible de lire pan.wav\n");
        exit(EXIT_FAILURE);
    }

    /* Initialisation de la SDL */
    SDL_Init(SDL_INIT_VIDEO);

    SDL_ShowCursor(SDL_DISABLE);
    ecran = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
    SDL_WM_SetCaption("Gestion du son avec FMOD", NULL);

    viseur = IMG_Load("viseur.png");

    while (continuer)
    {
        SDL_WaitEvent(&event);

        switch(event.type)
        {
        case SDL_QUIT:
            continuer = 0;
            break;
        case SDL_MOUSEBUTTONDOWN:
            /* Lorqu'on clique, on joue le son */
            FMOD_System_PlaySound(system, FMOD_CHANNEL_FREE, tir, 0, NULL);
            break;
        case SDL_MOUSEMOTION:
            /* Lorsqu'on déplace la souris, on place le centre du viseur à la position de la souris
               ... D'où notamment le "viseur->w / 2" pour réussir à faire cela */
            position.x = event.motion.x - (viseur->w / 2);
            position.y = event.motion.y - (viseur->h / 2);
            break;
        }

        SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 0, 0, 0));
        SDL_BlitSurface(viseur, NULL, ecran, &position);
        SDL_Flip(ecran);
    }

    /* On ferme la SDL */
    SDL_FreeSurface(viseur);
    SDL_Quit();

    /* On libère le son et on ferme et libère l'objet système */
    FMOD_Sound_Release(tir);
    FMOD_System_Close(system);
    FMOD_System_Release(system);

    return EXIT_SUCCESS;
}

La figure suivante vous donne un aperçu du mini-jeu, mais le mieux est encore de voir le résultat en vidéo avec le son sur le web !

Aperçu du mini-jeu du viseur

Voir la vidéo de la gestion du son avec FMOD : le viseur (111 Ko)

Ici, j'ai chargé FMOD avant la SDL et je l'ai libéré après la SDL. Il n'y a pas de règles au niveau de l'ordre (j'aurais tout aussi bien pu faire l'inverse). J'ai choisi de charger la SDL et d'ouvrir la fenêtre après le chargement de FMOD pour que le jeu soit prêt à être utilisé dès que la fenêtre s'ouvre (sinon il aurait peut-être fallu attendre quelques millisecondes le temps que FMOD se charge).
Ceci étant, libre à vous de choisir l'ordre que vous voulez ; c'est un détail.

Le code est, je pense, suffisamment commenté. Il n'y a pas de piège particulier, pas de nouveauté fracassante.

On notera la « petite » difficulté qui consistait à blitter le centre du viseur au niveau du pointeur de la souris. Le calcul de la position de l'image est fait en fonction.

Pour ceux qui n'auraient pas encore compris la différence, voici de quoi il retourne. Pour l'occasion, j'ai réactivé l'affichage du pointeur de la souris pour qu'on voie comment est placé le viseur par rapport au pointeur.

Code incorrect
(viseur mal placé)

Image utilisateurImage utilisateur
position.x = event.motion.x;
position.y = event.motion.y;

Code correct
(viseur bien placé)

Image utilisateurImage utilisateur
position.x = event.motion.x - (viseur->w / 2);
position.y = event.motion.y - (viseur->h / 2);
Idées d'amélioration

Ce code est la base d'un jeu de shoot. Vous avez le viseur, le bruit de tir, il ne vous reste plus qu'à faire apparaître ou défiler des ennemis et à marquer le score du joueur. Comme d'habitude, c'est à vous de jouer. Vous vouliez faire un jeu ? Qu'à cela ne tienne, vous avez le niveau maintenant, et même un code de base pour démarrer un jeu de tir ! Qu'est-ce que vous attendez, franchement ?

Les musiques (MP3, OGG, WMA…)

En théorie, le flag FMOD_CREATESAMPLE permet de charger n'importe quel type de son, y compris les formats compressés MP3, OGG, WMA. Le problème concerne les sons « longs », c'est-à-dire les musiques.

En effet, une musique dure en moyenne 3 à 4 minutes. Or, avec ce flag, la fonction FMOD_System_CreateSound charge tout le fichier en mémoire (et c'est la version décompressée qui est mise en mémoire, donc ça prend beaucoup de place !).

Si vous avez un son long (on va parler de « musique » dorénavant), il est préférable de le charger en streaming, c'est-à-dire d'en charger de petits bouts au fur et à mesure de la lecture ; c'est d'ailleurs ce que font tous les lecteurs audio.

Trouver des musiques

Là, on rentre en terrain miné, épineux, explosif (comme vous préférez).
En effet, la plupart des musiques et chansons que l'on connaît sont soumises au droit d'auteur. Même si vous ne faites qu'un petit programme, il faut verser une redevance à la SACEM (en France du moins).

Donc, mis à part les MP3 soumis à droit d'auteur, que nous reste-t-il ?
Heureusement, il y a des chansons libres de droit ! Les auteurs vous autorisent à diffuser librement leurs chansons, il n'y a donc aucun problème pour que vous les utilisiez dans vos programmes.

Bon, la question maintenant est : « où trouver des musiques libres de droit ? ». On pourrait faire une recherche de « Free Music » sur Google, mais là pour le coup il n'est pas notre ami. En effet, allez savoir pourquoi, on a beau taper le mot « Free », on tombe quand même sur des sites qui nous proposent d'acheter des musiques !

Il existe heureusement des sites qui sont dédiés à la musique libre de droit. Là, je vous recommande Jamendo qui est un très bon site, mais ce n'est pas le seul qui existe dans le domaine.

Les chansons sont classées par style. Vous avez beaucoup de choix. On y trouve du bon, du moins bon, du très très bon, du très très nul… En fait, tout dépend de vos goûts et de votre réceptivité aux différents styles de musique. De préférence, prenez une chanson qui peut servir de musique de fond et qui correspond bien à l'univers de votre jeu.

Pour information, cette chanson provient de l'album « Lies and Speeches » du groupe français « Hype ». Pour en savoir plus sur « Hype », vous pouvez visiter leur page MySpace.

J'ai donc téléchargé l'album et je vais utiliser la chanson « Home » au format MP3.
Vous pouvez la télécharger directement si vous voulez faire des tests en même temps que moi. C'est un des avantages de la musique libre : on peut la copier / distribuer librement, donc ne nous gênons pas.

Les étapes à suivre pour jouer une musique

La seule différence est le flag donné à la fonction FMOD_System_CreateSound.
Au lieu de lui donner le flag FMOD_CREATESAMPLE, on lui donnera les flags suivants : FMOD_SOFTWARE, FMOD_2D et FMOD_CREATESTREAM.
Ne vous attardez pas trop sur la signification de ces flags ; celui qui nous intéresse le plus est FMOD_CREATESTREAM, car c'est lui qui dira à FMOD de charger la musique bout par bout.

Pour utiliser tous ces flags en même temps, on utilisera l'opérateur logique | de cette façon :

FMOD_System_CreateSound(system, "ma_musique.mp3", FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM, 0, &sound);

Et voilà le travail !
Mais ce n'est pas tout. Dans le cas d'une musique, il peut être bien de savoir modifier le volume, gérer les répétitions de la chanson, la mettre en pause ou même l'arrêter. C'est ce genre de choses que nous allons voir maintenant.
Mais avant ça, nous aurons besoin de travailler sur les canaux directement.

Récupérer un canal ou un groupe de canaux

Dans des versions précédentes de la librairie FMOD, le simple numéro d'identification d'un canal suffisait pour pouvoir modifier le volume ou bien mettre en pause une chanson.
Depuis FMOD Ex, il y a eu un petit changement : à partir du numéro de canal, on utilise une fonction qui fournit un pointeur vers ce canal. L'idée est restée la même, seule l'implémentation a changé.
Un canal est défini comme étant du type FMOD_CHANNEL, et la fonction qui permet de récupérer un canal à partir d'un numéro id est FMOD_System_GetChannel.
Par exemple, si j'ai un objet système system et que je veux récupérer le canal n°9, il faut faire :

FMOD_CHANNEL *channel;
FMOD_System_GetChannel(system, 9, &channel);

Rien de plus simple !

  • Le premier paramètre est l'objet système.

  • Le deuxième est le numéro id du canal.

  • Le troisième est l'adresse du pointeur où l'on veut stocker l'information voulue.

Une fois qu'on aura notre pointeur de canal, on pourra facilement manipuler la musique (modifier le volume, mettre en pause…).

Notez qu'on peut aussi récupérer tout un groupe de canaux en un seul pointeur ; ça évite de refaire la même manipulation pour chaque canal distinct.
Le type d'un groupe de canaux est FMOD_CHANNELGROUP, et une des fonctions qui nous intéresse le plus est FMOD_System_GetMasterChannelGroup, car elle permet d'obtenir un pointeur vers la totalité des canaux utilisés par un objet système.
Le mode de fonctionnement de cette fonction est identique à la précédente.

Modifier le volume

Pour modifier le volume, on peut le faire soit pour un canal précis, soit pour tous les canaux.
Par exemple, pour le faire pour tous les canaux, il faut d'abord récupérer un pointeur vers le groupe de canaux, puis utiliser la fonction FMOD_ChannelGroup_SetVolume dont le prototype est :

FMOD_RESULT FMOD_ChannelGroup_SetVolume(
  FMOD_CHANNELGROUP *  channelgroup,
  float  volume
);

Le paramètre channelgroup est celui qu'on vient de récupérer.
Le paramètre volume est du type float, tel que 0.0 correspond au silence, et 1.0 correspond à une lecture pleine puissance (c'est cette valeur qui est par défaut).

Répétition de la chanson

On a souvent besoin de répéter la musique de fond. C'est justement ce que propose la fonction FMOD_Sound_SetLoopCount. Elle prend 2 paramètres :

  • Le pointeur vers la chanson.

  • Le nombre de fois qu'elle doit être répétée. Si vous mettez 1, la chanson sera donc lue deux fois. Si vous mettez un nombre négatif (comme -1), la chanson sera répétée à l'infini.

Avec ce code source, notre musique sera donc répétée à l'infini :

FMOD_Sound_SetLoopCount(musique, -1);
Mettre en pause la chanson

Il y a ici 2 fonctions à connaître :

  • FMOD_Channel_GetPaused(canal, &etat) : indique si la chanson jouée sur le canal indiqué est en pause ou pas. Elle met vrai dans etat si la chanson est en pause, faux si elle est en train d'être jouée.

  • FMOD_Channel_SetPaused(canal, etat) : met en pause ou réactive la lecture de la chanson sur le canal indiqué. Envoyez 1 (vrai) pour mettre en pause, 0 (faux) pour réactiver la lecture.

Ce bout de code de fenêtre SDL met en pause la chanson si on appuie sur la touche P du clavier, et la réactive si on appuie à nouveau sur P.

case SDL_KEYDOWN:
    if (event.key.keysym.sym == SDLK_p) // Si on appuie sur P
    {
        FMOD_BOOL etat;
        FMOD_Channel_GetPaused(canal, &etat);

        if (etat == 1) // Si la chanson est en pause
            FMOD_Channel_SetPaused(canal, 0); // On enlève la pause
        else // Sinon, elle est en cours de lecture
            FMOD_Channel_SetPaused(canal, 1); // On met en pause
    }
    break;

Si on veut appliquer le même traitement à tous les canaux réunis, on utilisera les fonctions FMOD_ChannelGroup_GetPaused et FMOD_ChannelGroup_SetPaused, à la seule différence qu'il faut faire passer comme paramètre un FMOD_CHANNELGROUP au lieu d'un FMOD_CHANNEL.

Stopper la lecture

Il suffit d'appeler FMOD_Channel_Stop pour stopper une musique sur un canal, ou bien FMOD_ChannelGroup_Stop pour un ensemble de canaux. On leur envoie respectivement le pointeur vers le canal ou bien le pointeur vers le groupe de canaux.

Et bien d'autres choses

On peut faire beaucoup d'autres choses, mais je ne vais pas vous les énumérer toutes ici, autant lire la doc ! Je vous invite donc à y jeter un œil si vous cherchez des fonctions supplémentaires.

Libérer la mémoire

Pour décharger la musique de la mémoire, appelez FMOD_Sound_Release et donnez-lui le pointeur.

FMOD_Sound_Release(musique);

Code complet de lecture du MP3

Le code ci-dessous vous montre un programme jouant la musique « Home » qu'on a récupérée sur Jamendo.
La musique est jouée dès le début du programme. On peut la mettre en pause en appuyant sur P.

#include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <fmodex/fmod.h>

int main(int argc, char *argv[])
{
    SDL_Surface *ecran = NULL, *pochette = NULL;
    SDL_Event event;
    SDL_Rect position;
    int continuer = 1;

    FMOD_SYSTEM *system;
    FMOD_SOUND *musique;
    FMOD_RESULT resultat;

    
    FMOD_System_Create(&system);
    FMOD_System_Init(system, 1, FMOD_INIT_NORMAL, NULL);

    /* On ouvre la musique */
    resultat = FMOD_System_CreateSound(system, "hype_home.mp3", FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM, 0, &musique);

    /* On vérifie si elle a bien été ouverte (IMPORTANT) */
    if (resultat != FMOD_OK)
    {
        fprintf(stderr, "Impossible de lire le fichier mp3\n");
        exit(EXIT_FAILURE);
    }

    /* On active la répétition de la musique à l'infini */
    FMOD_Sound_SetLoopCount(musique, -1);

    /* On joue la musique */
    FMOD_System_PlaySound(system, FMOD_CHANNEL_FREE, musique, 0, NULL);

    SDL_Init(SDL_INIT_VIDEO);

    ecran = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
    SDL_WM_SetCaption("Gestion du son avec FMOD", NULL);
    pochette = IMG_Load("hype_liesandspeeches.jpg");
    position.x = 0;
    position.y = 0;

    while (continuer)
    {
        SDL_WaitEvent(&event);
        switch(event.type)
        {
        case SDL_QUIT:
            continuer = 0;
            break;
        case SDL_KEYDOWN:
            if (event.key.keysym.sym == SDLK_p) //Si on appuie sur P
            {
                FMOD_CHANNELGROUP *canal;
                FMOD_BOOL etat;
                FMOD_System_GetMasterChannelGroup(system, &canal);
                FMOD_ChannelGroup_GetPaused(canal, &etat);
            
                if (etat) // Si la chanson est en pause
                    FMOD_ChannelGroup_SetPaused(canal, 0); // On enlève la pause
                else // Sinon, elle est en cours de lecture
                    FMOD_ChannelGroup_SetPaused(canal, 1); // On active la pause
            }
            break;
        }

        SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 0, 0, 0));
        SDL_BlitSurface(pochette, NULL, ecran, &position);
        SDL_Flip(ecran);
    }

    FMOD_Sound_Release(musique);
    FMOD_System_Close(system);
    FMOD_System_Release(system);

    SDL_FreeSurface(pochette);
    SDL_Quit();

    return EXIT_SUCCESS;
}

Histoire d'avoir autre chose qu'une fenêtre noire, j'ai mis la pochette de l'album en image de fond.

Pour apprécier pleinement le résultat, je vous invite à regarder la vidéo du programme en cours d'exécution.

Voir la vidéo d'une musique jouée avec FMOD (730 Ko)

En résumé

  • La SDL possède des fonctionnalités audio limitées et il est plutôt conseillé de se pencher sur une bibliothèque dédiée au son, comme FMOD.

  • On distingue 2 types de sons avec FMOD : des sons courts (un bruit de pas par exemple) et des sons longs (une musique par exemple).

  • Chacun de ces types se lit avec la même fonction mais avec des flags différents en option.

  • FMOD permet de jouer simultanément plusieurs sons différents à l'aide de plusieurs canaux.

Exemple de certificat de réussite
Exemple de certificat de réussite