DirectX (17.)

V minulém dílu jsme zakončili grafickou část našeho projektu menu. V dnešním díle si povíme něco o DirectMusic, což je další komponenta knihovny DirectX. Předem upozorňuji, že to nebude nikterak podrobný popis knihovny.

17.1. Shrnutí z minulého dílu

Dříve než se pustíme do nové látky, pokusím se shrnout to, co jsme se do této doby naučili:

17.2. Knihovna Audio.dll

V dnešní lekci si probereme úplné základy DirectMusic, přidáme do menu hudbu a nějaké ty zvuky. Chceme-li vidět postavení nové knihovny v projektu, podívejme se ještě jednou na schéma celého projektu:

Žluté bloky již máme za sebou a bílý blok Audio.dll vytvoříme v této lekci. Samozřejmě Game.exe a Engine.dll doznají taky několik změn (jinak by to celé bylo k ničemu). Vytvoříme novou knihovnu Audio.dll, která bude součást projektu z minulých lekcí. Přidat nový pod-projekt již umíme. Po té musíme nastavit "závislosti" (Dependencies) tak, aby knihovna Audio.dll mohla použít funkce z Common.dll a aby moduly Engine.dll a Game.exe mohly používat funkce z Audio.dll. Tudíž dialog závislostí by mohl vypadat zhruba  takto:

Po té nastavíme na kartě Settings... projektu Audio tato nastavení:

Nastavení nezapomeňte pozměnit i pro verzi Release. V projektu bude jediná třída CMusic v souborech Music.h a Music.cpp. Exportované funkce budou v deklarovány a definovány v souborech Common.h a Common.cpp, které musíte vložit ručně. Co konkrétně bude v jednotlivých souborech si povíme později.

17.3. Trocha teorie DirectMusic

Pomocí této komponenty se dá přehrávat jak hudba tak normální zvukové efekty. S DirectMusic můžete dělat různá kouzla, ale my se zde budeme zabývat základy:

DirectMusic ještě podporuje formát .sgt (Segment), což je formát určený přímo pro DirectMusic.

Nyní si v několika krocích povíme, co je potřeba, aby jsme mohli přehrát zvuk:

  1.  Objekty v DirectMusic využívají technologii COM, takže první co musíme udělat je, že zavoláme funkci CoInitializeEx(), abychom mohli vytvořit objekty Performance a Loader.

  2. Za druhé musíme vytvořit objekt tzv. Performance. Tento objekt řídí tok dat ze zdroje (zvukový soubor) do syntezéru (procesor zvukové karty). Navíc se stará o časování atd. Tento objekt má většina aplikací jediný.

  3. Dále vytvoříme objekt Loader. Tento objekt se narozdíl od Performance stará a nahrávání dat ze souborů .mid, .wav nebo .sgt. Audio data navíc mohou být uložena ve zdrojích projektu.

  4. V dalším kroku již můžeme nahrávat tzv. segmenty, což není nic jiného než objekty vytvořené z audio souborů. Tyto segmenty pak musíme "stáhnout" do syntezéru a teprve potom je můžeme přehrát.

17.4. Třída CMusic

Vše co jsme si řekli v předešlé části bude implementováno ve třídě CMusic. Tuto třídu již můžete vložit do našeho nového projektu Audio.

Návrh třídy by mohl vypadat takto:
Atributy
Typ Název Popis
BOOL m_bInit Inicializace objektu - TRUE nebo FALSE
int m_MusicVolume Hlasitost (kanál master)
CPtrArray m_arSegments Pole segmentů - jak bylo zmíněno výše, segment představuje audio data nahraná ze souboru .wav nebo .mid
IDirectMusicLoader8* m_lpDMLoader Objekt Loader
IDirectMusicPerformance8* m_lpDMPerformance Objekt Performance
Metody
Návratová hodnota Název Popis
void CreateDirectMusicSystem(HWND hWnd); Vytváří systém DirectMusic. Podobné metody známe již z projektu Display nebo Input.
void LoadMusicFromFile(DWORD dwID, CString BMPFile); Nahrává audio soubor, buď .wav nebo .mid. Prvním parametrem je určeno ID a toto ID také použijeme, když chceme zvuk přehrát.
void PlayMusic(DWORD dwID); Spustí hudbu. V jeden okamžik může bát přehrávána pouze jedna hudební stopa.
void PlaySound(DWORD dwID); Přehraje zvuk z .wav. Zvuky se dají trochu míchat.
void StopMusic(DWORD dwID); Zastaví přehrávání hudební stopy.
void IncreaseMusicVolume(); Zvýší hlasitost.
void DecreaseMusicVolume(); Sníží hlasitost.
int GetMusicVolume() Vrátí aktuální hlasitost.
void DeinitMusic(); Zastaví všechny stopy a zruší všechny DirectMusic objekty.

