Jouer des Midis avec DirectMusic
Un article de Games Creators Network.
Attention
Cet article est en attente du choix d'une licence par son auteur. Elle n'est pas soumise à la licence par défaut du GCN.
Cela signifie entre autre chose que si vous n'êtes pas l'auteur de cet article, vous n'avez probablement pas le droit de le modifier.
Maintenant que nous sommes capables d'écrire un jeu jouable (l'affichage et les saisies étant opérationnels), il serait très agréable d'ajouter une musique d'ambiance. Pour cela, un moyen simple est de jouer des fichiers Midi. En effet, DirectMusic offre une interface de programmation très simplifier pour exécuter cette opération. Ce tutoriel, inspiré du tutoriel présent dans l'aide de DirectX7 (les bugs en moins), est destiné à vous montrer 'Comment ça Marche'.
Sommaire |
[modifier] Initialisation de DirectMusic
DirectMusic est basé sur COM. Il faut donc initialiser COM avec la fonction suivante :
CoInitialize(NULL);Pour jouer nos midis, nous avons besoin de trois interfaces de DirectMusic :
- La Performance (IDirectMusicPerformance) qui sert de manager à la lecture des fichiers musicaux ;
- Le Loader (IDirectMusicLoader) qui sert à trouver, énumérer ou encore charger des fichiers musicaux ;
- Le Segment (IDirectMusicSegment) qui représente le morceau à jouer.
La phase d'initialisation de DirectMusic doit initialiser la Performance et ensuite le Loader.
[modifier] Initialisation de la Performance
Voici ma fonction d'initialisation de la Performance :
IDirectMusicPerformance* pPerf; // Objet Performance
bool CreatePerformance()
{
if(FAILED(CoCreateInstance(CLSID_DirectMusicPerformance,NULL,CLSCTX_INPROC,
IID_IDirectMusicPerformance2, (void**)& pPerf)))
{
pPerf=NULL;
return false;
}
if(FAILED(pPerf-> Init(NULL,NULL,NULL)))
{
return false;
}
if(FAILED(pPerf-> AddPort(NULL)))
{
return false;
}
return true;
}
CoCreateInstance : Crée un Objet associé à un CLSID spécifique. Ici il s'agit d'un objet de type DirectMusicPerformance. Le premier paramètre est le CLSID de l'objet à créer. Le deuxième est NULL si l'objet est indépendant, sinon si l'objet à créer fait partie d'un agrégat, il faut passer le pointer de l'objet dont il découle. Le troisième paramètre est un contexte expliquant la manière dont l'objet crée est utilisé par le programme (voir la liste des paramètres CLSCTX de MSDN). Le quatrième paramètre est l'identifieur de l'interface utilisée (ici IDirectMusicPerformance). Le dernier paramètre est l'adresse du pointer vers l'objet à créer.
Init : Fonction membre de la Performance. Elle est là pour associer la performance à un objet IDirectMusicet à un objet IDirectSound (Ici notre Performance ne sera rattachée à rien). Le premier paramètre est l'adresse d'un pointeur vers une interface IDirectMusic. Le second paramètre est l'adresse d'une interface IDirectSound. Le dernier paramètre est un handle de fenêtre qui a servi à l'objet IDirectSound.
AddPort : Ajoute un Port Midi à la Performance. Le paramètre est un pointeur vers une interface IDirectMusicPort. Ici, on passe NULL pour avoir le Port par défaut.
[modifier] Initialisation du Loader
Pour créer le Loader, rien de plus simple. Voici ma fonction :
IDirectMusicLoader* pLoader;
bool CreateLoader()
{
if(FAILED(CoCreateInstance(CLSID_DirectMusicLoader,NULL,CLSCTX_INPROC, IID_IDirectMusicLoader,(void**)& pLoader)))
{
pLoader=NULL;
return false;
}
return true;
}
CoCreateInstance : Voir dans la partie précédente.
[modifier] Chargement du Segment
Le segment est l'objet qui va contenir notre musique Midi. Pour le charger j'utilise une macro et 2 fonctions.
La macro se présente comme suit :
#define MULTI_TO_WIDE(x,y) MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,y,-1,x,_MAX_PATH);
MultiByteToWideChar : Cette fonction transforme une chaîne de caractère de type char* en chaîne de type WCHAR (UniCode). Ses paramètres sont dans l'ordre: La page de code (ici code ANSI), un indicateur pour la transformation (ici, la transformation par défaut), la chaîne source, le nombre de caractères à convertir (-1 = la totalité de la chaîne), la chaîne cible et enfin la taille maximale en caractères de la cible (ici la taille maximum de chemin de fichier).
Pour mes deux fonctions, je ne présenterais pas chaque commande utilisée mais je me contenterais des commentaires pour ne pas alourdir le tutoriel avec des choses qui n'ont pas une importance fondamentale. La première fonction nommée ChargeMidi fait appel à la seconde, nommée LoadMIDISegment.
IDirectMusicSegment* pSegment; // Segment DirectMusic.
bool ChargeMidi(char* MidiFileName)
{
// Chemin du fichier transformé en WCHAR.
WCHAR wszMidiFileName[_MAX_PATH];
// Si le segment n'est pas vide, on supprime son contenu.
if(pSegment)
{
pSegment-> Release();
pSegment=NULL;
}
// On convertie le chemin du fichier de char* en WCHAR.
MULTI_TO_WIDE(wszMidiFileName,MidiFileName);
// Si le loader existe, on charge le midi.
if(pLoader)
{
// On tente de charger le Segment.
if(!LoadMIDISegment(wszMidiFileName))
return false;
}
else
{
// Sinon on quitte.
return false;
}
// Si le Segment n'est pas chargé, on renvoie une erreur.
if(pSegment==NULL)
return false;
// Réussite.
return true;
}
bool LoadMIDISegment(WCHAR wszMidiFileName[_MAX_PATH])
{
// Description d'un objet DirectMusic.
DMUS_OBJECTDESC ObjDesc;
char szDir[_MAX_PATH];
WCHAR wszDir[_MAX_PATH];
// Récupère le répertoire de travail.
if(_getcwd(szDir,_MAX_PATH)==NULL)
return false;
// Transforme le chemin de travail de char* en WCHAR.
MULTI_TO_WIDE(wszDir,szDir);
// Indique au Loader le répertoire dans lequel il doit travailler.
HRESULT hr=pLoader-> SetSearchDirectory(GUID_DirectMusicAllTypes, wszDir,FALSE);
if(FAILED(hr))
return false;
// Initialise la description du Segment.
ZeroMemory(& ObjDesc,sizeof(ObjDesc));
ObjDesc.dwSize=sizeof(DMUS_OBJECTDESC);
ObjDesc.guidClass=CLSID_DirectMusicSegment;
// wsccpy équivaut à strcpy mais s'utilise avec les WCHAR.
wcscpy(ObjDesc.wszFileName,wszMidiFileName);
ObjDesc.dwValidData=DMUS_OBJ_CLASS|DMUS_OBJ_FILENAME;
pSegment=NULL;
// Charge le Segment à l'aide du Loader.
if(FAILED(pLoader-> GetObject(& ObjDesc,IID_IDirectMusicSegment2, (void**)& pSegment)))
return false;
if(pSegment==NULL)
return false;
// Paramétrages du Segment.
pSegment-> SetParam(GUID_StandardMIDIFile,-1,0,0,(void*)pPerf);
pSegment-> SetParam(GUID_Download,-1,0,0,(void*)pPerf);
return true;
}
[modifier] Jouer et stopper le Midi
Jouer un Midi une fois qu'il est chargé est on ne peut plus simple. Voici comment je procède :
void JoueMidi(int repete)
{
if(pSegment)
{
pSegment-> SetRepeats(repete);
pPerf-> PlaySegment(pSegment,0,0,NULL);
}
}
SetRepeats : Indique combien de fois le Midi doit boucler sur lui-même. Une fois ce nombre de fois joué (en sus du premier), le morceau s'arrête. Pour ne pas avoir de répétition il faut donc passer 0.
PlaySegment : Joue le Segment spécifié en premier paramètre. Le second paramètre est une liste d'indicateurs (voir liste des DMUS_SEGF_FLAGS dans l'aide de DirectX 7 SDK). Le troisième est le point de départ de la lecture (ici on part du debut du morceau). Le dernier paramètre est l'adresse d'un pointeur vers une interface de statistique de Segment (IDirectMusicSegmentState) qui sert lorsque l'on veut afficher le temps de lecture ou autres.
Arrêter la lecture d'un Midi est tout aussi simple:
pPerf->Stop(NULL,NULL,0,0);Stop : Arrête la lecture Midi lancée sur une performance. Le premier paramètre est l'adresse du segment à stopper (ici on arrete tous les segments lancées sur la performance). Le second est l'adresse d'une interface IDirectMusicSegmentState (inutile ici). Le troisième est le moment ou l'on veut stopper la lecture du midi (ici on arrête tout de suite). Le quatrième paramètre indique e type d'arrêt (ici immédiat), voir la liste des DMUS_SEGF_FLAGS dans l'aide de DirectX 7.
La fonction membre de IDirectMusicPerformance nommée IsPlayingpermet de savoir si un segment est en cours de lecture.
[modifier] Fermeture de DirectMusic
Avant de quitter votre application, il est préférable de 'clôturer' DirectMusic. Voici ma fonction de clôture :
void ShutDown()
{
pPerf-> Stop(NULL,NULL,0,0);
pSegment-> SetParam(GUID_Unload,-1,0,0,(void*)pPerf);
pSegment-> Release();
pPerf-> CloseDown();
pPerf-> Release();
pLoader-> Release();
CoUninitialize();
}
Cette fonction casse toutes les références que nous avons crée pour DirectMusic dans notre application. La dernière commande, CoUninitialise, sert à désactiver COM dans notre programme.
[modifier] Conclusion
Voilà, maintenant la lecture d'un fichier Midi n'a plus de secret pour vous. Je joint à ce tutoriel un exemple de programme jouant 2 fichier Midis. La touche Echap sert à quitter le programme et la touche Entrée sert à changer le Midi en cours de lecture. Cet exemple utilise ma classe de gestion des midis. Le Midis fournis ont été composé par François Hollingue. Vous pouvez retrouver ses créations sur son site : http://www.multimania.com/djeser
Si vous avez des questions, n'hésitez pas à me contacter, je me ferais un plaisir d'essayer de vous répondre.
Ce document a été publié sur la version 3 du G.C.N. par Kain.
- Auteur Original : Kain
- Date de publication : 9 janvier 2002

