Les modules ? Qu'est-ce donc ?

  • Les modules viennent tout droit du fond des ages, 1987 pour être exact, et ont fait leur apparition sur Amiga.
  • Un définition rapide serait:
    • format de fichier contenant les données de la partition, et les samples spécifiques à la musique .
    • Le fait d'embarquer les samples dans le module permets un rendu de qualité, contrairement au midi où toutes les musiques utilisent les mêmes instruments au rendu approximatif.
  • Dans la pratique, il n'existe pas un seul format de module, mais de nombreux: mod, it, xm, s3m etc... du premier format 4 voix jusqu'aux formats les plus exotiques supportants les plugins VST et 64 voix.


  • Avantages des modules, par rapport aux MP3:
  1. Une taille beaucoup moins importante, grâce au principe de loop des échantillons sonores.
  2. Un décodage ultra rapide.
  3. Une synchronisation parfaite avec le déroulement du programme (des évenements peuvent être programmés à la note près).
  4. Repositionnement rapide et précis dans les patterns (un pattern correspond à un segment de la partition ).
  • Les deux premiers avantages tendent à disparaitre, vu les capacités de stockage actuelles, et la puissance des machines (décoder un mp3 ne prend pratiquement plus de puissance processeur, sur un PC du moins).
  • Les deux derniers avantages permettent de synchroniser la musique avec les évenements.
    • Ceci a déja été exploité dans les démos (principalement, des effets visuels correspondants au tempo de la musique, un peu comme un métronome visuel).
    • Par contre, ceci a été peu exploité dans les jeux: imaginez un jeu d'arcade à la musique variant selon le nombre d'ennemis à l'écran. Cela serait difficile à mettre en place avec des mp3, mais reste tout à fait envisageable avec des modules.


  • Le site Web de MikMod
  • Aujourd'hui, des gens composent encore des modules
  • Chimera: des musiciens de talent, et un ancien contact :-)


MikMod face à la concurrence

  • Bass (site) : license commerciale, closed source
    • Bon rendu sur les modules. Malheureusement, l'auteur a toujours refusé de produire une version Linux. Format propriétaire MO3 permettant de compresser les samples des modules en OGG ou MP3.
  • Fmod (site) : license commerciale, closed source
    • Plutot orienté pour les développeurs console. Versions Windows et Linux (binaires). Joue les OXM (module XM avec samples compressés MP3, format interne à Fmod).
  • MikMod (site) : license libre, open source
    • Très bon rendu des modules (utilisé comme plugin par winamp), permets également de jouer des samples. Versions Linux (OSS,Alsa,ESD), Mac, Windows (WaveOut et DirectSound), OS/2, Unix (AIX, HP, SGI, Ultrasparc), sortie direct to disk (Raw et Wav) et stdout (!!!).
    • Pas de format interne supportant la compression des samples en OGG ou MP3.
  • OpenAL
    • Ne permets pas de jouer des modules (bibliothèque de bas niveau).


Certains benches ont circulés (je ne les ai pas retrouvés) montrant des performances déplorables de MikMod.

La différence s'est peut être vue sur les 486 ou les premiers pentium. Le rendu d'un module prends 0% de mon "vieil" Athlon64. Le contraire eut été malheureux.


Support MikMod sous Windows

Sur le site de MikMod, vous trouverez bien sûr les sources, mais pas de binaires.

Les sources se divisent en deux partie: le player (une interface pour la library) et la library proprement dite.

C'est bien sûr cette dernière que nous allons reprendre.

Pour mes tests, j'ai utilisé la dernière version stable disponible, c'est à dire la version 3.1.11.


Compiler avec mingw et msys

Par défaut, la bibliothèque est fournie avec un makefile pour compiler à l'aide (si l'on peut dire) de Visual Studio.