Vidíme, že třída je poměrně jednoduchá. Ani implementace nebude nikterak složitá. Dříve než se dostanu k deklaraci třídy, zmíním se ještě o jedné struktuře, která je použita v našem projektu. Každý nahraný soubor bude reprezentován objektem IDirectMusicSegment8*. My ale každém zvuku musíme přiřadit ID, proto je třeba vytvořit strukturu SAudio, která obsahuje zmíněné datové prvky. Ve třídě CMusic je pole ukazatelů právě těchto objektů.

Soubor Music.h vypadá takto:

struct SAudio {

    DWORD m_dwID;
    IDirectMusicSegment8* m_lpSegment;
};

class CMusic
{
private:
    IDirectMusicLoader8* m_lpDMLoader;
    IDirectMusicPerformance8* m_lpDMPerformance;

    CPtrArray m_arSegments;

    int m_MusicVolume;
    BOOL m_bInit;

public:
   
//
    // Initialization
   
void CreateDirectMusicSystem(HWND hWnd);
    void DeinitMusic();
    //
    // Get music from MIDI or WAV
    void LoadMusicFromFile(DWORD dwID, CString BMPFile);
  
 //
    // Music/sounds play & stop functions

    void PlayMusic(DWORD dwID);
    void PlaySound(DWORD dwID);
    void StopMusic(DWORD dwID);
  
 //
    // Master volume

    void IncreaseMusicVolume();
    void DecreaseMusicVolume();
    int GetMusicVolume() {return m_MusicVolume;}

public:
    CMusic();
    ~CMusic();
};

Metody vesměs vrací void, protože chybové hlášení ošetříme pomocí výjimek (viz. Kurz C++). Nyní budeme postupně implementovat metody třídy CMusic.

Začneme logicky od inicializace:

void CMusic::CreateDirectMusicSystem(HWND hWnd)
{
    DXTRACE("Init DirectMusic system...");
    DWORD dwRet;
   
//
    // Check initialization of system

    if(m_bInit) {
        DXTHROW("System DirectMusic is already initialized.");
    }
   
//
    // 1. COM initialization

    CoInitialize(NULL);
   
//
    // 2. Create DMLoader

    dwRet = CoCreateInstance(CLSID_DirectMusicLoader, NULL,
                            CLSCTX_INPROC, IID_IDirectMusicLoader8,
                            (void**)&m_lpDMLoader);
    if(dwRet != S_OK) {
        DXTHROWERR("Cannot create DMLoader due ", dwRet);
    }
   
//
    // 3. Create DMPerformance

    dwRet = CoCreateInstance(CLSID_DirectMusicPerformance, NULL,
                            CLSCTX_INPROC, IID_IDirectMusicPerformance8,
                            (void**)&m_lpDMPerformance);
    if(dwRet != S_OK) {
        DXTHROWERR("Cannot create DMPerformance due ", dwRet);
    }
  
 //
    // Init DMPerformance

    dwRet = m_lpDMPerformance->InitAudio(
                        NULL,
// IDirectMusic interface not needed.
                        NULL,
// IDirectSound interface not needed.
                        hWnd,
// Window handle.
                        DMUS_APATH_DYNAMIC_STEREO ,
// Default audiopath type.
                        64,
// Number of performance channels.
                        DMUS_AUDIOF_ALL,
// Features on synthesizer.
                        NULL
// Audio parameters; use defaults.
        );
    if(dwRet != S_OK) {
        DXTHROWERR("Cannot init DMPerformance due ", dwRet);
    }
 
  //
    // Set search directory - default is Music

    CString csPath = setGetDataFilePath("Music");
    dwRet = m_lpDMLoader->SetSearchDirectory(GUID_DirectMusicAllTypes, csPath.AllocSysString(), TRUE);
    if(dwRet != S_OK) {
        DXTHROWERR("Cannot set search directory due ", dwRet);
    }
  
 //
    // Initialization OK

    m_bInit = TRUE;
}

