┌vod
Instalace Ogg SDK
Torie k DirectSound
╚ßst 1
╚ßst 2
╚ßst 3
╚ßst 4
Funkce knihovny vorbisfile.dll
Odkazy
Ke sta₧enφ

R.Henys@seznam.cz
ICQ: 159629842

V tΘto Φßsti vytvo°φme projekty se kter²mi budeme dßle pracovat a budeme se tΘ₧ v∞novat implementaci zvukovΘho bufferu.

Obsah:

1.1 NovΘ °eÜenφ

Vytvo°φme si dva novΘ projekty. Oba budou typu Win32. Bude se jednat o jednu dynamickou knihovnu (dll) a spustiteln² soubor (exe). V knihovn∞ bude implementovßna samotnß t°φda a spustitelnß aplikace bude slou₧it jako testovacφ program. Jß jsem si pro knihovnu zvolil nßzev OggCodec a pro aplikaci Tester. Otev°ete v²vojovΘ prost°edφ a vytvo°te nov² projekt typu Win32 Project, zadejte nßzev projektu jak² je vßm nejbli₧Üφ a zaÜkrtn∞te volbu Create directory for Solution (zobrazφ se po stisku tlaΦφtka More). Klepn∞te na Ok. Zobrazφ se pr∙vodce. Na kart∞ Application Settings vyberte p°epφnaΦ Dll a vygenerujte zßkladnφ soubory kliknutφm na Finish. Te∩ p°idßme druh² projekt p°es menu File->Add Project->New Project.... Dßle postupujeme stejn∞ jako p°i vytvß°enφ prvnφho projektu, jen vybereme p°epφnaΦ Windows Application.


Obsah

1.2 Nastavenφ projekt∙

Dßle je t°eba provΘst jeÜt∞ n∞jakß nastavenφ ne₧ budeme moci zaΦφt. Klepn∞te na prvnφ projekt prav²m myÜφtkem a z kontextovΘho menu vyberte Properties. Ve stromovΘ struktu°e v levΘ Φßsti okna vyberte Configuration Properties/General a upravte vlastnost Output Directory na ..\Debug u Debug konfigurace projektu, respektive na ..\Release u Release konfigurace projektu. Takto uΦi≥te i pro druh² projekt.



Dßle u projektu OggCodec (dynamickß knihovna) p°ejd∞te do zßlo₧ky Linker a do °ßdku Input vlo₧te, zapiÜte nßsledujφcφ knihovny: vorbisfile_static.lib ogg.lib vorbis.lib dxerr9.lib dsound.lib winmm.lib strmiids.lib dxguid.lib. Zb²vß u₧ jen nastavit zßvislost projekt∙, respektive aplikace Tester na knihovn∞ OggCodec. StaΦφ navÜtφvit dialog Dependencies pomocφ nabφdky Project->Project Dependencies.... V menu vybereme projekt Tester a zaÜkrtneme polφΦko u OggCodec v seznamu. Pokud nynφ °eÜenφ p°elo₧φte (Build->Build Solution), dostane se vßm chybovΘho hlßÜenφ p°i sestavovßnφ ve smyslu ₧e nelze nßlezt soubor OggCodec.lib. To je zp∙sobeno tφm, ₧e jste z projektu jeÜt∞ neexportovali ₧ßdnou funkci, t°φdu ani rozhranφ.


Obsah

1.3 T°φda CSoundBuffer

Jeliko₧ od tohoto odstavce u₧ vlastn∞ zaΦφnßme s cel²m projektem, doporuΦil bych vßm hned na zaΦßtku vytvo°it si v projektu OggCodec nov² hlaviΦkov² soubor Common.h, kter² bude slou₧it definicφm kterΘ budou spoleΦnΘ pro vφce soubor∙. Navφc se tak vyhnete problΘm∙m s vφcenßsobn²m vlo₧enφm hlaviΦkovΘho souboru a chybov²m hlßÜenφm a v neposlednφ °ad∞ je to vφc p°ejhledn∞jÜφ, aspo≥ podle mΘho nßzoru, ne₧ vklßdat takovou definici do ka₧dΘho cpp souboru nebo hlaviΦkovΘho souboru. Do souboru vlo₧te, napiÜte tyto °ßdky:

