┌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