Ne possédant pas cette chose (et je reste poli), j'ai réécrit un makefile pour Mingw.

  • Indispensable : avant de commencer quoique ce soit, il faut
    • Se placer dans le répertoire win32 et copier le fichier mikmod_build.h dans le répertoire include de mikmod. Autrement, rien ne compilera.
    • Répéter l'opération avec le même fichier, sauf que le fichier de destination se nommera mikmod.h (toujours dans les include de mikmod).
    • Vous devez donc copier deux fois le fichier mikmod_buid.h, une fois en gardant son nom, une fois en le renommant.
  • Compiler avec les support WaveOut, NoSound, stdout, direct to disk (wav et raw)
    • Rien de particulier à faire, tout est inclus de base à la fois dans Windows et dans Mingw.
    • Windows utilise en interne les fonctions WaveOut. C'est un peu plus lent, mais ça marche toujours.
  • Compiler avec DirectSound
    • Il faut récupérer dsound.h et dsound.lib , dans le DDK Microsoft.
    • Afin de vous éviter de charger et d'installer le DDK juste pour ça, j'ai placé ces deux fichiers dans l'archive que vous trouverez à la fin de cet article.
    • Il faut bien sûr placer ces fichiers dans les répertoires include et lib de votre Mingw (pas besoin de renommer dsound.lib).


  • Il n'y a pas grand chose d'autre à signaler. Regardez dans le makefile pour les quelques subtilités:
    • Pour la compilation, il faut se placer dans le répertoire win32, où le Makefile aura été au préalable copié, et lancer make.
# Makefile pour compiler MikMod pour Windows
# $$DATE$$ : dim. 16 décembre 2007 (16:51:49)

loaders=load_669.o load_amf.o load_dsm.o load_far.o load_gdm.o load_imf.o load_it.o load_m15.o load_med.o load_mod.o load_mtm.o load_okt.o load_s3m.o load_stm.o load_stx.o load_ult.o load_uni.o load_xm.o

mmio=mmalloc.o mmerror.o mmio.o

playercode=mdreg.o mdriver.o mdulaw.o mloader.o mlreg.o mlutil.o mplayer.o munitrk.o mwav.o npertab.o sloader.o virtch.o virtch2.o virtch_common.o

drivers=drv_ds.o drv_win.o drv_nos.o drv_stdout.o drv_wav.o drv_raw.o

CXXFLAGS=-O2 -I../include/
#attention pour compiler en direct sound (-DDRV_DS); il faut dsound.h
CXXFLAGS+=-DWIN32 -DDRV_DS -DDRV_WIN -DHAVE_FCNTL_H -DHAVE_MALLOC_H -DHAVE_LIMITS_H
LIBS=-lwinmm -ldsound

mik=mikmod
CC=gcc

#emplacement des sources
VPATH=../loaders ../playercode ../mmio ../drivers

tous_les_objets=$(loaders) $(mmio) $(playercode) $(drivers)
path_obj=objets
# $(objets_avec_chemin) : pour indiquer les objets au linker, construit avec $(path_obj)
objets_avec_chemin=$(patsubst %,$(path_obj)/%,$(tous_les_objets))

$(shell mkdir -p $(path_obj))

all: $(mik).dll
        strip $(mik).dll

$(mik).dll: $(objets_avec_chemin)
        $(CC) -shared $^ -o $(mik).dll $(LIBS) -Wl,--out-implib,lib$(mik).a


$(path_obj)/%.o: %.c
        $(CC) $(CXXFLAGS) -c $< -o $@

clean:
        @find . -iname $(mik).dll -exec rm {} \;
        @find $(path_obj) -iname "*.o" -exec rm {} \;
        @echo -e "Killing is my business... and business is good!"



Premier exemple: hello_MikMod.c

Tiré directement de la doc, pas d'adaptation nécessaire, mais ne fais pas grand chose.

L'intêret est de la compiler et de l'exécuter. Si il s'exécute sans erreur, vous avez une dll fonctionnelle, félicitations!

Pour le compiler

gcc hello_MikMod.c -I../include/ -L./ -lmikmod

/* MikMod Sound Library example program: a skeleton */
#include <mikmod.h>

main()
{
        /* register all the drivers */
        MikMod_RegisterAllDrivers();

        /* initialize the library */
        MikMod_Init("");

        /* we could play some sound here... */

        /* give up */
        MikMod_Exit();
}


Second exemple: playmod_MikMod.c

Passons aux choses sérieuses avec ce second exemple, toujours tiré de la doc, mais adapté à nos besoins.