Soubor Common.h:

    #include <dmusici.h>
    #include <vector>
    #include <dsound.h>
    #include <dxerr9.h>
    #include "vorbisfile.h"
    #include "codec.h"
    #include <math.h>

    #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
    #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
    #define SAFE_DELETE_ARRAY(p) { if(p) {delete [] (p); (p) = NULL; } }

    #define OGGCODEC_API __declspec(dllexport)

Do souboru jsou vlo₧eny hlaviΦkovΘ soubory nezbytn∞ nutnΘ pro b∞h naÜeho "kodeku", jako je dsound.h. HlaviΦkovΘ soubory z Ogg SDK: vorbisfile.h, codec.h. Makra SAFE_RELEASE, SAFE_DELETE a SAFE_DELETE_ARRAY kterß budeme Φasto pou₧φvat. Prvnφ makro slou₧φ k uvoln∞nφ rozhranφ z pam∞ti, druhΘ ke smazßnφ dynamicky vytvo°enΘho objektu a t°etφ ke smazßnφ dynamicky vytvo°enΘho pole. OGGCODEC_API pou₧ijeme u definic funkcφ, t°φd nebo rozhranφ kterΘ budeme chtφt z knihovny exportovat abychom je mohli pou₧φt v dalÜφch programech.

Nynφ u₧ se m∙₧eme pustit do t°φdy CSoundBuffer :)

    Metody t°φdy CSoundBuffer
    Create Vytvo°φ rozhranφ reprezentujφcφ zvukov² buffer.
    RestoreBuffer Obnovφ buffer p°i jeho ztrßt∞.
    Reset Nastavφ pozici hracφho kurzoru v bufferu na 0.
    Play Spustφ p°ehrßvßnφ bufferu
    Stop Zastavφ p°ehrßvßnφ bufferu.
    SetFrequency Nastavφ frekvenci p°ehrßvan²ch zvukov²ch dat.
    SetPan Nastavenφ vyvß₧enosti levΘho a pravΘho kanßlu.
    SetVolume Nastavφ hlasitost bufferu.
    IsSoundPlaying Zjistφ zda buffer data p°ehrßvß nebo ne.

Implementace bufferu je velice podobnß tΘ kterou je mo₧nΘ najφt v p°φkladech SDK k DirectX, konkrΘtn∞ u DirectSound, ale tato t°φda je tak jednoduchß, ₧e se u nφ snad ani mnoho novΘho vymyslet nedß. Jednß se vlastn∞ jen o volßnφ metod rozhranφ IDirectSoundBuffer pomocφ naÜich vlastnφch metod, kterΘ navφc jen oÜet°ujφ mo₧nΘ chybovΘ stavy. Vlo₧te do projektu OggCodec novou t°φdu CSoundBuffer a zapiÜte implementaci t°φdy. Podφvejme se na soubor SoundBuffer.h:

    #pragma once
    //********************************************************************************************
    //Trida zapouzdrujici fce pro praci se zvukovymi buffery
    //********************************************************************************************

    class CSoundBuffer
    {
    protected:
       LPDIRECTSOUNDBUFFER m_DSSoundBuffer; //ukazatel na rozhrani IDirectSoundBuffer
       DWORD m_dwBufferSize; //velikost bufferu v bajtech
       DWORD m_dwCreationFlags; //volby ktere jsem pouzili pri vytvareni bufferu

    public:
       virtual HRESULT Create(LPDIRECTSOUND lpDSound,DSBUFFERDESC* dsBufferDesc);
       HRESULT RestoreBuffer(bool* bWasRestored);
       HRESULT Play(DWORD dwPriority = 0,DWORD dwFlags = 0);
       HRESULT Stop();
       HRESULT Reset();
       HRESULT SetFrequency(long lFrequency);
       HRESULT SetPan(long lPan);
       HRESULT SetVolume(long lVolume);

       bool IsSoundPlaying();

       LPDIRECTSOUNDBUFFER GetBuffer() {return m_DSSoundBuffer;}

       CSoundBuffer(void);
       ~CSoundBuffer(void);
    };

