Ú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 napíšeme rozhraní IOggFile. Toto rozhraní nám bude sloužit pro přístup k metodám třídy COggFile (kterou taky napíšeme) a pomocí které budeme schopni přehrát jeden soubor ogg.

Obsah:

2.1 Rozhraní

V seriálech které se v Chipu objevují již sice bylo řečeno co to rozhraní je, ale myslím že nebude na škodu se tu o tom také zmínit. Rozhraní můžeme brát jako objekt, který nám umožňuje používat jiný objekt, komunikovat s jiným objektem. No, nezní to moc jako definice, snažím se to říct tak jak to chápu :) Třeba rozhraním pro komunikaci s počítačem může být klávesnice. Rozhraní v C++ je třída, která má specifické vlastnosti. Taková třída neobsahuje žádné členské proměnné, ale jen metody, které navíc musejí být všechny virtuální a všechny musejí mít přístup public. Třída je tedy abstraktní. Pokud nadefinujete třídu a budete se držet těchto pravidel, bude se jednat o rozhraní. Další vlastností rozhraní je, že z něj nemůžete vytvořit instanci. V případě rozhraní IOggFile pro třídu COggFile nemůžete v programovém kódu napsat:
IOggFile* pInterface = new IOggFile
To nelze a překladač vás na to v případě že na to zapomenete upozorní :) Dalším způsobem jak v C++ (Visual C++) vytvořit rozhraní je použití klíčového slova __interface namísto class. Záměrně píši Visual C++, protože toto klíčové slovo patří do skupiny "microsoftích" příkazů a nemusí s ním pracovat ostatní překladače. Rozdíl je v tom, že pokud použijete toto klíčové slovo, jste nuceni dodržet pravidla která jsou s rozhraním svázána. Na deklarovanou členksou proměnnou budete překladačem upozorněni stejně jako na metodu s přístupem private. Též při použití tohoto klíčového slova nemusíte před deklarace metod psát slovo virtual. To je doplněno automaticky, bere se to jako samosřejmost, ať to tam napíšete nebo ne. Je jedno jak budete deklarovat rozhraní, zda použítím slova class nebo __interface. V tomto kurzu použijeme __interface, jednak proto že nám to nedovolí udělat chybu a také proto že ve Visual C++ má rozhraní jinou ikonku než třída a je hned na první pohled vidět co je co :) Pro zdárné pochopení rozhraní je nutné znát dědičnost. Probírala se v kurzu C++ od 13. dílu.

Obsah

2.2 Rozhraní IOggFile

Jak už jsem napsal v úvodu, pomocí rozhraní IOggFile budeme přistupovat k metodám třídy COggFile. Abychom se vyhnuli zbytečným úpravám které bychom museli udělat kdybychom nejdříve napsali třídu COggFile a pak rozhraní, napíšeme rozhraní nyní. Ono sice v tomto případě by se jednalo jen o malé úpravy, respektive dopisování kódu, ale někomu by v tom stejně mohl vzniknout menší zmatek. Následuje tabulka s metodami které bude rozhraní obsahovat a které bude obsahovat i třída COggFile

    Metody třídy COggFile / rozhraní IOggFile
    Open Otevře soubor.
    Close Zavře soubor.
    CheckIfNeedReFillBuffer Zjistí zda je třeba zapsat nová data do streamovaného bufferu a v případě potřeby je zapíše.
    Play Spustí přehrávání.
    Stop Zastaví přehrávání.
    Pause Pozastaví přehrávání.
    GetOggFilePlayingTime Vrátí délku souboru v milisekundách.
    GetOggFilePCMSize Vrátí počet bajtů které by byly zapotřebí k dekódování celého souboru.
    GetOggFileVersion Vrátí verzi ogg souboru.
    GetOggFileVendor Vrátí jméno identifikující verzi kodeku ogg kterým byl soubor vytvořen.
    GetCountOfOggFileComments Vrátí počet komentářů v ogg souboru.
    GetOggFileComment Vrátí komentář z ogg souboru.
    GetStreamedBuffer Vrátí ukazatel na zvukový buffer (členská proměnná typu ukazatel na CSoundBuffer).
    GetRepeats Vrátí počet opakování přehrávání souboru.
    SetRepeats Nastaví počet opakování přehrávání ogg souboru.
    GetStatus Získání stavu v kterém se zrovna nachází třída COggFile.
    ConvertOggToWav Dekódování ogg souboru na klasický wav.
    AddRef Inicialiazace nové instance třídy.
    Release Uvolnění instance třídy z paměti.

Nyní se podíváme na soubor Interfaces.h, v kterém je rozhraní definováno. Přidejte do projektu OggCodec nový hlavičkový soubor a pojmenujte jej stejně. Postarejte se aby soubor nabyl následujícího obsahu.

    #pragma once

    class CSoundBuffer;

    __interface IOggFile
    {
    public:
       HRESULT   Open(const char* cFileName,LPDIRECTSOUND8 lpDSound,DWORD dwNotifyCount,WORD wBufferLength = 3) = 0;
       HRESULT   Close() = 0;
       HRESULT   CheckIfNeedReFillBuffer() = 0;
       HRESULT   Play(DWORD dwPriority = 0) = 0;
       HRESULT   Stop() = 0;
       HRESULT   Pause() = 0;

       //ruzne vlastnosti ogg souboru
       double   GetOggFilePlayingTime() = 0;
       DWORD   GetOggFilePCMSize() = 0;
       int   GetOggFileVersion() = 0;
       char*   GetOggFileVendor() = 0;
       int   GetCountOfOggFileComments() = 0;
       char*   GetOggFileComment(int nComment) = 0;
       CSoundBuffer*   GetStreamedBuffer() = 0;
       int   GetRepeats() = 0;
       bool   SetRepeats(int iRepeatCount) = 0;
       BYTE   GetStatus() = 0;

       HRESULT   ConvertOggToWav(const char* cOggFileName,const char* cWavFileName) = 0;

       HRESULT   AddRef() = 0;
       HRESULT   Release() = 0;
    };

Do souboru budeme ještě přidávat jedno rozhraní v třetí části kurzu, zatím nám ale stačí toto. Myslím že to je vše co jsem měl na srdci ohledně rozhraní, jdeme na třídu COggFile.

Obsah

2.3 Třída COggFile

