Přehrávání hudby ve formátu Ogg Vorbis
Z CHWiki
Obsah |
[editovat] Úvod
Hudba je důležitou součástí každé hry protože hodně pomáhá s tvorbou atmosféry a hra, která je tichá, působí tak trochu ochuzeně. Proto se v tomto článku podíváme, jak naprogramovat přehrávání hudby ve formátu Ogg Vorbis.
Vorbis je opensource algoritmus pro kompresi hudby, principem podobný formátu MP3. Většinou dosahuje ještě lepších výsledků v poměru kvalita/velikost než má MP3. Ogg je formát souboru, do kterého se takto komprimovaná hudba obvykle ukládá. S hudbou v tomto formátu bez problému pracují všechny softwarové přehrávače a i některé kapesní. Domovská stránka projektu je na adrese http://xiph.org/vorbis/ . Hudba v tomto formátu je například ve hrách Unreal Tournament 2004 nebo UFO: Extraterrestrials.
http://xiph.org/images/logos/fish_xiph_org.png
[editovat] Instalace knihoven
Abychom mohli Ogg Vorbis použít, stáhneme si ze stránky http://xiph.org/downloads/ zdrojové kódy knihoven libogg a libvorbis. Obě tyto knihovny mají licenci ve stylu BSD, takže není problém je použít i v komerčních aplikacích. Obě knihovny si přidáme do projektu, nastavíme cesty pro hlavičkové soubory a knihovny a zkompilujeme.
[editovat] Detailní návod pro Visual Studio 2005
Zdrojové kódy knihoven rozbalíme tak, aby adresářová struktura vypadala takto:
+ adresář projektu
+- libvorbis
+- doc
+- include
+- ...
+- libogg
+- doc
+- include
+- ...
Do solution si přidáme tyto projektové soubory:
- libogg\win32\VS2003\libogg\libogg.vcproj
- libvorbis\win32\VS2005\libvorbis\libvorbis.vcproj
- libvorbis\win32\VS2005\libvorbisfile\libvorbisfile.vcproj
U všech nastavíme General\Output directory na $(SolutionDir)$(ConfigurationName) a General\Intermediate Directory na $(ConfigurationName) abychom měli všechny soubory pohromadě. Všechny projekty nyní zkompilujeme, musíme ale dávat pozor na to, že libvorbis vyžaduje libogg a libvorbisfile obě zbývající knihovny. Výchozí nastavení je na vytvoření dynamických knihoven, takže musíme přilinkovat vytvořené .lib soubory.
Nakonec přidáme do našeho projektu cesty k adresářům s hlavičkovými soubory a nastavíme přilinkování .lib souborů. Dále ověříme, že .dll soubory se zapisují do adresáře se spustitelným souborem našeho projektu.
Dále bude možná nutné do souboru libvorbisfile.def dopsat ov_fopen, nějak na tuto funkci vývojáři zapomněli.
[editovat] Použití
S pomocí API vorbisfile je přehrávání souborů Ogg Vorbis velmi snadné. Soubor můžeme otevřít pomocí některé z následujících funkcí:
- ov_fopen otevře soubor na disku.
- ov_open načte soubor otevřený pomocí standardní C knihovny (pomocí fopen)
- ov_open_callbacks načte soubor pomocí funkcí, které sami dodáme. To se využije, pokud máme vlastní balíkovací systém a soubory nečteme přímo.
ov_open má následující parametry. Pamatujte, že kvůli problémům s kompatibilitou s CRT pod Windows není doporučeno na Windows tuto funkci používat.
- FILE *f
- Identifikátor otevřeného souboru, který jsme získali voláním fopen.
- OggVorbis_File *vf
- Ukazatel na strukturu OggVorbis_File, kterou funkce naplní a kterou budeme používat později při čtení.
- char *initial
- Pokud už jsme nějaká data ze souboru přečetli a nemůžeme se vrátit zpátky, předáme v tomto parametru ukazatel na buffer, kde ta data jsou. Tento parametr typicky nepoužíváme.
- long ibytes
- Velikost přečtených dat v initial. Používá se jen v kombinaci s initial.
Dále máme funkci ov_open_callbacks, která otevře soubor pomocí našich vlastních čtecích funkcí. To využijeme, pokud soubory čteme z balíku. Jeji parametry jsou:
- void *datasource
- Tento parametr funkce předá naším funkcím, typicky obsahuje identifikátor zdroje dat.
- OggVorbis_File *vf
- Ukazatel na strukturu OggVorbis_File, kterou funkce naplní a kterou budeme používat později při čtení.
- char *initial
- Stejný význam jako u předchozí funkce.
- long ibytes
- Stejný význam jako u předchozí funkce.
- ov_callbacks callbacks
- Struktura obsahující ukazatele na naše funkce.
Definice ov_callbacks vypadá takto:
typedef struct {
size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource);
int (*seek_func) (void *datasource, ogg_int64_t offset, int whence);
int (*close_func) (void *datasource);
long (*tell_func) (void *datasource);
} ov_callbacks;
Významy parametrů jsou stejné jako u funkcí ftell, fread apod. z CRT. Pokud zdroj dat nepodporuje libovolné přesouvání čtecí pozice, můžeme nastavit seek_func a tell_func na NULL. To pro běžné hraní stačí.
Všechny funkce nám předají ukazatel na strukturu OggVorbis_File, která identifikuje náš otevřený soubor. Tuto strukturu předáváme
dalším funkcím.
Pro jednoduché přehrávání nás bude zajímat prakticky jen funkce ov_read. Její parametry jsou
- OggVorbis_File *vf
- Identifikátor otevřeného souboru.
- char *buffer
- Ukazatel na buffer v paměti, který jsme naalokovali a kam dostaneme dekódovaná zvuková data.
- int length
- Velikost bufferu, kolik dat maximálně chceme. Typické velikosti jsou kolem 4096 bajtů.
- int bigendianp
- Formát dat, zda chceme Little Endian (nastavíme na 0) nebo Big Endian (nastavíme na 1). Typicky použijeme hodnotu 0.
- int word
- Nastavte na 2 pro 16bitové vzorkování nebo na 1 pro 8bitové. Typicky se používá 16 bitů.
- int sgned
- Nastavte na 1 pro formát dat se znaménkem (kladné/záporné) nebo na 0 pro data bez znaménka. Typicky se používá 1.
- int *bitstream
- Ukazatel na proměnnou, kam dostaneme číslo aktuální logické sekce souboru.
Funkce vrací hodnotu typu int, která může mít následující hodnoty:
- OV_HOLE
- Problém v dekódování, který není kritický.
- OV_EBADLINK nebo OV_EINVAL
- Špatné parametry nebo problém při čtení.
- 0
- Došlo se na konec souboru, dekódování končí.
- kladné číslo
- Kolik bajtů dekódovaného audia jsme dostali do bufferu. Toto číslo bude typicky menší než velikost bufferu předaná v length.
Tuto funkci budeme pravidelně volat, abychom mohli kontinuálně předávat data zvukovému API. Ve většině případů funguje zvukové API tak, že nás o data samo požádá, neboli zavolá naši funkci, kterou jsme pro tento účel vyhradili.
[editovat] Příklad pro přehrávání souboru pomocí SDL_Audio
Zvukových API existuje celá řada. SDL je široce používané, proto jsem je vybral. Příklad je velmi jednoduchý, neřeší přehrávání více zvuků zaráz (na to by se použil SDL_mixer). Také používá globální proměnné, což je fuj ;).
#include <vorbis\codec.h>
#include <vorbis\vorbisfile.h>
#include <SDL.h>
const int SDL_BUFFER_SIZE = 4*1024;
SDL_AudioSpec *obtained = NULL;
OggVorbis_File vorbis_file;
// Tuto funkci vola SDL_Audio z druheho vlakna, aby nacetla dalsi kus dat k prehravani.
void audio_callback(void *userdata, Uint8 *stream, int len) {
int current_section;
int read_bytes = 0;
int total_read_bytes = 0;
// Nacteme 'len' dat z vorbisu do 'stream'. Musime to udelat v cyklu, protoze ov_read nemusi nutne
// nacist vsechno najednou.
while (total_read_bytes < len) {
// Pouziti reinterpret_cast je zde nutne kvuli tomu, ze SDL a vorbis pouzivaji ruzne typy pro buffer v pameti.
// Nicmene oba jsou osmibitove a znamenko si obe strany hlidaji, takze to funguje.
read_bytes = ov_read(
&vorbis_file,
reinterpret_cast<char*>(stream) + total_read_bytes,
len - total_read_bytes,
0,
2,
1,
¤t_section
);
if (read_bytes < 0) // pokud se vyskytl nejaky problem, nepokracujeme v plneni
break;
total_read_bytes += read_bytes;
}
}
void init_sdl_audio() {
// Nastavime SDL_Audio na stejny format zvukovych dat jako dostavame z libvorbis.
SDL_AudioSpec *desired = new SDL_AudioSpec();
desired->callback = audio_callback;
desired->freq = 44100;
desired->channels = 2;
desired->format = AUDIO_S16;
desired->padding = 0;
desired->samples = SDL_BUFFER_SIZE;
desired->userdata = NULL;
obtained = new SDL_AudioSpec();
if (SDL_OpenAudio(desired, obtained) < 0) {
printf("Nepodarilo se spustit zvuk.");
exit(-1);
}
delete desired;
}
void close_sdl_audio() {
SDL_CloseAudio();
}
void open_vorbis_music_file(char *file_name) {
ov_fopen(file_name, &vorbis_file);
}
void close_vorbis_music_file() {
ov_clear(&vorbis_file);
}
int _tmain(int argc, _TCHAR* argv[]) {
init_sdl_audio();
open_vorbis_music_file("music.ogg");
SDL_PauseAudio(0);
// Pockame na stisknuti nejake klavesy.
// SDL zatim ve druhem vlakne prehrava hudbu.
_getch();
SDL_PauseAudio(1);
close_vorbis_music_file();
close_sdl_audio();
return 0;
}