Nejprve je třeba otestovat, zda-li metodu nevoláme podruhé. Dále jsou kroky očíslovány jako ve výše popsaném postupu. Takže za prvé inicializujeme COM funkcí CoInitialize(). Dále získáme ukazatel na rozhraní objektu Loaderu. To provedeme pomocí funkce CoCreateInstance(), která zároveň vytvoří i instanci objektu. První parametr této funkce je ID třídy objektu, dále mimo jiné zadáváme ID rozhraní, které požadujeme a ukazatel na toto rozhraní, který bude inicializován. Prakticky totéž provedeme pro objekt Performance. Nyní musíme tento objekt vnitřně zinicializovat. K tomu slouží metoda InitAudio(), která má následující parametry:

Nakonec nastavíme pomocí metody SetSearchDirectory() cestu k audio souborům. Až program bude nahrávat soubory, bude je hledat v adresáři Music.

Inicializaci bychom měli. Nyní se podíváme, jak systém uvolníme. Uvolnění zajišťuje metoda DeinitMusic():

void CMusic::DeinitMusic()
{
    DWORD dwRet;
   
//
    // Check initialization of system

    if(!m_bInit) {
        DXTHROW("System DirectMusic is not initialized.");
    }
  
 //
    // Stop all music and sounds

    if(m_lpDMPerformance) {
        dwRet = m_lpDMPerformance->Stop(
                                        NULL,
// Stop all segments.
                                        NULL,
// Stop all segment states.
                                        0,
// Do it immediately.
                                        0
// Flags.
                                        );
        if(dwRet != S_OK) {
            DXTHROWERR("cannot stop all tracks due", dwRet);
        }
       
// Close performance
        m_lpDMPerformance->CloseDown();
    }
    SAudio *pAudio;
  
 //
    // Delete all segments

    for(int i = 0; i < m_arSegments.GetSize(); i++) {
        pAudio = (SAudio*) m_arSegments[i];
        SAFE_RELEASE(pAudio->m_lpSegment);
        SAFE_DELETE(pAudio);
    }
   
//
    // Safe release loader and performance

    SAFE_RELEASE(m_lpDMLoader);
    SAFE_RELEASE(m_lpDMPerformance);
}

Nejprve zkontrolujeme, zda-li byl systém vůbec někdy zinicializován. Všimněte si, že tuto kontrolu provádíme u každé metody CMusic. Pokud nejprve nezavoláte metodu CreateDirectMusicSystem(), všechny ostatní metody budou vyhazovat výjimku.
Dále musíme zastavit veškeré audio, které se aktuálně přehrává. K tomu použijeme objekt Performance a metodu Stop(). Pomocí čtyř parametrů zajistíme, že se zastaví všechny segmenty, v libovolných stavech a zastaví se okamžitě! Činnost objektu Performance ukončíme metodou CloseDown(), která uvolní vnitřní reference atd. Jak uvidíme později, každý segment je alokován dynamicky. Proto musí být uvolněn a posléze vymazán z paměti. Tuto činnost provede následující cyklus, kdy uvolníme a dealokujeme všechny segmenty. Nakonec uvolníme rozhraní objektů Loader a Perfomance.

Bude potřeba nahrát zvuky a hudbu ze souborů .wav nebo .mid. K tomu slouží metoda LoadMusicFromFile():

void CMusic::LoadMusicFromFile(DWORD dwID, CString BMPFile)
{
    DWORD dwRet;
    SAudio *pAudio;
  
 //
    // Check initialization of system

    if(!m_bInit) {
        DXTHROW("System DirectMusic is not initialized.");
    }
 
  //
    // Check if the specified ID is not already in array

    for(int i = 0; i < m_arSegments.GetSize(); i++) {
        pAudio = (SAudio*) m_arSegments[i];
        if(pAudio->m_dwID == dwID) {
            DXTHROW("Segment %d - %s is already in the array.", dwID, BMPFile);
        }
    }
   
//
    // Create new audio segment

    SAudio *pNew = new SAudio;
    pNew->m_dwID = dwID;
   
//
    // Try to load audio file
    dwRet = m_lpDMLoader->LoadObjectFromFile(
                            CLSID_DirectMusicSegment,
// Class identifier.
                            IID_IDirectMusicSegment8,
// ID of desired interface.
                            BMPFile.AllocSysString(),
// Filename.
                            (LPVOID*) &pNew->m_lpSegment
// Pointer that receives interface.
                            );
    if(dwRet != S_OK) {
        SAFE_DELETE(pNew);
        DXTHROWERR("Cannot load audio from file due", dwRet);
    }
  
 //
    // Download audio to synthesizer

    dwRet = pNew->m_lpSegment->Download( m_lpDMPerformance );
    if(dwRet != S_OK) {
        SAFE_DELETE(pNew);
        DXTHROWERR("Cannot download audio due", dwRet);
    }
   
//
    // Add audio segment pointer

    m_arSegments.Add(pNew);
}