Nemusíme si už uvádět tabulku metod, byla by stejná jako pro rozhraní IOggFile, ukážeme si rovnou soubor OggFile.h, kde uvidíte i vazbu mezi rozhraním a třídou.

    #pragma once

    int const IWAVEHEADERSIZE = 38;
    int const IWAVEDATASIZE = 8;

    int const IQUIET = 0;
    int const IBITS = 16;

    int const IENDBITS = 0;
    int const ISIGNED = 1;

    //nejake konstanty pro prehravani, stavy souboru
    #define OGGPLAY_REPEATINFINITE -1 //opakovat prehravani do nekoncecna
    #define OGGSTATUS_PLAYING 1 //ogg soubor je prave prehravan
    #define OGGSTATUS_STOPPED 2 //prehravani je zastaveno
    #define OGGSTATUS_PAUSED 3 //prehravani je pozastaveno

    //******************************************************************************************
    //Trida pro praci se soubory ogg
    //******************************************************************************************

    class COggFile : public IOggFile
    {
    private:
       CSoundBuffer* m_StreamedBuffer; //zvukovy buffer pro ulozeni zvukovych dat

       OggVorbis_File m_OggVorbisFile; //struktura ogg vorbis file
       FILE* m_OggFilePointer; //ukazatel na otevreny ogg soubor

       DWORD m_dwPCMSize; //velikost dekodovaneho ogg souboru v bajtech

    //promenne pro funkcnost streamovaneho bufferu
       DWORD m_dwLastPlayPos; //posledni pozice hraciho kurzoru v bufferu
       DWORD m_dwNextWriteOffset; //dalsi offset v bufferu odkud se bude zapisovat
       DWORD m_dwNotifySize; //po jak velkych usecich se ma zapisovat
       DWORD m_dwPlayProgress; //pocet prehranych bajtu souboru
       DWORD m_dwBufferSize; //celkova velikost bufferuv bajtech

       int m_iRepeatCount; //pocet opakovani
       BYTE m_bStatus; //stav tridy, prehravani, zastaveno, pozastaveno

       DWORD m_dwRef;

    //fce pro vytvoreni bufferu a nahrani dat
       HRESULT InitBufferAndLoadFirstData(LPDIRECTSOUND8 lpDSound,DWORD dwNotifyCount,WORD wBufferLength);
    //fce pro nahravani dat o velikosti dwBytesRead do bufferu na offset dwOffset
       bool WriteDataToBuffer(DWORD dwOffset,DWORD dwBytes,DWORD* dwBytesRead);
    public:
       virtual HRESULT Open(const char* FileName,LPDIRECTSOUND8 lpDSound,DWORD dwNotifyCount,WORD wBufferLength = 3); //otevre ogg z disku
       virtual HRESULT Close(); //zavreni ogg souboru
       virtual HRESULT CheckIfNeedReFillBuffer(); //fce testuje jestli je cas naplnit cast bufferu novymi daty a pokud ano tak tak ucini
       virtual HRESULT Play(DWORD dwPriority = 0); //prehravani ogg souboru s nastavenim priority
       virtual HRESULT Stop(); //zastaveni prehravani, ukazatel v souboru bude presunut na zacatek a pri spusteni se bude hrat od zacatku
       virtual HRESULT Pause(); //pozastaveni prehravani

    //ruzne vlastnosti ogg souboru
       virtual double GetOggFilePlayingTime() {return ov_time_total(&m_OggVorbisFile,-1);} //celkovy cas souboru
       virtual DWORD GetOggFilePCMSize() {return m_dwPCMSize;} //vrati velikost ogg souboru v bytech
       virtual int GetOggFileVersion() {return m_OggVorbisFile.vi->version;}
       virtual char* GetOggFileVendor() {return m_OggVorbisFile.vc->vendor;}
       virtual int GetCountOfOggFileComments() {return m_OggVorbisFile.vc->comments;}
       virtual char* GetOggFileComment(int nComment);
       virtual CSoundBuffer* GetStreamedBuffer() {return m_StreamedBuffer;}
       virtual int GetRepeats() {return m_iRepeatCount;}
       virtual bool SetRepeats(int iRepeatCount);
       virtual BYTE GetStatus() {return m_bStatus;}

       virtual HRESULT ConvertOggToWav(const char* cOggFileName,const char* cWavFileName);

       virtual HRESULT AddRef();
       virtual HRESULT Release();

       COggFile(void);
       ~COggFile(void);
    };

Jak je vidět třída je potomkem rozhraní IOggFile. V tom je celý fígl rozhraní. Třída COggFile může jako potomek zastoupit svého rodiče. Budeme tedy používat ukazatele typu IOggFile, které budou ukazovat na stejnou nebo různé instance třídy COggFile a přes rozhraní volat metody třídy COggFile. V třetí části ještě napíšeme funkci která bude vracet ukazatele na rozhraní. Jak si můžete všimnout je definováno několk konstant OGGSTATUS_..., které snad sami jistě říkají jaký stav třídy zastupují. Můžete vidět také několik členských proměnných, kterým se budu blíže věnovat až u funkcí kde budou použity. Můžeme poppjít. Konstruktor. Destruktor a metody související s vytvořením a uvolněním instance.

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

    COggFile::COggFile(void)
    {
       m_dwLastPlayPos = 0; //nulovani posledni pozice hraciho kurzoru
       m_dwPlayProgress = 0; //nulovani prubehu prehravani
       m_dwNextWriteOffset = 0; //nulovani pozice zapisovaciho kurzoru
       m_iRepeatCount = 0; //nastaveni poctu opakovani na nula, zvuk se prehraje jen jednou
       m_bStatus = OGGSTATUS_STOPPED; //stav nastavime na zastaveny
       m_StreamedBuffer = NULL;
    }
    //*******************************************************************************************************
    //Destruktor
    //*******************************************************************************************************

    COggFile::~COggFile(void)
    {
       Close(); //zavreni ogg souboru a uvolneni zdroju ktere k jeho otevreni a prehrani byly zabrany
    }
    //*******************************************************************************************************
    //Fce zvýší počet instancí na rozhraní IOggFile
    //*******************************************************************************************************

    HRESULT COggFile::AddRef()
    {
       return m_dwRef++;
    }
    //*******************************************************************************************************
    //Fce uvolní rozhraní
    //*******************************************************************************************************

    HRESULT COggFile::Release()
    {
       if (m_dwRef > 0)
          m_dwRef--;

       if (m_dwRef == 0)
       {
          delete this;
          return 0;
       }
       else
          return m_dwRef;
    }

