Moteur de son utilisant fmod (c++) (wav-midi-mp3-etc...)

Soyez le premier à donner votre avis sur cette source.

Vue 13 442 fois - Téléchargée 836 fois

Description

Ce petit moteur sonore est vraiment le B-A BA de ce
que l'on peut faire avec Fmod.

Il est réalisé en C/C++ mais d'autres
langages aurais aussi bien pu faire l'affaire ...

Il va sagir d'initialiser/désinitialiser Fmod,
de pouvoir ajouter/retirer des sons stockés à l'intérieur
d'un vector (utilisé pour l'occasion comme un petit tableau dynamique),
et de pouvoir les jouer et les stopper quand on le désire.

Donc en résumé, tout ça restera très basique : pas de son 3D, pas d'effets ou de fioritures...
Ce tut est donc réservé aux débutants (dont je fais partie ;-)) désirant avoir une première approche évoluée de ce que peut apporter la prise en charge du son dans un prog...

J'ai réalisé ce SoundEngine sous Visual C++ 6, donc je ne peux
expliquer comment régler l'environnement que sur VS.

Mais la doc de Fmod étant très complète, je vous propose d'aller y jeter
un oeil si vous êtes allergiques à Microsoft, tout sera expliqué en détail pour les autres EDI.

Donc pour commencer vous n'aurez besoin de 3 choses :
- la librairie fmodvc.lib à inclure dans le projet (spécifique ici à VS6 !)
- le fichier d'entête fmod.h
- la dll de Fmod : fmod.dll à mettre soit dans le
répertoire de l'exécutable, soit dans system32
Vous trouverez tout ça en téléchargeant l'api Fmod sur le site de Fmod (http://www.fmod.org)

Après, c'est que du code, mais rien de bien méchant vous allez voir.

Source / Exemple :


1. Création d'une classe intermédiaire, la classe Son

INTERFACE

class Son 
{
      public:
      Son() {}
      ~Son() {}

      //format
      int format;
      FSOUND_STREAM * stream;
      FSOUND_SAMPLE * sample;
      FMUSIC_MODULE * module;

      //charge un fichier son
      BOOL Charger(char *fichAdr, int format);
      void Liberer(void);

      //joue et un stoppe le son
      BOOL Jouer(void);
      void Stopper(void);
};

Vous vous demandez sûrement quels sont les formats de sons évoqués ci-dessus. C'est tout simple. On peut disinguer 3 grandes sortes de sons :
- les gros fichiers WAV/MP3/etc... stockés en stream
- les petits fichiers WAV/MP3/etc... stockés en sample
- les fichiers MIDI/etc... stockés en modules

Remarque : c'est à l'utilisateur lui-même de savoir à l'avance quel format de fichier va être chargé

Remarque : un petit point important : Fmod est séparé en deux plus petites api, FSound et FMusic. Chacune est spécialisée dans le traitement d'une famille de son. FSound pour les stream et sample et FMusic pour les modules.

Pour chaque son il y a donc un pointeur vers chacune de ces structures. 

On définit pour cela quelques constantes qui vont être passées en argument lors du chargement des sons :

//gros mp3/wav -> loop
#define STREAM_TYPE    0
//petits mp3/wav -> joués une fois
#define SAMPLE_TYPE    1
//midi et autres -> loop
#define MODULE_TYPE_MUSIC    2
//midi et autres -> joués une fois
#define MODULE_TYPE_SOUND   3

Les autres fonctions parlant d'elles-même, passons à la réalisation.

Nous n'allons exposer ici que la méthode de chargement. Etant la plus "complexe" (^_^ toute proportion gardée), elle va nous permettre de poser quelques bases :

REALISATION

