Vφtßm vÜechny programßtorskΘ nadÜence v kurzu jeho₧ tΘmatem a ·Φelem je dßt do hromady n∞kolik t∞ch °ßdk∙
k≤du v C++ je₧ nßm zajistφ mo₧nost p°ehrßvßnφ zvukov²ch soubor∙ ogg.
Nebudu se tady zab²vat historiφ
ogg formßtu ani nebudu vysv∞tlovat jak funguje komprese v n∞m pou₧itß. Za prvΘ proto, ₧e to nenφ pro naÜe ·Φely v∙bec pot°eba, a za druhΘ proto,
₧e jsem to sßm nenastudoval :)Abychom mohli v∙bec na ogg pom²Ület, budeme pot°ebovat Ogg SDK, kterΘ najdete na internetu nebo na CHIP Cd.
KonkrΘtn∞ budeme pou₧φvat knihovnu vorbisfile, kterß poskytuje funkce pro snadnou prßci s ogg soubory. SDK obsahuje (m∙₧eme takΘ °φct ₧e souΦßstφ ogg kodeku jsou) jeÜt∞ knihovny vorbisenc (slou₧φ pro k≤dovßnφ do ogg)
,ogg a vorbis. Poslednφ dv∞ nebudeme probφrat. Jednak proto, ₧e obsahujφ funkce pro prßci s ogg soubory na nejni₧Üφ ·rovni a knihovny
vorbisfile a vorbisenc je pou₧φvajφ a nabφzejφ nßm funkce podstatn∞ jednoduÜÜφ. Dßle takΘ proto ₧e programovßnφ s pomocφ t∞chto knihoven nemßm takΘ zrovna v malφΦku :).
To m∞ p°ivßdφ na poznßmku, ₧e jakΘkoliv poznatky, zkuÜenosti a p°ipomφnky jsou vφtßny. NauΦφme se tedy dek≤dovat ogg soubor pomocφ vorbisfile, od toho jsme
jen kr∙Φek k p°evodu na wav soubor. Podporu pro p°ehrßvßnφ ogg soubor∙ budeme implementovat jako t°φdu, pozd∞ji jako rozhranφ,
a ud∞lßme tΘ₧ jak²si mana₧er ogg soubor∙. Co se t²kß tohoto kurzu, uzp·sobφm implementaci t°φdy a v∞tÜφ Φßsti projektu tak, aby bylo mo₧nΘ ji zahrnout do enginu kter²
je probφrßn v serißlu o DirectX. Ostatn∞ C++ jsem se prakticky nauΦil dφky Chipu.
Mßte oproti m∞ tu v²hodu, ₧e vßm tu vysv∞tlφm, jak bude nßÜ projekt pracovat a na vßs u₧ je jen si opsat uve°ejn∞n² k≤d, nebo
napsat vlastnφ. Nevφm jak ostatnφm, ale m∞ se pro uΦenφ nov²ch v∞cφ nejvφc osv∞dΦila metoda opisovßnφ k≤du (nap°φklad p°i uΦenφ DirectX). Nßsleduje editace k≤du.
Tak takΘ v∞tÜinou zjistφm co se mi na implementaci nelφbφ, uzp∙sobφm si k≤d k obrazu svΘmu Φi dod∞lßm v∞ci co mi chybφ. Jakmile danou problematiku aspo≥ z Φßsti zvlßdnu, pustφm se
do dostupnΘ dokumentace a odpoutßvßm se od serißlu, p°esn∞ji od ukßzkovΘho k≤du a pφÜu vlastnφ k≤d. Tφm se uΦφm nejvφc.
Samoz°ejm∞ to nejde u ka₧dΘho tΘmatu. P°i hledßnφ zp∙sobu jak p°ehrßvat ogg jsem takΘ opisoval. Dokumentace k SDK, aspo≥ tedy m∞, se zdß
nep°φliÜ podrobnß a pro Φlov∞ka kter² o problΘmu nic nevφ takΘ moc vst°φcnß nenφ. Te∩ kdy₧ do nφ nakouknu, je mnohem p°ßtelÜt∞jÜφ a nechßpu co mi p°edtφm d∞lalo takovΘ problΘmy.
Ka₧dpopßdn∞ jsem stßhl z internetu program, kter² p°ehrßval ogg, docela jednoduch², dobr² k uΦenφ, opsal jsem ho a pochopil jak funguje. Fungoval bez chyby, reproduktory vyluzovaly t≤ny p°esn∞ podle oΦekßvßnφ, ale vadila mi na n∞m jedna maliΦkost.
Jeliko₧ knihovny kodeku Ogg neposkytujφ ₧ßdnou funkci typu Hraj, musφte sami zajistit samotnΘ p°ehrßvßnφ, nejlΘpe pomocφ DirectSound. To byl p°φpad ukßzkovΘho programu a je to i p°φpad nßÜ. Program
inicializoval tuto souΦßst DirectX, vytvo°il zvukov² buffer (sound buffer) pro ogg soubor, cel² soubor do n∞j dek≤doval a spustil p°ehrßvßnφ vytvo°enΘho zvukovΘho bufferu.
Kdy₧ ale pou₧ijete soubor o dΘlce t°eba 3 minuty, vzorkovßnφm 44100Hz, Stereo, 16bit∙ na vzorek, bude se muset vaÜe pam∞t p°ipravit na cca (44100Hz = 44100 vzork∙ za sekundu, jeden vzorek mß 2 bajty (tj 16bit∙))
44100 vzork∙/sekundu * 180 sekund * 2 kanßly * 2 bajty = 31752000 bajt∙ = 31007,9kB = 30,3MB. Pokud bychom pou₧ili zmφn∞n² postup t°eba ve h°e a pot°ebovali p°ehrßvat n∞jak²
seznam hudebnφch soubor∙ (playlist), cht∞lo by to asi naΦφst vφce soubor∙ najednou. Za to by vßs operaΦnφ pam∞t asi rßda nem∞la. Nehled∞ na to ₧e dek≤dovßnφ celΘho souboru do takovΘho zvukovΘho bufferu nenφ zßle₧itostφ
jednoho cyklu procesoru a navφc je to velmi nefektivnφ,co₧ to by naÜe sv∞domφ programßtora neustßlo. Co₧ ale nic nem∞nφ na tom, ₧e zmφn∞n² p°φklad je v²born²m odrazov²m m∙stkem a jß jsem jeho autorovi za n∞j vd∞Φn². Bez n∞j bych se mo₧nß ani nedostal dßl.
Hledal jsem tedy dßl na internetu zda najdu n∞jak² elegantn∞ji vy°eÜen² p°φklad, ze kterΘho bych mohl Φerpat, ale marn∞, nebo Üpatn∞. ╪φkßte, ₧e by bylo nejlepÜφ
dek≤dovat v₧dy jen n∞jakou Φßst souboru a tu potom p°ehrßt? Sprßvn∞, to bude lepÜφ °eÜenφ, naÜφ pam∞ti se bude daleko vφc zamlouvat. A kdy₧ si projdete sprßvnΘ p°φklady a Φßsti dokumentace z
SDK k DirectX t²kajφcφ se DirectSound, p°ijdete jist∞ takΘ jako jß na zp∙sob, jak tento plßn realizovat.
KlφΦov²m slovem dne se pro nßs tedy stßvß streamovan² zvukov² buffer (streaming sound buffer). Ono vlastn∞ DirectSound nic takovΘho neposkytuje, jde jen o techniku pou₧φvßnφ zvukovΘho bufferu, kterΘmu se pak °φkß
₧e je streamovan². SamotnΘ DirectSound nßm nabφdne jen zvukov² buffer.
Vyto°φme si pro hudebnφ soubor zvukov² buffer do kterΘho se nßm bude vejφt jen krßtk² ·sek zßznamu, °ekn∞me nap°φklad pro 3s, 5s, to je jedno. Hodnota by ale nem∞la b²t p°φliÜ malß ani p°φliÜ vysokß. Pak spustφme p°ehrßvßnφ tohoto bufferu p°iΦem₧ budeme tak nßroΦnφ, ₧e
mu p°ikß₧eme, aby poka₧dΘ, kdy₧ se p°ehraje do konce, se vrßtφl zp∞t na zaΦßtek a hrßl znovu a znovu a po°ßd dokud ho nezastavφme. Jeho obsah budeme tedy moci teoreticky poslouchat donekoneΦna, prakticky pak o n∞co kratÜφ dobu (nebo snad u₧ n∞kdo mß nßpoj nesmrtelnosti?).
Jß jsem zvolil 3s. Vy co jste si v t∞ch p°φkladech k DirectSound z SDK DirectX vÜimli, ₧e tam pou₧ili takΘ 3s, mlΦte! Nßsledn∞ musφme nahrßt, respektive dek≤dovat do zvukovΘho bufferu co se do
n∞j vejde, 3s. Co potom? Buffer je p°ehrßvßn po°ßd dokola. Abychom neposlouchali po°ßd to samΘ, budeme muset data v bufferu m∞nit, tj starß data co u₧ byla p°ehrßna nahradit nov²mi.
Pokud to nenφ zcela jasnΘ, ud∞lßm v tom v∞tÜφ zmatek jeÜt∞ zavedenφm pojmu hracφ a zapisovacφ (play and write) kursor (cursor).
Pomocφ hracφho kurzoru m∙₧eme zjistit, jak² bajt bufferu je prßv∞ p°ehrßvßn, tj. kolik bajt∙ od zaΦßtku bufferu u₧ bylo p°ehrßno. Zapisovacφ kurzor udßvß pozici na kterou m∙₧eme zapsat novß data.
P°ehrßt cel² buffer nov²mi daty v₧dy kdy₧ hracφ kurzor dorazφ na konec bufferu je takΘ nevhodnΘ, jednak by to program nestihl a na zaΦßtku by urΦit∞ vznikla n∞jakß dφra, tj Φßst zvuku bychom nikdy neslyÜeli a takΘ by mohlo dojφt
k p°ek°φ₧enφ hracφho a zapisovacφho kurzoru. Tyto kurzory nikdy nesmφ vzßjemn∞ ukazovat na stejnΘ mφsto. To znamenß, ₧e pokud bude pozice hracφho kurzoru n, m∙₧eme zapisovat data p°ed pozici n do n-1 a od
n+1 (co₧ je taky dost o vyjφmku) do konce bufferu, ale nikdy ne na n. P°esn∞ °eΦeno, on zapisovacφ kurzor m∙₧e ukazovat na stejnΘ mφsto jako hracφ kurzor, ale nikdy nesmφte na pozici n zaΦφt zapisovat v okam₧iku kdy tam ukazuje i hracφ kurzor, tj. buffer prßv∞ p°ehrßvß dan² bajt! Rozd∞lφme si tedy zvukov² buffer na n∞kolik Φßstφ, lΘpe °eΦeno, definujeme si po kolika bajtech budeme buffer obnovovat, °φkejme tomu dΘlka. Tu si stanovφme nap°φklad tak, aby se veÜla do bufferu nap°φklad 16x.
Pokud se ve sv∞t∞ poΦφtaΦ∙ pohybujete delÜφ dobu, jist∞ vφte ₧e je jak²msi zp∙sobem zam∞°en² spφÜe na sudß Φφsla, konkrΘtn∞ na mocniny 2. Pokud se nepohybujete, vφte to te∩ i bez toho b∞hßnφ :)
B∞hem p°ehrßvßnφ bufferu budeme hlφdat vzdßlenost mezi hracφm kurzorem a zapisovacφm kurzorem (poslednφ pozice kam jsme zapsali data) a v₧dy kdy₧ p°ekroΦφ naÜi definovanou dΘlku, nahradφme data v bufferu kterß zaΦφnajφ na pozici
zapisovacφho kurzoru a konΦφ na pozici hracφho kurzoru - 1.
Jak se dozvφme ₧e vzdßlenost kurzor∙ u₧ je dostateΦn∞ velkß a ₧e m∙₧eme bezpeΦn∞ obnovit data v bufferu bez nebezpeΦφ p°ek°φ₧enφ kurzor∙? To m∙₧eme ud∞la dv∞ma zp∙soby. Jednak se m∙₧eme
bufferu ptßt v ka₧dΘm cyklu aplikace kde se nachßzφ hracφ kurzor a na zßklad∞ toho se rozhodnout zda obnovit data Φi ne, nebo si m∙₧eme bufferu °φct, a¥ nßs upozornφ pomocφ zprßvy
windows ₧e byla p°ehrßna urΦitß dΘlka bufferu, urΦit² poΦet bajt∙. Vybral jsem si prvnφ zp∙sob, kter² se podle m∞ vφce hodφ pro pou₧itφ v hernφm enginu, kde pot°ebujeme p°ehrßvat vφce zvuk∙ najednou,
namφsto druhΘho zp∙sobu, kter² se hodφ vφce pro p°ehrßvaΦ, kter² p°ehrßvß jeden zvuk v jeden Φas a nemusφ se starat, kter² buffer o sob∞ dal vlastn∞ v∞d∞t.
Cel² postup demonstruje nßsledujφcφ animace (k posuvu pou₧φvejte Üipku vpravo dole):
Jedna z poslednφch v∞cφ kterou je snad t°eba zmφnit je, jak se budeme chovat u konce bufferu. Situace: Zapisovacφ kurzor je 2 dΘlky p°ed koncem bufferu. ╪ekn∞me ₧e CPU poΦφtaΦe se zrovna n∞kde zdr₧φ a a₧ se dostane k testu na dΘlku vzdßlenosti mezi kurzory, bude vzdßlenost
p°ekroΦena t°eba 1.5 krßt. My ale nahrßvßme data od poslednφ pozice zapisovacφho kurzoru do pozice hracφho kurzoru - 1. Nastavφme novou pozici zapisovacφho kurzoru na pozici hracφho kurzoru. Te∩ u₧ nßm p°ed koncem bufferu zbyla jen p∙lka definovanΘ dΘlky. Nevadφ, budeme poΦφtat tuto polovinu.
Buffer se vrßtφ na zaΦßtek a pokraΦuje v p°ehrßvßnφ. My poΦφtßme bajty dßl a p°iΦφtßme k tomu co zbylo na konci. A₧ bude vzdßlenost dostateΦnß, nahrajeme polovinu dat na konec bufferu, p°esn∞ do mezery kterß nßm tam zbyla a zbytek na zaΦßtek bufferu. Proto₧e buffer by se m∞l obnovovat 16x (zßvisφ na definici dΘlky) za jeden pr∙chod, nem∙₧e
se stßt ₧e bychom nestaΦili nahrßt na konec bufferu data vΦas ne₧ tam dorazφ hracφ kurzor a ₧e bychom slyÜeli jeÜt∞ t≤ny kterΘ tam byly p°i minulΘm pr∙chodu.
K vlastnφmu p°ehrßvßnφ zvukov²ch dat pou₧ijeme zvukov² buffer (sound buffer), kter² je souΦßstφ DirectX.
Definujeme si dΘlku, kterß rozd∞lφ buffer na n∞kolik menÜφch Φßstφ
Budeme hlφdat vzdßlenost mezi hracφm a zapisovacφm kurzorem a pokud p°ekroΦφ definovanou dΘlku, nahrajeme novß data
do bufferu od pozice zapisovacφho kurzoru do pozice hracφho kurzoru - 1;
Pokud nßm na konci bufferu nezbyde mφsto velikosti dΘlka kam bychom nahrßli data, budeme poΦφtat vzdßlenost jeÜt∞ p°i
p°ehrßvßnφ bufferu znovu od zaΦßtku a a₧ bude vzdßlenost p°ekroΦena nahrajeme data na konec bufferu a zbytek na zaΦßtek bufferu
op∞t do pozice hracφho kurzoru - 1
Te∩ jeÜt∞ n∞co na zp∙sob v²vojovΘho diagramu (i kdy₧ ho tak nenßvidφm :)):
Mßte pravdu pokud si °φkßte ₧e program na v²vojovΘm diagramu nikdy neskonΦφ, Ülo mi jen o nakreslenφ zp∙sobu obnovy zvukovΘho bufferu. O rozpoznßvßnφ
konce ogg souboru, p°ehrßnφ celΘho souboru se budeme podrobn∞ v∞novat a₧ pozd∞ji.
Doufßm ₧e se mi poda°ilo srozumiteln∞ vysv∞tlit jak bude program pracovat, v Φßsti 1 zaΦneme s implementacφ zvukovΘho bufferu do kterΘho budeme pozd∞ji dek≤dovat ogg soubor.
K opravdovΘ prßci s funkcemi ogg se dostaneme ve druhΘ Φßsti, ale v∞tÜinou to tak b²vß, ₧e Φlov∞k musφ ud∞lat spoustu nezß₧ivnΘ prßce ne₧ dosp∞je do stßdia kdy d∞lß n∞co novΘho a
zajφmavΘho. V p°φpad∞ problΘm∙, nedorozum∞nφ, nßmitk∙, post°eh∙ a podobn²ch dodatk∙ m∞ kontaktujte na e-mailu Φi na icq.