Konstruktor a destruktor snad není nutné nějak popisovat. Dělají jen to co se od nich očekává. Inicializace a úklid. Metoda AddRef je také velmi jednoduchá, zvýší počet instancí rozhraní které ukazují na třídu COggFile (tak to budeme později používat, samo o sobě to jen inkrementuje hodnotu členské proměnné m_dwRef (References, odkazy)) a vrátí počet instancí. Pokud vytvoříme jednu třídu COggFile, můžeme na ní vytvořit rozhraní pomocí funkce kterou napíšeme ve třetí části. Pokud čtete seriál o DirectX jistě jste se s tím setkali, rozhraní jsem se naučil právě z nich :) Vždy předáte funkci jen proměnnou typu ukazatel na ukazatel na IOggFile do které chcete ukazatel uložit. Po uložení ukazatele se zavolá automaticky tato metoda, aby třída věděla že na ní existuje nový odkaz. Když nebudete v programu funkce třídy více potřebovat, zavoláte metodu Release přes rozhraní. Ta sníží počet instancí, odkazů na třídu o jeden který právě rušíte. Pokud zrušíte všechny odkazy, bude z paměti smazána samotná třída (delete this). Stejně vlastně pracuje i automatická správa paměti ve Visual Studiu NET, tzv. Garbage Collection. Také pokud zrušíte všechny ukazatele na objekt, usoudí se že ho už nepoužíváte a je automaticky uvolněn. Náš projekt ale Garbage Collection nepoužívá a proto musíme úklid zajistit sami. Při používání rozhraní je proto nutné nezapomínat volat metodu Release a to přesně tolikrát kolik jsme vytvořili odkazů, rozhraní, jiank nám i po skončení projektu zůstane třída viset v paměti. To se ale dá také programově ohlídat :) Nyní už se můžeme pustit do metody Open

    //*******************************************************************************************************
    //OggOpen: Fce pro otevreni ogg soubor
    //*******************************************************************************************************

    HRESULT COggFile::Open(const char *FileName,LPDIRECTSOUND8 lpDSound,DWORD dwNotifyCount,WORD wBufferLength)
    {
       HRESULT hRet;

       if ((m_OggFilePointer = fopen(FileName,"rb")) == NULL) //otevreni zvukoveho souboru
       {
          return -1; //vraceni chyby
       }

       //volani modifikovane fce pro otevreni ogg souboru, inicializaci dekoderu ogg
       //posledni parametr je velikost souboru, -1 znamena ze fce si velikost souboru, respektive jeho konec zjisti tim ze se presune na jeho konec
       if ((hRet = ov_open(m_OggFilePointer,&m_OggVorbisFile,NULL,0,-1)) < 0)
       {
          return -2;
       }

       //vypocet velikosti dekodovaneho ogg souboru v bajtech
       m_dwPCMSize = (DWORD)ceil(m_OggVorbisFile.vi->channels * m_OggVorbisFile.vi->rate * ov_time_total(&m_OggVorbisFile, -1) * IBITS / 8);

       //vypiseme do logu zakladni informace o ogg soubru ktery nacitame
       //ErrorMsg.Format("Opening Ogg Vorbis File: <b>%s</b>, Channels: <b>%d</b>, Rate: <b>%ldHz</b>, Samples: <b>%I64d</b>, Serial Number: <b>%ld</b>, Bitrate: <b>%ld</b>bits",cFileName->GetBuffer(),m_OggVorbisFile.vi->channels,m_OggVorbisFile.vi->rate,ov_pcm_total(&m_OggVorbisFile,-1),ov_serialnumber(&m_OggVorbisFile,-1),ov_bitrate(&m_OggVorbisFile,-1));
       //TRACE(ErrorMsg.GetBuffer());

       //vytvoreni zvukoveho bufferu a nahrani prvnich dat
       hRet = InitBufferAndLoadFirstData(lpDSound,dwNotifyCount,wBufferLength);

       //vraceni vysledku otevirani
       return hRet;
    }

Metoda očekává několik parametrů. První je jméno souboru, druhý je ukazatel na rozhraní IDirectSound. Další parametr udává kolikrát chceme do bufferu nahrát data při jednom jeho přehrání (dwNotifyCount) a poslední je velikost bufferu v bajtech (dwBufferLength). Pomocí těchto hodnot později vypočítáme po jak velkých blocích budeme data v bufferu obnovovat. Funkce nejprve otevře soubor pomocí standartní io funkce fopen a uloží si ukazatel na otevřený soubor. Tento ukazatel pak předá funkci ov_open, která neobsahuje parametr jako jméno souboru, sama si tedy soubor neotevře. Má to i svoje plus, můžete mít ogg soubor zapsaný v nějakým velkém souboru, něco na způsob archivu zip (ale nesmí být nijak komprimovaný ani zašifrovaný), otevřít ho a přesunout se na pozici, kde ogg soubor začíná. Ukazatel pak předáte zmíněné funkci ov_open, která ukazatel nepřesune na začátek souboru, ale začne číst data z aktuální pozice v souboru. Bohužel funkce ov_open neobsahuje žádný parametr jako počet bajtů souboru, což v reálu znemožňuje přehrávat soubor přímo z archivu. Funkce ov_open nejdřív načte potřebná data ze začátku souboru a pak se přesune na konec a začne číst odzadu. Tak zjistí jak je soubor dlouhý a spočte jeho délku. Když budeme mít ogg soubor v archivu, je velmi pravděpodobné že data na konci archivu už nebudou patřit otevíranému ogg souboru a funkce ov_open tak selže. Jediný případ kdy to bude fungovat je tedy případ kdy bude ogg soubor posledním souborem v archivu. Jelikož jsou ale zdrojové kódy knihoven kodeku ogg dostupné volně ke stažení, není ani toto nepřekonatelný problém. Dalším parametrem který funkce ov_open žádá je struktura OggVorbis_File, která obsahuje další struktury a proměnné potřebné jednak pro dekódování souboru, jednak jsou v těchto strukturách všechny důležité informace o souboru. Všechny další funkce z knihovny vorbisfile.dll vyžadují tuto strukturu jako parametr. Pokud funkce ov_open neselže, bude struktura inicializována a bude možno ji používat při volání dalších funkcí. Není důležité znát členské proměnné struktury OggVorbis_File, můžete ji používat stejně jako strukturu FILE při práci s "obyčejnými" soubory, ale přesto se tu o některých proměnných zmíním:

  • vorbis_info vi; - dílčí struktura obsahující informace jako verze kodéru použitého pro vytvoření souboru, počet kanálů, počet vzorků, minimální, průměrný a maximální bitrate (počet bitů použitých k uchování jedné sekundy záznamu).
  • vorbis_comment vc; - dílčí struktura obsahující pole komentářů z ogg souboru a související proměnné.
  • ov_callbacks callbacks; - struktura uchovávající ukazatele na funkce pro práci se soubory (čtení, psaní, přesun v souboru a zavření souboru). Standartně jsou tyto ukazatele nastaveny na funkce fread, fwrite, fseek a fclose. Je možné je ale nastavit na jiné, uživatelem definované funkce. Pokud budete chtít použít jiné funkce než zmíněné standartní, použijte k otevření souboru funkci ov_open_callbacks, jejíž poslední parametr je ukazatel na zmíněnou strukturu ov_callbacks.