Nynφ se m∙₧eme podbobn∞ji podφvat na jednotlivΘ funkce. Prvnφ bude konstruktor a destruktor, kde se provßdφ jen inicializace a ·klid a myslφm ₧e to nenφ t°eba podbrobn∞ji rozebφrat. Soubor SoundBuffer.cpp:

    #include "StdAfx.h"
    #include "Common.h"
    #include "soundbuffer.h"

    //********************************************************************************************
    //Konstruktor
    //********************************************************************************************

    CSoundBuffer::CSoundBuffer(void)
    {
       m_DSSoundBuffer = NULL; //neplatny ukazatel na rozhrani, neni jeste vytvoreno
       m_dwBufferSize = 0; //vychozi hodnoty promennych
       m_dwCreationFlags = 0;
    }
    //********************************************************************************************
    //Destruktor
    //********************************************************************************************

    CSoundBuffer::~CSoundBuffer(void)
    {
       SAFE_RELEASE(m_DSSoundBuffer); //uvolneni rozhrani z pameti
    }

DalÜφ si pobereme funkci Create

    //*************************************************************************************************************
    //Fce pro vytvoreni rozhrani pro zvukovy buffer
    //*************************************************************************************************************

    HRESULT CSoundBuffer::Create(LPDIRECTSOUND lpDSound,DSBUFFERDESC* dsBufferDesc)
    {
       HRESULT hRet = DS_OK;

       if (FAILED(hRet = lpDSound->CreateSoundBuffer(dsBufferDesc,&m_DSSoundBuffer,NULL))) //pokud vytvareni rozhrani selze
          return hRet; //vratime chybu dal

       m_dwBufferSize = dsBufferDesc->dwBufferBytes; //ulozeni velikosti bufferu
       m_dwCreationFlags = dsBufferDesc->dwFlags; //ulozeni p°φznak∙

       return hRet; //vraceni vysledku, sem uz by se melo dostat jen S_OK
    }

Funkce oΦekßvß ₧e jφ p°edßme ukazatel na rozhranφ IDirectSound a strukturu obsahujφcφ popis bufferu. Volßnφm funkce CreateSoundBuffer zmφn∞nΘho rozhranφ vytvo°φme rozhranφ kterΘ bude reprezentovat zvukov² buffer. P°edßme jφ popis bufferu, do druhΘho argumentu se ulo₧φ ukazatel na nov∞ vytvo°enΘ rozhranφ a poslednφ argument souvisφ s technologiφ COM a je v₧dy NULL. Pokud funkce vrßtφ chybnou hodnotu, nepokraΦuje se dßl. V opaΦnΘm p°φpad∞ si jen ulo₧φme velikost bufferu a p°φznaky.

    //*************************************************************************************************************
    //Fce pro obnoveni bufferu
    //*************************************************************************************************************

    HRESULT CSoundBuffer::RestoreBuffer(bool *bWasRestored)
    {
       HRESULT hRet;

       if (!m_DSSoundBuffer) //kontrola jestli je rozhrani vubec vytvorene
          return CO_E_NOTINITIALIZED;

       if (bWasRestored) //kontrola ukazatele
          *bWasRestored = false;

       DWORD dwStatus;
       if (FAILED(hRet = m_DSSoundBuffer->GetStatus(&dwStatus))) //pokud se nam nepodari ziskat stav bufferu
          return hRet; //tak rovnou koncime vracenim chyby

       if (dwStatus & DSBSTATUS_BUFFERLOST) //pokud byl buffer ztracen
       {
          do //budeme se v cyklu snazit ho obnovit
          { //dokud se to nepovede
             hRet = m_DSSoundBuffer->Restore();
             if (hRet == DSERR_BUFFERLOST)
                Sleep(10);
          }
          while ((hRet = m_DSSoundBuffer->Restore()) == DSERR_BUFFERLOST);

          if (bWasRestored) //ulozime do uzivatelske promenne ze buffer
          *bWasRestored = true; //byl obnoven

          return S_OK;
       }
       return S_FALSE;
    }