/* Charge un son en fonction du format spécifié*/
BOOL Son::Charger(char *fichAdr, int format)
{
      BOOL result = TRUE;

      //on stocke le format du son
      this->format = format;

      //on charge suivant le format
      switch(format)
      {
            //stream
            case STREAM_TYPE:
            stream = NULL;
            stream = FSOUND_Stream_OpenFile(fichAdr,                   FSOUND_LOOP_NORMAL, 0);
            if(stream == NULL)
                  result = FALSE;
            break;

            //sample
            case SAMPLE_TYPE:
            sample = NULL;
            sample =                   FSOUND_Sample_Load(FSOUND_FREE, fichAdr,             FSOUND_LOOP_OFF, 0);
            if(sample == NULL)
                  result = FALSE;
            break;

            //module loop
            case MODULE_TYPE_MUSIC:
            module = NULL;
            module = FMUSIC_LoadSong(fichAdr);
            FMUSIC_SetMasterVolume(module, 160);
            if(module == NULL)
                  result = FALSE;
            break;

            //module simple
            case MODULE_TYPE_SOUND:
            module = NULL;
            module = FMUSIC_LoadSong(fichAdr);
            FMUSIC_SetMasterVolume(module, 160);
            FMUSIC_SetLooping(module,FALSE);
            if(module == NULL)
                  result = FALSE;
            break;

            //si aucun init fail
            default:
            result = FALSE;
            break;
      }

      return result;
}

Remarque : j'ai choisi de régler du volume des sons de type module juste après leur chargement. 
Il faut savoir que pour ce type de fichiers particulier, le volume se règle individuellement pour chaque module (de 0 à 255 - 160 étant un bon compromis).
On aurais aussi bien pu régler tout ça par la suite, mais autant le faire depuis le début.... 

Ce qu'il faut bien remarquer dans cette méthode est le passage en paramètre du format du son. Comme je l'ai évoqué plus haut, c'est donc à l'utilisateur de savoir quel sont il va charger au moment de l'appel de la procédure.

J'ai également fait un choix (contestable, mais se révèlant après expérience plutôt concluant) : les répétitions.
C'est-à-dire le nombre de fois qu'un son va être joué d'affilé : les stream et les modules de type musique sont répétées à l'infini, tandis que les sample et les modules de type son ne sont joués qu'une fois.
On aurais très bien pu complexifier cette fonction de chargement pour la rendre plus "customisable"... Mais le principal caractère de ce moteur étant son accessibilité, je n'ai rien inplémenté de tel.