Poslední dva parametry funkce ov_open se používají v případě že již byla nějaká data ze zdrojového souboru načtena. Většinou jsou ale NULL a 0 a my nebudeme v tomto kurzu dělat žádné vyjímky :) Pokud se podaří otevřít soubor standartní funkcí fopen a následně neselže ani funkce ov_open, můžeme se pustit do dekódování. Ale nejdřív :) Nejdřív je třeba zajistit ještě několik maličkostí. m_dwPCMSize je velikost čistých audio dat dekódovaného souboru v bajtech, nepočítají se do toho hlavičky ogg souboru. Tuto proměnnou využijeme při zjišťování počtu přehraných bajtů a rozhodování o konci přehrávání. Funkce ov_time_total vrací délku souboru v sekundách. Parametry jsou struktura OggVorbis_File a číslo (ID) fyzického bitového proudu (bitstreamu). Když zadáte -1, znamená to aktuální bitstream. Způsob výpočtu se shoduje s tím co jsem uvedl v úvodu. Dále si můžete všimnout že je zakomentována část s výpisem informací o souboru do debug souboru. Je to jen proto že v projektu funkci nemám definovanou. Doporučuji vám ale si podobnou funkci napsat a průběžně si vypisovat do souboru co se v programu děje. Hlavně vypisovat chybové hlášky, pomáhá to při hledání problémů. Na konci se už volá jen funkce která vytvoří zvukový buffer pro nově otevřený soubor a naplní ho prvními daty. A tady je:

    //*******************************************************************************************************
    //Fce pro vytvoreni zvukoveho bufferu a nacteni prvnich dat do bufferu
    //*******************************************************************************************************
    HRESULT COggFile::InitBufferAndLoadFirstData(LPDIRECTSOUND8 lpDSound,DWORD dwNotifyCount,WORD wBufferLength)

    {
       HRESULT hRet;
       DWORD dwBufferSize, dwNotifySize;

       //vypocet velikosti bufferu pro uchovani wBufferLength sekund zvuku
       //dalsi je vypocet velikosti bloku dat po kterych se bude buffer obnovovat, po kolika bajtech budeme do buffer
       //nahravat nova data
       //posledni je prepocet velikosti bufferu, pokud bude zadan nesikovne pocet obnoveni bufferu, muze nam dojit pri deleni k zaokrouhleni
       //protoze se pracuje s celymi cisly

       dwBufferSize = (DWORD)ceil(float(m_OggVorbisFile.vi->channels * m_OggVorbisFile.vi->rate * wBufferLength * IBITS / 8));
       dwNotifySize = (DWORD)dwBufferSize / dwNotifyCount;
       m_dwBufferSize = dwBufferSize = dwNotifyCount * dwNotifySize;

       m_dwNotifySize = dwNotifySize; //ulozeni poctu obnoveni bufferu za jedno jeho prehrani

       WAVEFORMATEX wfx; //struktura pro definovani formatu zvuku
       ZeroMemory(&wfx,sizeof(wfx)); //nulovani pameti
       wfx.cbSize = sizeof(wfx); //dulezite, nataveni velikosti struktury
       wfx.wFormatTag = WAVE_FORMAT_PCM; //format dat - klasicke nekomprimovane data, PCM jako u wave
       wfx.nChannels = m_OggVorbisFile.vi->channels; //pocet kanalu
       wfx.nSamplesPerSec = m_OggVorbisFile.vi->rate; //pocet vzorku za sekundu
       wfx.nAvgBytesPerSec = m_OggVorbisFile.vi->channels * m_OggVorbisFile.vi->rate * IBITS / 8;
       wfx.nBlockAlign = m_OggVorbisFile.vi->channels * IBITS / 8;
       wfx.wBitsPerSample = IBITS; //bitu na vzorek

       DSBUFFERDESC dsBufferDesc; //struktura obsahujici popis zvukove bufferu - Buffer Description
       ZeroMemory(&dsBufferDesc,sizeof(dsBufferDesc));
       dsBufferDesc.dwSize = sizeof(DSBUFFERDESC);
       dsBufferDesc.dwBufferBytes = dwBufferSize; //velikost zvukoveho bufferu
       //v priznacich nastavime co vsechno chceme aby buffer umoznoval, umel
       //DSBCAPS_CTRLPAN - chci mit moznost ridit vyvazeni leveho a praveho kanalu
       //DSBCAPS_CTRLVOLUME - chci mit moznost ridit hlasitost
       //DSBCAPS_CTRLFREQUENCY - chci mit moznst zmenit frekvenci, kmitocet
       //DSBCAPS_GETCURENTPOSITION2 - chci mit moznost ptat se na pozici hraciho kurzoru, tato volba je nezbytne nutna pro
       //implementaci streamovaneho bufferu, bez tech predchozich se obejdeme

       dsBufferDesc.dwFlags = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GETCURRENTPOSITION2;
       dsBufferDesc.lpwfxFormat = &wfx; //format audio dat, viz struktura inicilizovana vyse

       m_StreamedBuffer = new CSoundBuffer; //vytvoreni noveho objektu zvukoveho bufferu

       if (!m_StreamedBuffer) //kontrolaukazatele aneb byl objekt vytvoren
       {
          //TRACE("Error: Cannot create Sound Buffer!");
          return -3;
       }

       //zavolani metody create tridy CSoundBuffer pro vytvoreni bufferu, vsechno co chce je vyse inicilizovana struktura
       //uchovavajici popis bufferu

       if(FAILED(hRet = m_StreamedBuffer->Create(lpDSound,&dsBufferDesc)))
          return hRet;

       //ted naplneni bufferu daty z ogg souboru
       //nejdriv se radsi presuneme na zacatek souboru, pro jistotu

       hRet = ov_raw_seek(&m_OggVorbisFile,1);

       //zapsani dat do bufferu
       WriteDataToBuffer(0,m_dwBufferSize,NULL);

       return S_OK;
    }