Metoda má dva parametry: první je ID zvuku, podle kterého určíme, že chceme přehrát právě tento zvuk a druhý parametr je řetězec daného souboru. V této metodě nejdříve musíme zkontrolovat, zda-li uživatel nevložil zvuk se stejným ID dvakrát. Pokud je ID unikátní, pokračujeme dále a vytvoříme vlastní objekt zvuku. Přiřadíme ID a zinicializujeme rozhraní segmentu. To provedeme pomocí metody LoadObjectFromFile() (zde si všimněte, že používáme objekt Loader). Metoda vyžaduje takzvaný wide string a ten vrací metoda AllocSysString() třídy CString. Nakonec musíme data stáhnout do syntezéru (jinak by audio nešlo přehrát) a uložíme ukazatel do pole segmentů.

Nyní máme nahraná data a potřebujeme spustit přehrávání ať už hudby nebo jen zvuku. K tomu nám slouží metody PlayMusic() a PlaySound(). Obě metody jsou až na jeden řádek identické, proto tu uvedu jen jednu z nich a na zmíněný řádek upozorním:

void CMusic::PlayMusic(DWORD dwID)
{
    DWORD dwRet;
    SAudio *pAudio;
  
 //
    // Check initialization of system

    if(!m_bInit) {
        DXTHROW("System DirectMusic is not initialized.");
    }
   
//
    // Find source and start playing

    for(int i = 0; i < m_arSegments.GetSize(); i++) {
        pAudio = (SAudio*) m_arSegments[i];
        if(pAudio->m_dwID == dwID) {
           
//
            // Play segment

            dwRet = m_lpDMPerformance->PlaySegmentEx(
                                                    pAudio->m_lpSegment,
// Segment to play.
                                                    NULL,
// Used for songs; not implemented.
                                                    NULL,
// For transitions.
                                                    0 ,
// Flags.
                                                    0,
// Start time; 0 is immediate.
                                                    NULL,
// Pointer that receives segment state.
                                                    NULL,
// Object to stop.
                                                    NULL
// Audiopath, if not default.
            );
            if(dwRet != S_OK) {
                DXTHROWERR("Cannot play music due", dwRet);
            }
        }
    }
}

Princip je velice snadný. Podle ID najdeme požadovaný segment v poli (pokud tam vůbec je). Pokud segment nalezneme, zavoláme metodu objektu Performance PlaySegmentEx(). Tato metoda má poněkud více parametrů a my si zde uvedeme jen ty pro nás podstatné:

Nyní si vysvětlíme rozdíl metod PlayMusic() a PlaySound():

dwRet = m_lpDMPerformance->PlaySegmentEx(
                                         pAudio->m_lpSegment,
// Segment to play.
                                        NULL,
// Used for songs; not implemented.       
                                        NULL,
// For transitions.
                                      
 DMUS_SEGF_SECONDARY , // Flags.       
                                        0,
// Start time; 0 is immediate.
                                        NULL,
// Pointer that receives segment state.
                                        NULL,
// Object to stop.
                                        NULL
// Audiopath, if not default.
);

Toto je výsek metody PlaySound() a vyznačený řádek je právě ten řádek, ve kterém se obě metody liší. Hudba se musí přehrávat tzv. primárním bufferu. V tomto bufferu může běžet najednou pouze jedna stopa. My ale potřebujeme pouštět více stop najednou, takže zvuky budou přehrávány v tzv. sekundárním bufferu. To nastavíme příznakem DMUS_SEGF_SECONDARY.

Pokud chceme hudbu zastavit, voláme metodu StopMusic():

void CMusic::StopMusic(DWORD dwID)
{
  
 //
    // Check initialization of system

    if(!m_bInit) {
        DXTHROW("System DirectMusic is not initialized.");
    }
    SAudio *pAudio;
    for(int i = 0; i < m_arSegments.GetSize(); i++) {
        pAudio = (SAudio*) m_arSegments[i];
        if(pAudio->m_dwID == dwID) {
            m_lpDMPerformance->Stop(pAudio->m_lpSegment, NULL , 0, 0);
        }
    }
}

