┌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:
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
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
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
|