Na první pohled možná mnoho kódu, ale přesto není zas tak moc složitý. Myslím že první řádky jsou dostatečně okomentovány v kódu. Dále je inicializována strukura WAVEFORMATEX, která popisuje formát dat wave. Zvuk nemůžeme přehrát komprimovaný, musíme ho vždy dekomprimovat a přehrát v dekomprimované formě. Nejedná se vlastně o nic jiného než přiřazování příslušejících si hodnot struktury WAVEFORMATEX a OggVorbis_File->vi. Samotná tato struktura nám ale nestačí. K vytvoření zvukového bufferu potřebujeme víc informací, jako jeho velikost a nějaké požadavky, co všechno by měl umět. K tomu slouží struktura DSBBUFFERDESC, která je součástí SDK DirectX. V komentáři jsou vysvětleny příznaky které nastavujeme. Pokud chcete znát všechny možnosti, zabrouzdejte do dokumentace k DirectSound kde najdete kapitolu o zvukových bufferech. Jde například o možnosti rozšíření na 3D buffer, kontrola pozice zdroje zvuku v prostoru a přehrávání 3D zvuku. Samozřejmě ne vše jde zkombinovat se vším a ne vše musí kdejaké železo podporovat. Následuje vytvoření zvukového bufferu a přesunutí se na začátek ogg souboru pomocí funkce ov_raw_seek. Jak je vidět, opět žádá strukturu OggVorbis_File. Druhý parametr je offset v komprimovaných bajtech (komprimovanými bajty se myslí že zadáváte offset v ogg souboru, pohybujete se tedy ještě v komprimovaných datech, to je něco jiného než zadat offset v dekódovaném souboru). Další funkce pro změnu pozice v souboru jsou ov_pcm_seek a ov_time_seek. Obě mají také dva parametry, u první funkce se offset zadává jako číslo vzorku, v druhém případě je to čas. Poslední je volání funkce která dekóduje ogg soubor a dekódovaná data zapíše do zvukového bufferu:

    //***************************************************************************************************************
    //Fce pro zapsani dat do zvukoveho bufferu
    //***************************************************************************************************************

    bool COggFile::WriteDataToBuffer(DWORD dwOffset,DWORD dwBytes,DWORD* dwBytesRead)
    {
       LPVOID lpvPtr1 = NULL; //ukazatel na prvni cast pameti pro zapis do bufferu
       DWORD DataSize1 = 0; //velikost dat ktera se zapisi do prvni casti
       LPVOID lpvPtr2 = NULL; //ukazatel na druhou cast pameti pro zapis do bufferu
       DWORD DataSize2 = 0; //velikost dat ktera se zapisi do druhe casti
       long lBytesRead = 0; //pocet prectenzch bajtu souboru
       DWORD lSize = 0;
       int iCurrStream = 0;
       HRESULT hRet;

       //nejdriv dekodujeme potrebnou cast ogg souboru
       char* DataBuffer = new char[dwBytes];

       while ((lBytesRead = ov_read(&m_OggVorbisFile,DataBuffer + lSize,dwBytes - lSize,IENDBITS,IBITS / 8,ISIGNED,&iCurrStream)) != 0)
       {
          if (lBytesRead < 0 && !IQUIET)
          {
             if ((lBytesRead != OV_HOLE) && (lBytesRead != OV_EBADLINK))
                break;
             continue;
          }

          lSize += lBytesRead;
       }

       if (dwBytesRead)
          *dwBytesRead = dwBytes - lSize;

       //ted zapsani dat do sound bufferu
       hRet = m_StreamedBuffer->GetBuffer()->Lock(dwOffset,lSize,&lpvPtr1,&DataSize1,&lpvPtr2,&DataSize2,0L);

       //if (DataSize1 != dwBytes)
          // WriteToDebugFile(__FILE__,__LINE__,"Cannot copy all data!",true);


       if (hRet == DSERR_BUFFERLOST)
       {
          m_StreamedBuffer->RestoreBuffer(NULL);
          hRet = m_StreamedBuffer->GetBuffer()->Lock(dwOffset,dwBytes,&lpvPtr1,&DataSize1,&lpvPtr2,&DataSize2,0L);
       }

       if (SUCCEEDED(hRet))
       {
          CopyMemory(lpvPtr1,DataBuffer,DataSize1);
          if (lpvPtr2 != NULL)
          {
             CopyMemory(lpvPtr2,DataBuffer + DataSize1,DataSize2);
          }

          hRet = m_StreamedBuffer->GetBuffer()->Unlock(lpvPtr1,DataSize1,lpvPtr2,DataSize2);

          if (SUCCEEDED(hRet))
          {
             SAFE_DELETE_ARRAY(DataBuffer);
             return true;
          }
       }

       SAFE_DELETE_ARRAY(DataBuffer);
       return false;
    }