Plusieurs choses sont à remarquer.

  • Faites du sleep
    • usleep n'est pas supporté sous Windows. Sous Unix; cela permets une mise en sommeil à la microseconde.
    • Windows possède Sleep qui fonctionne à la milliseconde (ça reste honnête, mais la temporisation n'est pas garantie).
    • Il suffit de faire une macro pour transformer notre Sleep en usleep
#if defined(WIN32)
#define usleep(micro) Sleep(micro/1000)
#endif     


  • DirectSound et coupure du son
    • Quand on utilise le driver DirectSound et que l'on quitte la fenêtre auquel le programme est attaché, le son se coupe également.
    • Ceci est normal et dû à DirectSound qui utilise un driver basse latence qui ne joue que dans la fenêtre à laquelle il a été attaché.
    • Pas de solution simple. Pour un jeu, on n'est pas censé changer de fenêtre, et pour une application dite multimedia fonctionnant en arrière plan, il vaut mieux utiliser le driver WavOut.


  • Obtenir des infos sur les drivers
    • Une fonction bien pratique permets de connaitres les drivers supportés.
    • MikMod_InfoDriver() (ne pas oublier de libérer le buffer retourné)


  • Changer le driver par défaut
    • Les exemples utilisent MikMod_RegisterAllDrivers() qui charge tous les drivers disponibles, et utilise le premier disponible.
    • Pour Windows, le premier disponible est toujours le driver DirectSound, quand son support est compilé. On vient de voir que nous pouvions nous trouver dans des cas où le driver WavOut (Windows par défaut) est préférable.
    • Dans ce cas, il faut uniquement charger le(s) driver(s) désiré(s) avec MikMod_RegisterDriver
    • La doc de MikMod n'explique pas où trouver les paramètres à passer à cette fonction. Pour les trouver, il faut fouiller dans les sources, et plus particulièrement: mdreg.c
    • En regardant la suite d'appel de __mm_registeralldrivers(void) , on a la liste des structures définies pour chaque driver
//quelques exemples:
MikMod_RegisterDriver(&drv_ds); //pour passer l'adresse de la structure drv_ds, DirectSound
MikMod_RegisterDriver(&drv_win); //même principe que ci-dessus, correspond au driver WavOut


  • Le listing du second exemple
//jouer le module dont on passe le nom en paramètre.
//utilisation du driver Windows pour le rendu sonore.

#if defined(WIN32)
    #define usleep(micro) Sleep(micro/1000)
#endif     

#include <mikmod.h>
#include <windows.h>

int main(int argc, char **argv)
{
  int retour=0;
  char *buffer=0;
  MODULE *module;

    if (argc<2) {
      printf("Gnaaa faut un module en paramètre!
");
      retour=1;
    }

    if (!retour) {
    /* on n'enregistre pas tous les drivers, sinon DirectSound prends le pas sur WaveOut */
   //  MikMod_RegisterAllDrivers();
    MikMod_RegisterDriver(&drv_win);  //déja, WaveOut
    MikMod_RegisterDriver(&drv_ds);   //ensuite, DirectSound

    /* register all the module loaders */
    MikMod_RegisterAllLoaders();

    /* initialize the library */
    md_mode |= DMODE_SOFT_MUSIC;
    if (MikMod_Init("")) {
        fprintf(stderr, "Pas pu initialiser, erreur: %s
",
                MikMod_strerror(MikMod_errno));
        return;
    }

    /* quelques infos sur le driver */
    buffer=MikMod_InfoDriver();
    fprintf(stderr, "Drivers dispos:
%s

Driver utilisé:
--> %s
", buffer, md_driver->Name);
    if (buffer) free(buffer);
    fflush(NULL);

    /* load module */
    module = Player_Load(argv[1], 64, 0);
    if (module) {
        /* start module */
        Player_Start(module);

        while (Player_Active()) {
            /* we're playing */
            usleep(10000); //mise à jour toutes les 10ms
            MikMod_Update();
        }

        Player_Stop();
        Player_Free(module);
    } else
        fprintf(stderr, "Pas pu charger le module, erreur: %s
",
                MikMod_strerror(MikMod_errno));

    /* give up */
     MikMod_Exit();
    }
 return retour;
}


Musique!

Allez, laissez tomber les mp3 dans vos jeux, et utilisez un format dynamique et pas du tout dépassé (gageons que cela va revenir dans les téléphones portables, tout en étant présenté comme une révolution )

Ceux qui ont lu jusque là peuvent charger directement une archive contenant une dll Windows compilée avec les drivers disponibles pour cette plateforme, ainsi que le makefile et les exemples présentés précédemment.

Happy coding! ;-)