Funkce nejd°φv kontroluje ukazatel na rozhranφ bufferu a poslΘze ukazatel kter² byl zadßn jako parametr. Na mφsto kam ukazuje bWasRestored se v p°φpad∞ ₧e je ukazatel platn² ulo₧φ zda byl buffer obnoven nebo ne. Bude-li tato hodnota po vykonßnφ funkce nab²vat false, nemusφ to znamenat ₧e fuknce selhala, ale takΘ ₧e nebylo t°eba buffer obnovit. Pokud je pot°eba buffer obnovit, provßdφ se to v cyklu, kter² trvß tak dlouho, dokud funkce Restore rozhranφ IDirectSoundBuffer nevrßtφ jinou hodnotu ne₧ DSBSTATUS_BUFFERLOST. Pokud je buffer ztracen, je mo₧nΘ ₧e ho nebudeme moci obnovit prßv∞ v dob∞ volßnφ tΘto funkce, ale a₧ pozd∞ji. T°eba proto ₧e aplikace nemß fokus.

    //*************************************************************************************************************
    //Fce spusti prehravani bufferu
    //*************************************************************************************************************

    HRESULT CSoundBuffer::Play(DWORD dwPriority,DWORD dwFlags)
    {
       if (!m_DSSoundBuffer) //kontrola platnosti ukazatele na rozhrani
          return CO_E_NOTINITIALIZED;

       return m_DSSoundBuffer->Play(0,dwPriority,dwFlags);
    }

Funkce Play je jednoduchß. Kontroluje jen platnost ukazatele na rozhranφ, abychom se neodkazovali na neplatnΘ mφsto v pam∞ti a nezp∙sobili tak krach programu a pak u₧ jen volß metodu Play rozhranφ IDirectSoundBuffer a p°edßvß jφ argumenty kterΘ dostala. Jak si m∙₧ete vÜimnout v hlaviΦkovΘm souboru t°φdy CSoundBuffer, jsou pro oba parametry zadßny v²chozφ hodnoty, funkci proto m∙₧ete volat bez, s jednφm nebo s ob∞ma parametry. Prvnφ parametr je priorita a pokud je nula a budete mφt takovΘ buffery t°eba dva, v²sledn² efekt bude ten, ₧e budou hrßt oba najednou. Pokud bude u jednoho priorita 1, poΦkß se a₧ dohraje buffer s prioritou 0 a pak se spustφ buffer s prioritou 1.

    //*************************************************************************************************************
    //Fce nastavi frekvenci
    //*************************************************************************************************************

    HRESULT CSoundBuffer::SetFrequency(long lFrequency)
    {
       if (!m_DSSoundBuffer)
          return CO_E_NOTINITIALIZED;

       if (lFrequency != -1 && m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY)
          return m_DSSoundBuffer->SetFrequency(lFrequency);

       return S_FALSE;
    }

    //*************************************************************************************************************
    //Fce nastavi hlasitost
    //*************************************************************************************************************

    HRESULT CSoundBuffer::SetVolume(long lVolume)
    {
       if (!m_DSSoundBuffer)
          return CO_E_NOTINITIALIZED;

       if (m_dwCreationFlags & DSBCAPS_CTRLVOLUME)
          return m_DSSoundBuffer->SetVolume(lVolume);

       return S_FALSE;
    }
    //*************************************************************************************************************
    //Fce pro nastaveni vyvazenosti leveho a preveho kanalu
    //*************************************************************************************************************

    HRESULT CSoundBuffer::SetPan(long lPan)
    {
       if (!m_DSSoundBuffer)
          return CO_E_NOTINITIALIZED;

       if (m_dwCreationFlags & DSBCAPS_CTRLPAN)
          return m_DSSoundBuffer->SetPan(lPan);

       return S_FALSE;
    }
    //*************************************************************************************************************
    //Fce zastavi prehravani bufferu
    //*************************************************************************************************************

    HRESULT CSoundBuffer::Stop()
    {
       if (!m_DSSoundBuffer)
          return CO_E_NOTINITIALIZED;

       return m_DSSoundBuffer->Stop();
    }
    //*************************************************************************************************************
    //Fce nastavi pozici hraciho kurzoru v bufferu na nula
    //*************************************************************************************************************

    HRESULT CSoundBuffer::Reset()
    {
       if (!m_DSSoundBuffer)
          return CO_E_NOTINITIALIZED;

       return m_DSSoundBuffer->SetCurrentPosition(0);
    }