Další ze stěžejních funkcí třídy. Pro její pochopení je nutné pochopit způsob práce streamovaného bufferu, který je popisován v úvodu. Její úkol je docela jednoduchý, a to zapsat do zvukového bufferu dwBytes bajtů dekódovaných zvukových dat ze souboru ogg a zápis začít na pozici dwOffset počítané od nuly (začátek bufferu, první bajt bufferu). Nelekejte se množství proměnných hned na začátku funkce :) První co ve funkci uděláme je alokace pole bajtů do kterého budeme ukládat dekódovaná zvuková data. Následuje cyklus v kterém si určitě všimnete funkce ov_read. Funkce je definována v SDK Ogg a slouží právě k dekomprimaci/dekódování ogg souboru do formátu raw wav (čístý wav, žádné hlavičky a doplňující informace ke zvuku, prostě jen hudební vzorky které pokud se dostanou až do reproduktoru, měly by připomínat něco jako hudbu, řeč atd.). Funkce má trochu víc parametrů než ty se kterými jsme se doposud setkali. První je jako vždy struktura OggVorbis_File. Druhý je ukazatel na buffer typu char do kterého chceme uložit dekódovaná data. Tj. DataBuffer + lSize. Při následujícím vysvětlení funkce některých proměnných se budu zmiňovat o dekódovaných bajtech, tím mám na mysli velikost audio dat, které funkce ov_read dekódovala, vrátila. Může přečíst z ogg souboru n bajtů, ale dekódovaných bajtů bude m (m > n). Dekódoavné bajty tedy znamenají m. Doufám že to chápete, těžko se to vysvětluje :( DataBuffer je ukazatel a k němu přičteme hodnotu lSize, která říká kolik bajtů už jsme dekódovali. Funkce ov_read neumí vrátit dwBytes bajtů, typicky dekóduje 4096 bajtů. Počet bajtů které funkce dekódovala a uložila do bufferu (začátek je na DataBuffer + lSize) je též návratová hodnota funkce pokud nedojde k nějaké chybě. Proto si tuto hodnotu ukládáme do proměnné lBytesRead a tuto hodnotu na konci cyklu přičítáme k proměnné lSize, čímž se posunujeme v bufferu DataBuffer stále dopředu a nepřepisujeme si data dekódovaná dříve. Třetí parametr funkce ov_read je naopak počet bajtů k dekódování: dwBytes - lSize. Tj zadáváme počet bajtů který byl zadán funkci mínus to co už máme dekódované v bufferu. Další parametry se týkají výstupního formátu audio vzorků. Třetí parametr udávává formát bajtů, big nebo little endian (jedná se o způsob uložení bajtů v paměti). Typická hodnota je 0, tj. little endian. To znamená že bajt s největší váhou je vlevo a bajt s nejmnenší váhou je vpravo (Bajty jsou číslovány z prava do leva od nuly). U big endian je to obráceně, ostatně jak také vyplývá z názvu. Tento parametr má význam pokud budete chtít programovat přehrávač ogg i pod jinou platformu než pro procesory INTEL, která používá big endian. Pátý parametr je velikost slova, přesně řečeno udává kolika bitové chceme vzorky, 1 pro 8bitový zvuk, 2 pro 16bitový. Šestý parametr říká jestli mají být vzorky bez znaménka nebo se znaménkem (zda jsou signed nebo unsigned). Poslední je ukazatel na číslo aktuálního bitstreamu. Jak je vidět, použil jsem 0. Tenhle parametr bude mít zase význam při dekódování prokládaného ogg souboru, címž se tady nebudeme zabývat. Jak jsem říkal již dříve, funkce vrací počet bajtů dekódovaných audio dat. Pokud je návratová hodnota rovna 0, znamená to EOF, konec souboru. Hodnoty menší než nula jsou chyby, na které se podívejte do dokumentace. To by bylo dekóvání části ogg souboru která zabere v paměti dwBytes bajtů. Do proměnné na kterou ukazuje dwBytesRead se uloží počet bajtů které byly skutečne dekódovány. Nemusí se vždy podařit dekódovat dwBytes bajtů které chceme. Význam této proměnné pochopíte v další funkci. Teď ještě překopírovat obsah bufferu DataBuffer do zvukového bufferu. Abychom to provedli co nejrychleji, poskytuje zvukový bufferu metodu Lock, která nám zpřístupní paměť zvukového bufferu pomocí jednoho nebo dvou ukazatelů. Budeme mít přímý přístup do paměti bufferu. První její paremtr je offset na kterém chceme do paměti zvukového bufferu začít zapisovat (dwOffset), druhý je počet bajtů kolik chceme zapsat (dwBytes). Zbylé parametry jsou výstupní. Postupně se nám do lpvPtr1 uloží ukazatel na první část paměti zvukového bufferu kam můžeme zapisovat, do DataSize1 počet bajtů které můžeme zapsat do paměti na kterou ukazuje lpvPtr1. Poslední dva parametry mají význam úplně stejný, jen je to ukazatel na druhou část paměti a druhý počet bajtů na zapsání. K čemu dva ukazatele a dvě proměnné s počtem bajtů které můžeme do paměti zapsat? Již dříve jsem se zmiňoval že se můžeme nacházet u konce zvukového bufferu a že tam nebude dost místa pro zapsání dwBytes bytů. Nemůžeme zapat data i dál, to bychom přepsali pamět která nám nepatří. Naštěstí funkce Lock počítá s tímto problémem a proto když nemůže zajistit zapsání dwBytes bytů na adrese lpvPtr1, zajistí zapsání m bytů na adrese lpvPtr1 a n bytů na adrese lpvPtr2 (n+m = dwBytes). Jelikož máme zajištěno že tato funkce se bude volat vždy jen když bude vzdálenost mezi zapisovacím kurzorem a hracím kurzorem větší než dwBytes, může nastat popisovaná situace jen u konce bufferu. V ostatních případech bude vždy lpvPtr2 = NULL a DataSize2 = 0, tedy vše se zapíše na lpvPtr1. Pokud situace nastane, zapíšeme data do konce bufferu, lpvPtr2 bude ukazovat na zčátek bufferu a to co se nevešlo na konec zapíšeme zase na začátek. Pokud jste četli úvod, neměli by jte mít nejasnosti ohledně, proč to? Přesun obsahu bufferu DataBuffer do paměti zvukového bufferu pomocí funkce CopyMemory a ošetření NULL ukazatele lpvPtr2 snad už není třeba podrobně vysvětlovat. Ano, máte pravdu, proces by se dal ještě urychlit zápisem dat přímo do paměti zvukového bufferu. Ušetřili bychom si paměťové přesuny které jsou dost časově náročné. Můžete to zkusit. Myslím že pro lepší pochopení dekódování a zápisu dat do zvukového bufferu vůbec, je tento způsob lepší. V praxi bych radši viděl zápis přímo do zvukového bufferu :)

    //**********************************************************************************************************
    //Fce pro kontrolu nutnosti zapisu novych dat do bufferu
    //**********************************************************************************************************

    HRESULT COggFile::CheckIfNeedReFillBuffer()
    {
       HRESULT hRet = S_FALSE;
       bool bRet = false;
       DWORD dwCurrPlayPos = 0;
       DWORD dwDataDelta = 0;
       DWORD dwCursorsDistance = 0;

       if (m_StreamedBuffer->IsSoundPlaying() || m_bStatus != OGGSTATUS_PAUSED)
       {
          //ziskani aktualni pozice hraciho kurzoru
          if(FAILED(hRet = m_StreamedBuffer->GetBuffer()->GetCurrentPosition(&dwCurrPlayPos,NULL)))
             return hRet;

          if ((m_dwPlayProgress + dwCurrPlayPos) >= m_dwPCMSize) //tady to se zpracovava pokud dojde k prehrani celeho souboru
          {
             hRet = m_StreamedBuffer->Stop(); //pokud dojde k prehrani celeho souboru, zastavime prehravani bufferu
             hRet = m_StreamedBuffer->Reset(); //a resetujeme buffer a prislusne promenne
             m_dwNextWriteOffset = 0; //zapis bude zacinat opet na nule
             m_dwPlayProgress = 0; //stejne tak pocet prehranzch bytu je nula
             m_dwLastPlayPos = 0; //posledni pozice hraciho kurzoru taktez
             dwCursorsDistance = 0; //vzdalenost hraciho a zapisovaciho kurzoru taktez nulova
             ov_raw_seek(&m_OggVorbisFile,0); //presun na zacatek v ogg souboru
             WriteDataToBuffer(0,m_dwBufferSize,NULL); //naplneni bufferu daty z ogg souboru od zacatku, kdyby byla pozdeji spustena fce play
             //tak at to tam je
             if (!m_iRepeatCount) //pokud je pocet opakovani 0
             {
                //nastavime stav zastaveno
                m_bStatus = OGGSTATUS_STOPPED;
                return S_FALSE; //vratime false jako indikaci ze nedoslo k obnoveni dat v bufferu
             }

             if (m_iRepeatCount != OGGPLAY_REPEATINFINITE) //pokud pocet opakovani neni nula a neni ani roven konstante pro
                m_iRepeatCount--; //nekonecno opakovani, snizime pocet opakovani

             hRet = m_StreamedBuffer->Play(0,DSBPLAY_LOOPING); //zacneme znovu prehravat buffer, pac se este neprehral tolikrat jak je nastaveno
             return hRet;
          }

          //pokud je posledni ulozena pozice hraciho kurzoru vetsi nez pozice kurzoru aktualni, znamena to ze
          //buffer dosahl konce a preskocil zpet na zacatek, podle toho musime pocitat vzdalenost kurzoru rozdilne
          if (m_dwLastPlayPos > dwCurrPlayPos)
             //hraci kurzor presel zpet na zacatek, musime vypocitat kolik bytu zbylo na konci bufferu neaktualizovanzch od posledni aktualizace
             //a pricist k nim pocet bytu co uz byly prehrany znova od zacatku a tak ziskame vzdalenost kurzoru
             //vzdalenost mezi hracim a zapisovacim kurzorem
             dwCursorsDistance = (m_dwBufferSize - m_dwNextWriteOffset) + dwCurrPlayPos;
          else
             //hraci kurzor je pred koncem, zapisovaci za nim, vzdalenost je rozdil jejich pozic
             //abychom nepocitali i bajt na kterem hraci kurzor zrovna je
             dwCursorsDistance = dwCurrPlayPos - m_dwNextWriteOffset;

          //ted se mrknem jestli vzdalenost kurzoru presahla pocet bajtu po kterych se ma buffer obnovovat
          if (dwCursorsDistance >= m_dwNotifySize) //a pokud ano, zapiseme do bufferu tolik bytu jako je vzdalenost kurzoru
          { //cili data co tam zapiseme budou koncit na poslednim bajtu pred pozici
             bRet = WriteDataToBuffer(m_dwNextWriteOffset,dwCursorsDistance - 1,&dwDataDelta); //hraciho kurzoru, tedy mela by, muze se stat
             //vzdalenost mezi kurzory bude licha a v tom pripade nam fce ov_read ktera dekoduje ogg soubor
             m_dwNextWriteOffset += dwCursorsDistance - 1 - dwDataDelta; //vrati o nekolik bytu min, dekoduje totiz vzdy sudy pocet a ten pocet bajtu
              //musime snizit pozici dalsiho zapisovani, aby nevznikla jakasi dira, viz dwDataDelta
             if (m_dwNextWriteOffset >= m_dwBufferSize) //tady se hlida situace kdy je psaci kurzor skoro na konci a jeho aktualizaci o pocet zapanzch bajtu by doslo k prekroceni bufferu
                m_dwNextWriteOffset -= m_dwBufferSize; //odecteme proto velikost bufferu a psaci kurzor se dostane na spravnou pozici od zacatku bufferu

             if (m_dwLastPlayPos > dwCurrPlayPos)
                m_dwPlayProgress += m_dwBufferSize;

             m_dwLastPlayPos = dwCurrPlayPos; //aktualizace posledni pozice hraciho kurzoru
          }
       }

       return hRet;
    }