Ta vlastně využívá stejnou metodu jako metoda DeinitMusic(), ale použije konkrétní segment, který se má zastavit. Takže najdeme požadovaný segment a zastavíme ho.

Nakonec nám zbývají dvě metody pro ovládání hlasitosti IncreaseMusicVolume() a DecreaseMusicVolume(). Obě metody jsou skoro totožné, takže zde opět uvedu jen jednu z nich a na jeden malý rozdíl upozorním:

void CMusic::IncreaseMusicVolume()
{
 
   //
    // Check initialization of system

    if(!m_bInit) {
        DXTHROW("System DirectMusic is not initialized.");
    }
    int Volume = m_MusicVolume;
   
//
    // Increment volume

    Volume += 100;
   
// Set 0 if 0
    if(Volume >= 0)
        Volume = 0;
   
// Set volme to performance
    m_lpDMPerformance->SetGlobalParam( GUID_PerfMasterVolume,
                                        (void*)&Volume, sizeof(long) );
   
// Remember volume
    m_MusicVolume = Volume;
}

V metodě pracujeme s pomocnou proměnnou, do které nejprve uložíme aktuální hlasitost. Tu potom zvýšíme a zkontrolujeme, zda-li jsme nepřekročili maximální či minimální hodnotu. Poté pomocí metody SetGlobalParam() předáme hlasitost objektu Performance. Nakonec zpětně uložíme hlasitost do proměnné m_MusicVolume. Pomocí této metody můžeme také například měnit tempo přehrávání pokud použijeme hodnotu GUID_PerfMasterTempo. Člověk by to nečekal, ale metody Increase a DecreaseMusicVolume() se liší v tomto řádku:

Volume += 100; a Volume -= 100;

Tímto jsme zakončili implementaci třídy CMusic. Ještě zbývá vytvořit globální objekt CMusic a exportovat některé metody.

17.5. Export metod a úprava projektů Game a Engine

Exportované funkce budou v souborech Common.h a Common.cpp. Navíc zde také budeme vkládat hlavičkový soubor pro práci s DirectMusic. V praxi pak bude stačit vložit hlavičkový soubor Common.h a systém bude fungovat.

Výpis souboru Common.h:

#ifndef AUDIO_COMMON_H
    #define AUDIO_COMMON_H
   
//
    // Include own common

    #include "..\Common\Common.h"
  
 //
    // Include DMusic

    #define INITGUID
    #define DWORD_PTR DWORD
    #include <dmusici.h>
  
 //
    // Export/import macros

    #ifndef AUDIO_API
        #define AUDIO_API __declspec( dllimport )
    #endif
// AUDIO_API

    #include "Music.h"
   
//
    // Exported functions

    AUDIO_API void audInitMusic(HWND hWnd);
    AUDIO_API void audDeinitMusic();
    AUDIO_API void audLoadMusicFromFile(DWORD dwID, CString BMPFile);
    AUDIO_API void audIncreaseMusicVolume();
    AUDIO_API void audDecreaseMusicVolume();
    AUDIO_API int audGetMusicVolume();
    AUDIO_API void audPlayMusic(DWORD dwID);
    AUDIO_API void audStopMusic(DWORD dwID);
    AUDIO_API void audPlaySound(DWORD dwID);

#endif
// AUDIO_COMMON_H

Zde jsou deklarovány exportované funkce. Na podobný zápis jsme už zvyklí z minulých lekcí.

Výpis souboru Common.cpp:

#include "stdafx.h"

#define AUDIO_API __declspec(dllexport)

#include "Common.h"

CMusic theMusic;

AUDIO_API void audInitMusic(HWND hWnd)
{
    theMusic.CreateDirectMusicSystem(hWnd);
}

AUDIO_API void audLoadMusicFromFile(DWORD dwID, CString BMPFile)
{
    theMusic.LoadMusicFromFile(dwID, BMPFile);
}

AUDIO_API void audPlayMusic(DWORD dwID)
{
    theMusic.PlayMusic(dwID);
}

AUDIO_API void audPlaySound(DWORD dwID)
{
    theMusic.PlaySound(dwID);
}