Excepté ces petits détails, aucune difficulté (consultez la doc de Fmod pour les méthodes de chargement (d'ailleurs très simples à utiliser)).

Le reste des méthodes, Jouer/Stopper et Liberer ne necessitent pas selon moi de longues explications si l'on à compris comment était réalisée la méthode Charger.

A noter tout de même que la fonction Charger ne libére rien au moment de charger un son. Pensez donc bien à libérer un son avant de vouloir le recharger à partir d'un autre fichier.

Voilà les autres fonction.

/* Libère le son*/
void Son::Liberer(void)
{
      //si c'est une stream ou un module on libère
      if(format == STREAM_TYPE)
      {
            FSOUND_Stream_Close(stream);
            stream=NULL;
      }
      else if(format == MODULE_TYPE_MUSIC 
             || format == MODULE_TYPE_SOUND)
      {
            FMUSIC_FreeSong(module);
            module=NULL;
      }
}

/* Joue le son en fonction du format*/
BOOL Son::Jouer(void)
{
      //on joue suivant le type de format
      switch(format)
      {
            //stream (loop)
            case STREAM_TYPE:
            if(FSOUND_Stream_Play(FSOUND_FREE,                                                       stream) == -1)
                  return FALSE;
            break;

            //sample
            case SAMPLE_TYPE:
            if(FSOUND_PlaySound(FSOUND_FREE,                                                       sample) == -1)
                  return FALSE;
            break;

            //module (loop)
            case MODULE_TYPE_MUSIC:
            if(FMUSIC_PlaySong(module) == NULL)
                  return FALSE;
            break;

            //module
            case MODULE_TYPE_SOUND:
            if(FMUSIC_PlaySong(module) == NULL)
                  return FALSE;
            break;

            default:
            break;
      }

      return TRUE;
}

/* Stope la lecture en cours d'une stream ou d'un module*/
void Son::Stopper(void)
{
      //si c'est une stream ou un module on stoppe
      if(format == STREAM_TYPE)
            FSOUND_Stream_Stop(stream);
      else if(format == MODULE_TYPE_MUSIC
              ||format == MODULE_TYPE_SOUND)
            FMUSIC_StopSong(module);
} 

2. La classe SoundEngine

Voilà comment elle se présente :

INTERFACE

class SoundEngine
{
      public :
      vector<Son> sons;

      SoundEngine() {}
      ~SoundEngine() {}

      /* Initialise tout : 

  • mixRate = frequence de sortie
  • maxChannels = nombre de sons pouvant être traité à la fois
  • /
BOOL Initialiser(int mixRate, int maxChannels); //On désinitialise tout BOOL Desinitialiser(void); /* Charge un nouveau son :
  • fichAdr = adresse du fichier son
  • format = stream/sample/module (voir constantes)
  • /
BOOL AjouterSon(Son son); BOOL AjouterSon(Son son, int index); BOOL AjouterSon(char *fichAdr, int format); BOOL AjouterSon(char *fichAdr, int format, int index); //Retire un son, soit à un index donné, soit le dernier chargé BOOL RetirerSon(void); BOOL RetirerSon(int index); //Joue le son BOOL Jouer(int index); //Stoppe le son BOOL Stopper(int index); }; On peut distinguer 3 types de méthodes : - Initialisation : Initialiser/Desinitialiser - Stockage : AjouterSon/RetirerSon - Rendu : Jouer/Stopper Pour stocker les sons j'utilise un vector de Son Pour ceux qui ne savent pas ce que c'est qu'un vector, c'est en fait une fonctionnalité faisant partie de la stl - bibliothèque de classes container - permettant en autres de créer de tableaux dynamique efficacement. Il permet d'éviter l'enchainement des new et delete en cascade et ainsi pas mal de problèmes ^_^. Allez faire un tour sur la page des liens si vous voulez en savoir plus. REALISATION Bon, voilà le code : /*Initialisation*/ BOOL SoundEngine::Initialiser(int mixRate, int maxChannels) { //On definit le volume des mp3 et wave FSOUND_SetVolume(FSOUND_ALL,160); return FSOUND_Init(mixRate, maxChannels, FSOUND_INIT_USEDEFAULTMIDISYNTH ); } Remarque : on règle ici le volume des stream et des sample (map3 et wav principalement). Le volume de ces format ne sont pas réglés individuellement pour chaque son, comme pour les modules, mais globalement. A noter que cette fonctionalité est utilisable à n'importe quel moment dans un programme. /*Desinitialisation*/ BOOL SoundEngine::Desinitialiser(void) { BOOL init = TRUE; try { //on stoppe et libère les sons for(int i=0; i<sons.size(); i++) { sons.at(i).Stopper(); sons.at(i).Liberer(); } } catch(...) { init = FALSE; } //on ferme Fmod FSOUND_Close(); return init; } Remarque : attention à ne pas l'oublier, la bonne libération des objets peut éviter bien des soucis par la suite (surtout sous Win9x...) /*Ajoute un son dans le vector - Ver1*/ BOOL SoundEngine::AjouterSon(Son son) { //on le range dans le vector sons.push_back(son); return TRUE; } /*Ajoute un son dans le vector - Ver2*/ BOOL SoundEngine::AjouterSon(Son son, int index) { //si l'index dépasse la capacité du vector if(index < 0 || index > sons.size() - 1) return FALSE; vector<Son>::iterator it; it = sons.begin(); it += index; //on le range dans le vector à l'index indiqué sons.insert(it, son); return TRUE; } /*Ajoute un son dans le vector - Ver3*/ BOOL SoundEngine::AjouterSon(char *fichAdr, int format) { Son temp; if(!temp.Charger(fichAdr, format)) return FALSE; //on le range dans le vector sons.push_back(temp); return TRUE; } /*Ajoute un son dans le vector - Ver4*/ BOOL SoundEngine::AjouterSon(char *fichAdr, int format, int index) { Son temp; //si l'index dépasse la capacité du vector if(index < 0 || index > sons.size() - 1) return FALSE; //on charge un objet temporaire if(!temp.Charger(fichAdr, format)) return FALSE; vector<Son>::iterator it; it = sons.begin(); it += index; //on le range dans le vector à l'index indiqué sons.insert(it, temp); return TRUE; } Remarque : 4 versions pour une même méthode. On peut ainsi ajouter des sons directement à partir d'une classe Son mais également en fournissant simplement le chemin d'accès au fichier ainsi que son format. Il est également possible de choisir où ce son sera stocké dans le vector. /*Retire un son du vector - Ver1*/ BOOL SoundEngine::RetirerSon(void) { //on retire le dernier son chargé sons.pop_back(); return TRUE; } /*Retire un son du vector - Ver2*/ BOOL SoundEngine::RetirerSon(int index) { //si l'index dépasse la capacité du vector if(index > sons.size() - 1 || index < 0) return FALSE; vector<Son>::iterator it; it = sons.begin(); it += index; //on retire le son à l'index indiqué sons.erase(it); return TRUE; } Remarque : 2 versions pour cette méthode. Cela va permettre de pouvoir retirer un son à n'importe quel endroit du vector. /*Joue le son situé à l'index indiqué*/ BOOL SoundEngine::Jouer(int index) { //si l'index dépasse la capacité du vector if(index > sons.size() - 1 || index < 0) return FALSE; sons[index].Jouer(); return TRUE; } /*Stoppe le son indiqué*/ BOOL SoundEngine::Stopper(int index) { //si l'index dépasse la capacité du vector if(index > sons.size() - 1 || index < 0) return FALSE; //on joue le son sons[index].Stopper(); return TRUE; } Remarque : Pour jouer et stopper les sons, on utilise directement la fonction contenue dans la classe Son, après avoir bien sur testé la validité de l'index fournis en paramètre. Voilà, c'est tout pour la création du SoundEngine. Si vous avez des questions n'hésitez pas à me contacter. Passons maintenant à son utilisation au sein de votre programme. 3. Utilisation Tout d'abord, vous devez déclarer une variable de type SoundEngine (ici globale mais on peut adapter). //variable de type SoundEngine SoundEngine soundEngine; //Viens ensuite l'initialisation : //fréquence à 44khz et 32 cannaux soundEngine.Initialiser(44000, 32); //On peut maintant ajouter des sons : //ajout de sons avec une classe Son déjà déclarée Son temp; temp.Charger("grosMP3.mp3", TYPE_STREAM); soundEngine.AjouterSon(temp); temp.Liberer(); temp.Charger("petitWAV.wav", TYPE_SAMPLE); soundEngine.AjouterSon(temp, 1); temp.Liberer(); temp.Charger("midiMusique1.mid", MODULE_TYPE_MUSIC); soundEngine.AjouterSon(temp); temp.Liberer(); temp.Charger("midiSon1.mid", MODULE_TYPE_SOUND); soundEngine.AjouterSon(temp); temp.Liberer(); //ajout de sons par chemin d'accés soundEngine.AjouterSon("petitMP3.mp3", SAMPLE_TYPE); soundEngine.AjouterSon("grosWAV.wav", STREAM_TYPE); soundEngine.AjouterSon("midiMusique1.mid", MODULE_TYPE_MUSIC); soundEngine.AjouterSon("midiSon1.mid", MODULE_TYPE_SOUND, 0); //Par exemple. //On peut maintenant les jouer/stopper. ... //jouera midiSon1.mid soundEngine.Jouer(0); //jouera grosMP3.mp3 soundEngine.Jouer(1); ... //arretera la lecture soundEngine.Stopper(0); //arretera la lecture soundEngine.Stopper(1); ... //jouera grosWAV.wav soundEngine.Jouer(6); ... //arretera la lecture soundEngine.Stopper(6); //A la fin de votre programme il ne vous restera plus qu'à désinitialiser : //désinitialise Fmod et libère tous les sons chargés soundEngine.Desinitialiser(); Voilà c'est tout ! Si vous voyez des erreurs, prévenez-moi je corrigerais tout ça dès que je pourrais. Si vous avez des conseils concernant ce SoundEngine, n'hésitez pas non plus à me contacter ;-). J'ai fournit une archive contenat le fichier d'entête et de réalisation. Bonne lecture. Fab

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

Messages postés
3006
Date d'inscription
dimanche 14 avril 2002
Statut
Membre
Dernière intervention
31 décembre 2008

Regarde, tu peux remplacer ça:

# //module (loop)
# case MODULE_TYPE_MUSIC:
# if(FMUSIC_PlaySong(module) == NULL)
# return FALSE;
# break;
#
# //module
# case MODULE_TYPE_SOUND:
# if(FMUSIC_PlaySong(module) == NULL)
# return FALSE;
# break;

par ça:

# //module
# case MODULE_TYPE_MUSIC:
# case MODULE_TYPE_SOUND:
# if(FMUSIC_PlaySong(module) == NULL)
# return FALSE;
# break;


à part ça, merci pr le code, je vois mieux comment fmod est hiérarchisé !
Messages postés
1
Date d'inscription
jeudi 16 juin 2005
Statut
Membre
Dernière intervention
16 juin 2005

salut

g telechargé la derniere version (3.74) de fmod mais apparement
la fonction "FSOUND_Stream_OpenFile" ne semble pas fonctionner car g le message d erreur suivi à la compilation :
" Undefined symbol '_FSOUND_Stream_OpenFile@12' referenced in "simplest.c". "
Pourtant, il ne pose aucun probleme pour lire les MODULES (s3m,mod,it,midi etc) avec la fonction "FMUSIC_LoadSong"

ce qui est bizarre c que dans la doc, la fonction FSOUND_Stream_OpenFile n'existe pas mais est remplacée par FSOUND_Stream_Open et que dans "fmod.h", c'est le prototype FSOUND_Stream_OpenFile qui apparait...

Quelqu'un peut il me venir en aide, PLEASE!!!

PS : si vous connaissez, je suis sous Labwindows CVI 7.0 (code en C pur)
Messages postés
2
Date d'inscription
samedi 19 mars 2005
Statut
Membre
Dernière intervention
21 mars 2005

Juste une petite remarque à propos de la classe SoundEngine: elle reste limitée du fait qu'il n'est pas possible d'en instancier plusieurs, l'initialisation de FMOD ne pouvant pas se faire 2 fois de suite.
Par contre, connais-tu un moyen d'utiliser FMOD sur 2 périphériques son différents en même temps ?
Par exemple, jouer un son sur une carte son et en jouer un autre en même temps sur une deuxième carte son ?
Messages postés
13
Date d'inscription
dimanche 21 novembre 2004
Statut
Membre
Dernière intervention
15 octobre 2005

C'est du beau travail, mais j'y ai trouvé 2 petites coquilles :
Dans l'utilisation (paragraphe 3), il est écrit :
temp.Charger("grosMP3.mp3", TYPE_STREAM);
et plus bas :
temp.Charger("petitWAV.wav", TYPE_SAMPLE);
Mais il faut mettre STREAM_TYPE et SAMPLE_TYPE à la place de TYPE_STREAM et TYPE_SAMPLE, pour qu'elles soient en accord avec les déclarations des variables dans SoundEngine.h.
Voilà c'est tout.

Une dernière chose : le moteur est compatible avec la dernière version de FMod, c'est qui n'est pas toujours le cas des codes que l'on trouve actuellement :
On voit souvent fsound_stream_openfile à la place de fsound_stream_open.
Bravo.
Messages postés
688
Date d'inscription
jeudi 26 décembre 2002
Statut
Membre
Dernière intervention
19 mai 2005

bien bien :) mais je pense qu'il faut ajouter la gestion de la frequence et la 3d et aussi la rapidité de lecture c ta besoin d'aide : je suis la :)
Afficher les 10 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.