Funkce se stará o hlídání aktuálnosti dat v bufferu. Hlídá vzdálenost mezi zapisovacím a hracím kurzorem a v případě že překročí m_dwNotifySize tak do bufferu nahraje nová data. Funkce je myslím dostatečně okomentovaná, proto se jí nebudu tak podrobně zabývat. Úplně první rozhodovací blok hlídá jestli je přehrávání vůbec spuštěné, pokud není, nemá cenu provádět nějaké další testy. V opačném případě následuje kontrola dokončení přehrávání a počítání počtu opakování. Pokud byl zvukový soubor přehrán m_iRepeatCount krát, tak se přehrávání zastaví. Po ošetření těchto stavů následuje výpočet vzdálenosti zapisovacího a hracího kurzoru, který je různý pro dvě možné situace. Jednak může pozice hracího kurzoru být menší než je poslední známá pozice hracího kurzoru (tj. uložená pozice z minulého volání funkce), pak je vzdálenost kurzorů dwCursorsDistance = dwCurrPlayPos - m_dwNextWriteOffset. Ale situace může být i opačná, tj. poslední známá pozice je větší než aktuální pozice hracího kurzoru, to znamená že se hrací kurzor od posledního volání funkce vrátil zpět na začátek bufferu. Buffer byl přehrán a přehrávání jde od začátku. Výpočet vzdálenosti je dwCursorsDistance = (m_dwBufferSize - m_dwNextWriteOffset) + dwCurrPlayPos. Doufám že názvy proměnných mluví za sebe a že nebude problém pochopit proč je výpočet právě takový. Když je potřeba je buffer naplněn daty voláním funkce WriteDataToBuffer. Poslední parametr je dwDataDelta, do kterého se uloží počet bajtů které nebylo možno dekódovat do paměti zvukového bufferu. Jak jsem říkal u implementace funkce WriteDataToBuffer, funkce ov_read nemusí být vždy schopná dekódovat tolik bajtů kolik bychom chtěli. Funkce WriteDataToBuffer to kontroluje a vždy nám do propměnné dwDataDelta uloží počet bajtů které nebyly do zvukového bufferu zapsány. O tuto hodnotu snížíme pozici dalšího místa zápisu dat, abychom kompenzovali mezeru. Může se zdát, že taková 3bajtová mezera nemůže být vůbec znát, ale věřte mi, je to slyšet! Na konci funkce se aktualizuje průbeh prehrávani který se počítá v bajtech dekódovaného souboru. Už toho moc nezbývá, takže honem na další funkce :)

//**********************************************************************************************************
//Funkce pro zavreni ogg souboru
//**********************************************************************************************************

HRESULT COggFile::Close()
{

   if (m_OggFilePointer)
      fclose(m_OggFilePointer);

   ov_clear(&m_OggVorbisFile);

return 0;
}
//**********************************************************************************************************
//Funkce pro prevod ogg-wav
//**********************************************************************************************************