Prvnφ t°i funkce manipulujφ s r∙zn²mi parametry kterΘ jsou zvukov²m dat∙m vlastnφ :) Jde o nastavenφ frekvence (SetFrequency), vyvß₧enosti levΘho a pravΘho kanßlu (SetPan) a hlasitosti (SetVolume. VÜechny tyto funkce kontrolujφ ukazatel na rozhranφ a pak takΘ p°φznaky s jak²mi byl buffer vytvo°en. Pokud nenφ p°φznak pro danou vlastnost nastaven, znamenß to ₧e ji u befferu nem∙₧eme °φdit, m∞nit, jen ji Φφst. Pokud ano, zavolß se jen p°φsluÜnß funkce rozhranφ IDirectSoundBuffer a p°edß se jφ zadan² parametr. DalÜφ dv∞ funkce jsou Stop a Reset. Zase se jen kontroluje ukazatel na rozhranφ. Funkce Reset funguje tak, ₧e nastavφ pozici hracφho kurzoru na 0, dalo by se jφ takΘ °φkat Rewind. Poslednφ funkcφ t°φdy CSoundBuffer je IsSoundPlaying:

    //*************************************************************************************************************
    //Fce rekne jestli je buffer prehravan ci ne
    //*************************************************************************************************************

    bool CSoundBuffer::IsSoundPlaying()
    {
       if (!m_DSSoundBuffer)
          return false;

       DWORD dwStatus;
       m_DSSoundBuffer->GetStatus(&dwStatus);

       return (dwStatus & DSBSTATUS_PLAYING) ? true : false;
    }

Funkce je op∞t jednoduchß. Zda je buffer p°ehrßvßn zjistφme jednoduÜe tak, ₧e se ho zeptßme na stav pomocφ fuknce GetStatus. Pokud je buffer p°ehrßvßn vrßtφ se nßm DSBSTATUS_PLAYING a podle toho vrßtφme bu∩ true nebo false


To je ke t°φd∞ CSoundBuffer vÜe. Pokud °eÜenφ p°elo₧φte (Build->Build Solution), ob∞vφ se vßm op∞t chyba o nenalezenφ lib souboru knihovny OggCodec. Otev°ete soubor SoundBuffer.h a definici t°φdy upravte takto:
class OGGCODEC_API CSoundBuffer
P°idßnφm konstanty ze souboru Common.h jste v²vojovΘmu prost°edφ °ekli, ₧e chcete t°φdu exportovat a soubor lib bude automaticky vytvo°en. Pokud nynφ °eÜenφ zkusφte p°elo₧it znovu, nem∞la by se objevit ji₧ ₧ßdnß chyba. Upravte definici zp∞t na p∙vodnφ, proto₧e tuto t°φdu exportovat nebudeme. Ale pokud chcete, m∙₧ete ji samoz°ejm∞ takto ponechat. Na dalÜφ d∞nφ to nemß vliv :) M∙₧ete se pustit do dalÜφ Φßsti :)

Obsah