AUDIO_API void audStopMusic(DWORD dwID)
{
    theMusic.StopMusic(dwID);
}

AUDIO_API void audDeinitMusic()
{
    theMusic.DeinitMusic();
}

AUDIO_API void audIncreaseMusicVolume()
{
    theMusic.IncreaseMusicVolume();
}

AUDIO_API void audDecreaseMusicVolume()
{
    theMusic.DecreaseMusicVolume();
}

AUDIO_API int audGetMusicVolume()
{
    return theMusic.GetMusicVolume();
}

Ani zde není nic nového pod Sluncem. Exportujeme vlastně všechny metody.

Na úplný závěr této lekce ještě upravíme projekty Engine a Game, abychom vyzkoušeli novou knihovnu. Do projektu Engine vložíme pouhý jeden řádek, který přehraje určitý zvuk při stisku tlačítka:

if(GetState()->GetID() != BS_DISABLE) {

    switch(_Action) {
    case IA_MOUSEMOVE:
        if(GetState()->GetID() != BS_PRESS) {
            SetState(BS_FOCUS);
        }
        break;
    case IA_MOUSECLICK_UP:
        if((*((UINT*)_Data)) == LEFT_MOUSE_BUTTON) {
            SetState(BS_FOCUS);
        }
       
audPlaySound(1);
        break;
    case IA_MOUSECLICK_DOWN:
        if((*((UINT*)_Data)) == LEFT_MOUSE_BUTTON) {
            SetState(BS_PRESS);
        }
    break;   
    case IA_KEYPRESS:
        //none handling
        break;
    case IA_NONE:
        SetState(BS_NORMAL);
        break;
    }
}

V celé aplikaci máme dva zvuky, takže nepoužívám symbolické konstanty, jak by se mělo, pokud bychom nahrávali více zvuků. V projektu Game těch uprav bude trochu víc. Za prvé musíme zinicializovat celý systém, po té musíme nahrát hudbu a zvuk kliknutí, dále spustit hudbu a nakonec upravit funkci UpdateFrame() tak, aby šla ovládat hlasitost.

Takto upravte kód ve funkci WinMain():

try {
    audInitMusic(g_hWnd);
    audLoadMusicFromFile(0, "passport.mid");
    audLoadMusicFromFile(1, "click.wav");
    audPlayMusic(0);

    disInit(g_hWnd, DDFLG_CLIPPER|DDFLG_FULLSCREEN);
    inpCreateDirectInputSystem(hInstance, g_hWnd, disGetResolution());
    disDefineBackground(_S_BACKGROUND, 0);
}
catch(LPCSTR str) {
    DXTRACE(str);
}

A takto upravte funkci UpdateFrame():

void UpdateFrame()
{
    disUpdateBackground();

    inpProcessInput();

    // Pri stisknuti klavesy Esc ukoncime aplikaci
    if(inpIsKeyDown(DIK_ESCAPE, FALSE)) {
        PostMessage(g_hWnd, WM_DESTROY, 0, 0);
    }
   
if(inpIsKeyDown(DIK_ADD, TRUE)) {
        audIncreaseMusicVolume();
    }
    if(inpIsKeyDown(DIK_SUBTRACT, TRUE)) {
        audDecreaseMusicVolume();
    }

    menTestMouseMove(inpGetCursor());
    if(inpIsLButtonDown()) {
        menTestMouseClick(inpGetCursor(), LEFT_MOUSE_BUTTON, BA_DOWN);
    }
    if(inpIsLButtonUp()) {
        menTestMouseClick(inpGetCursor(), LEFT_MOUSE_BUTTON, BA_UP);
    }

    menUpdateMenu();
    inpUpdateCursor();

    disPresent();
}

Nakonec ještě přidejte volání funkce audDeinitMusic() do procedury okna:

case WM_DESTROY:
    // Cleanup and close the app
   
audDeinitMusic();
    menReleaseMenu();
    PostQuitMessage( 0 );
    return 0L;
}

Tak a to je vše. Pokud jste vše udělali správně, po kompilaci byste měli slyšet hudbu a při stisku tlačítka zvuk kliknutí.

17.6. Závěr

To je vše, co Vám k této komponentě mohu poskytnout. Podrobnější informace najdete v knihovně MSDN, kde jsou detailně popsány všechny možnosti DirectMusic a DirectSound.

V této lekci bych také chtěl definitivně zakončit projekt Game.

Těším se příště nashledanou.

Jiří Formánek