HRESULT COggFile::ConvertOggToWav(const char* cOggFileName,const char* cWavFileName)
{
   char AudioData[4096]; //buffer pro nacitani dat, 4096 je maximalni pocet bytu kolik nacte fce ov_read pri jednom volani
    //zaroven takove cislo, mocnina dvou je nejvodnejsi pro prenost pres sbernici do souboru na disku
   OggVorbis_File OggVorbisFile; //struktura OggVorbisFiloe ktera obsahuje informace o ogg souboru
   FILE* FilePointer; //ukazatel na soubor
   FILE* OutputFilePointer; //ukazatel na vystupni soubor

   if ((FilePointer = fopen(cOggFileName,"rb")) == NULL) //ogg soubor musime nejdriv otvrit pres normalni fopen a pak zavolat
      return -1; //ov_open, ktera naplni strukturu OggVorbisFile

   if ((OutputFilePointer = fopen(cWavFileName,"wb")) == NULL) //otevreni vystupniho souboru
      return -2;

   if ((ov_open(FilePointer,&OggVorbisFile,NULL,0,-1) < 0)) //naplneni struktury OggVorbisFile, precteni hlavicky ogg souboru
      return -3;

   //spocitani velikosti dekodovaneho zvuku, to musiem ulozit do wav hlavicky
   long iDataSize = (long)ceil(OggVorbisFile.vi->channels * OggVorbisFile.vi->rate * ov_time_total(&OggVorbisFile, -1) * IBITS/8);

   //velikost wav souboru,audio data + hlavicka
   int riffSize = iDataSize + IWAVEHEADERSIZE;
   int ckSize = sizeof(WAVEFORMATEX); //velikost struktury WAVEFORMATEX
   WAVEFORMATEX WaveFormat;
   WaveFormat.cbSize = ckSize; //nastaveni velikosti
   WaveFormat.wFormatTag = WAVE_FORMAT_PCM; //format nastavime na normalni nekomprimovany wav
   WaveFormat.nChannels = OggVorbisFile.vi->channels; //nastaveni poctu kanalu
   WaveFormat.nSamplesPerSec = OggVorbisFile.vi->rate; //pocet vzorku za sekundu
   WaveFormat.nAvgBytesPerSec = OggVorbisFile.vi->rate * OggVorbisFile.vi->channels * IBITS / 8; //prumerne bytu za sekundu
   WaveFormat.nBlockAlign = OggVorbisFile.vi->channels * IBITS / 8;
   WaveFormat.wBitsPerSample = IBITS; //pocet bitu na vzorek

   //ukladani dat z hlavicky do vystupniho souboru
   fprintf(OutputFilePointer,"RIFF");
   fwrite(&riffSize,1,sizeof(riffSize),OutputFilePointer);
   fprintf(OutputFilePointer,"WAVE");
   fprintf(OutputFilePointer,"fmt ");
   fwrite(&ckSize,1,sizeof(ckSize),OutputFilePointer);
   fwrite(&WaveFormat,1,sizeof(WaveFormat),OutputFilePointer);
   fprintf(OutputFilePointer,"data");
   fwrite(&iDataSize,1,sizeof(iDataSize),OutputFilePointer);

   long iBytesRead; //sem se bude ukladat kolik bytu bylo precteno z ogg souboru
   long iSize = 0; //pocet celkem nactenych bytu
   int iCurrSection; //aktualni cislo logickeho proudu v ogg souboru

   //cyklus pro nacitani a dekodovani ogg audio dat, o dekodovani se stara fce ov_read, cyklus pokracuje tak dlouho dokud fce vraci ze precetla vic jak 0 bytu
   while ((iBytesRead = ov_read(&OggVorbisFile, AudioData, iDataSize - iSize,IENDBITS,IBITS / 8,ISIGNED,&iCurrSection)) != 0)
   {
      if (iBytesRead < 0 && !IQUIET)
         continue;

      iSize += iBytesRead; //aktualizace poctu nactenych bytu

      //ulozeni dekodovanych dat z bufferu, pameti do souboru
      fwrite(AudioData,1,iBytesRead,OutputFilePointer);
   }

   //dekodovani je hotovo, zavreni ogg souboru
   fclose(FilePointer);
   //zavreni vystupniho wav souboru
   fclose(OutputFilePointer);

   //klizeni struktury OggVorbisFile
   ov_clear(&OggVorbisFile);

   //vraceni 0, dekodovani probehlo uspesne
   return 0;
}

//**********************************************************************************************************
//Vrati komentar z ogg souboru
//**********************************************************************************************************

char* COggFile::GetOggFileComment(int nComment)
{

   return m_OggVorbisFile.vc->user_comments[nComment];
}
//**********************************************************************************************************
//Nastavi pocet opakovani prehravani
//**********************************************************************************************************

bool COggFile::SetRepeats(int iRepeatCount)
{
   if (iRepeatCount < OGGPLAY_REPEATINFINITE)
      return false;

   m_iRepeatCount = iRepeatCount;
   return true;
}
//**********************************************************************************************************
//Spusti prehravani
//**********************************************************************************************************

HRESULT COggFile::Play(DWORD dwPriority)
{
   m_bStatus = OGGSTATUS_PLAYING;
   return m_StreamedBuffer->Play(dwPriority,DSBPLAY_LOOPING);
}
//**********************************************************************************************************
//Zastavi prehravani
//**********************************************************************************************************

HRESULT COggFile::Stop()
{
   m_bStatus = OGGSTATUS_STOPPED;
   return m_StreamedBuffer->Stop();
}
//**********************************************************************************************************
//Pozastavi prehravani
//**********************************************************************************************************

HRESULT COggFile::Pause()
{
   m_bStatus = OGGSTATUS_PAUSED;
   return m_StreamedBuffer->Stop();
}

To je zbytek souboru OggFile.cpp. Jde už jen o jednoduché funkce. Ve funkci Close je zavřen zdrojový soubor pomocí funkce fclose (je volána vnitřně metodou ov_clear). Poté je volána funkce ov_clear která uvolní paměť související se strukturou OggVorbis_File a související zdroje použíté pro dekódování. Tuto funkci je nutné volat při ukončení práce s ogg soubory, aby po nás nezůstávalo smetí. Nestačí jen aplikovat na strukturu OggVorbis_File funkci jako ClearMmeory nebo memset, to by bylo jen nulování struktury a nebyly by uvolněny zdroje které si pro dekódování a práci alokoval samotný kodek ogg, respektive knihovna vorbisfile.dll. Další funkce ConvertOggToWav je vlastně jen spojením celého dekódovacího procesu do jedné funkce se zápisem do souboru. Myslím že její funkce by měla být jasná. Zbylé funkce jen nastavují členské proměnné třídy COggFile, hlavně tedy stav na příslušné hodnoty a vracejí hodnotu voláním funkcí zvukového bufferu (CSoundBuffer). Máme za sebou třídu COggFile. Chce to zaexperimentovat s metodami, nejlépe si je také krokovat a podívat se na obsahy různých proměnných a vůbec jak to funguje v praxi :)

Obsah