Konverze zvuků
Transformace zvuků z jednoho formátu na jiný je v SDL dvoustupňový proces. Nejprve je nutné vytvořit objekt struktury SDL_AudioCVT, nastavit ho na správné parametry a nakonec ho předat jako parametr do konverzní funkce.
typedef struct
{
int needed;
Uint16 src_format;
Uint16 dest_format;
double rate_incr;
Uint8 *buf; // Buffer s daty
int len; // Délka originálu
int len_cvt;
int len_mult; // Výpočet délky pro alokaci
double len_ratio; // Výpočet výsledné délky
void (*filters[10])(struct SDL_AudioCVT *cvt, Uint16 format);
int filter_index;
} SDL_AudioCVT;
Většina atributů struktury může být považována za privátní, budeme se proto zabývat jen těmi, které jsou důležité pro používání.
Buf je ukazatelem na zvuk, a to jak zdrojový, tak cílový. Původní data tedy budou konverzí přepsána novými. Druhým důsledkem je, že se data mohou při konverzi zvětšit, a tudíž je nutné alokovat dostatek paměti. Číselně by měla být velká len*len_mult bytů, kde len představuje velikost původních dat a len_mult obsahuje násobitel, kolikrát se mohou maximálně zvětšit, typickým příkladem je konverze 8bitového zvuku na 16bitový.
Len_ratio má podobný význam jako len_mult. Výsledkem násobení len*len_ratio bude po úspěšné konverzi opravdová délka nových dat v bytech.
Předtím, než může být objekt SDL_AudioCVT použit pro konverzi, musí být inicializován informacemi o zdrojovém a cílovém formátu. K tomu slouží funkce SDL_BuildAudioCVT(), jejíž parametry jsou stejné jako u struktury SDL_AudioSpec probrané v minulém dílu. Informace o zdrojovém zvuku jsou dostupné z načítání a u cíle se v naprosté většině případů volí formát hardwaru ze SDL_OpenAudio().
int SDL_BuildAudioCVT(SDL_AudioCVT *cvt,
Uint16 src_format, Uint8 src_channels, int src_rate,
Uint16 dst_format, Uint8 dst_channels, int dst_rate);
Funkce v případě úspěchu vrátí 1 a v případě neúspěchu –1. Byla-li úspěšná, může se do parametru len konverzní struktury přiřadit délka originálních dat, alokovat paměť pro buffer o velikosti len*len_mult bytů a zkopírovat do něj data zvuku. Pro samotnou konverzi se pak zavolá funkce SDL_ConvertAudio(), jejímž jediným parametrem je konverzní struktura. Úspěch označuje vrácená 0 a neúspěch –1.
int SDL_ConvertAudio(SDL_AudioCVT *cvt);
Pokud vše proběhne bez problémů, budou výsledná data uložena v atributu buf struktury a jejich délka bude len*len_ratio bytů.
V prvním ukázkovém programu naleznete obecně použitelnou funkci LoadSound(), která nahraje zvuk ze souboru filename, zkonvertuje ho pomocí právě popsané techniky na libovolný formát a výsledek uloží na adresu svého posledního parametru. Kód není vložen přímo do článku kvůli relativně velké délce.
Knihovna SDL_sound
SDL_sound je knihovna určená pro nahrávání zvuků mnoha populárních formátů a jejich dekódování. Aktuálně podporovanými podle dokumentace jsou
- .WAV (Microsoft WAVfile RIFF data, interně)
- .VOC (Creative Labs' Voice formát, interně)
- .MP3 (MPEG-1 Layer 3, prostřednictvím SMPEG a mpglib)
- .MID (MIDI hudba konvertovaná na Waveform data, interně)
- .MOD (MOD formát, prostřednictvím MikMod a ModPlug)
- .OGG (Ogg formát, prostřednictvím Ogg Vorbis knihoven)
- .SPX (Speex formát, prostřednictvím libspeex)
- .SHN (Shorten formát, interně)
- .RAW (Raw zvuková data v jakémkoli formátu, interně)
- .AU (Sun's Audio formát, interně)
- .AIFF (Audio Interchange formát, interně)
- .FLAC (Lossless audio komprese, prostřednictvím libFLAC)
Knihovna je šířena pod licencí GNU LGPL, nicméně externí dekodéry mohou mít licenci jinou. Asi nejlepší bude, když si přečtete soubor COPYING z kořenového adresáře archivu knihovny a následně jednotlivé licence všech v aplikaci používaných formátů.
Při použití je nutné přidat k parametrům linkeru řetězec -lSDL_sound, který způsobí přilinkování knihovny k programu. Hlavičky všech funkcí jsou umístěny v souboru SDL_sound.h a jejich jména začínají na jednotnou předponu ‚Sound_‘. Pokud nebude v textu uvedeno jinak, bude daná funkce vracet při chybě nulu, jinak nenulovou hodnotu.
Následující funkce jsou podobné svým SDL analogiím. Mělo by stačit uvést, že Sound_Init() by mělo být voláno jako první ze všech Sound_*() funkcí. Naproti tomu Sound_Quit() uvolní všechny systémové prostředky alokované knihovnou SDL_sound a mělo by být umístěno v kódu vždy před SDL_Quit().
int Sound_Init(void); // Inicializace
int Sound_Quit(void); // Deinicializace
const char *Sound_GetError(void); // Vrátí chybový řetězec
void Sound_ClearError(void); // Vynuluje ho
Zjištění, které dekodéry jsou aktuálně dostupné, lze provést funkcí Sound_AvailableDecoders(), která vrací ukazatel na pole struktur s informacemi o dekodérech. Poslední položka je zarážkou a má hodnotu NULL.
const Sound_DecoderInfo **Sound_AvailableDecoders(void);
typedef struct
{
const char **extensions; // Přípona souboru
const char *description; // Popis dekodéru
const char *author; // Autor
const char *url; // URL dekodéru
} Sound_DecoderInfo;
Loading zvuků
Zvukový soubor v libovolném podporovaném formátu se do aplikace dá nahrát jednou ze dvou níže uvedených funkcí. První z nich slouží pro nahrávání ze SDL_RWops (SDL abstrakce nad vstupními daty), parametr ext je pouze nápovědou při hledání vhodného dekodéru. Druhá funkce slouží pro načítání z diskového souboru.
Sound_Sample *Sound_NewSample(SDL_RWops *rw,
const char *ext,
Sound_AudioInfo *desired,
Uint32 bufferSize);
Sound_Sample *Sound_NewSampleFromFile(const char *fname,
Sound_AudioInfo *desired,
Uint32 bufferSize);
Parametrem desired lze určit, do jakého formátu má být zvuk při dekódování zkonvertován. V případě, že nejsou konverze potřeba, může být nastaven na NULL. Všechny tři atributy mají ve struktuře SDL_AudioSpec své analogie, takže k inicializaci stačí pouze tři jednoduchá přiřazení.
typedef struct
{
Uint16 format; // Formát zvuku
Uint8 channels; // 1 - mono, 2 - stereo
Uint32 rate; // Frekvence (vzorky za sekundu)
} Sound_AudioInfo;
Poslední parametr, bufferSize, určuje počáteční velikost čtecího bufferu v bytech. Čím je větší, tím více dekódování může být provedeno v jednom bloku, na druhou stranu bude trvat o něco déle a bude zabráno více zdrojů. Pro různé formáty mohou být vhodné jiné hodnoty, každopádně velikost musí být vždy násobkem velikosti vzorku. Pokud používáte například 16bitové stereo, kde zabírá každý vzorek 2*2 bytů, musí být velikost násobkem čtyř.
Obě funkce vrací ukazatel na objekt struktury Sound_Sample, která je pro SDL_sound důležitá asi stejně jako SDL_Surface pro SDL. Tento objekt ukládá všechny informace o zvukových datech a stavu jejich dekódování. Atributy by měly být považovány za READ-ONLY, pro jejich změny se využívají výhradně API funkce.
typedef struct
{
void *opaque; // Interní použití
const Sound_DecoderInfo *decoder;// Používaný dekodér
Sound_AudioInfo desired; // Formát pro konverze
Sound_AudioInfo actual; // Aktuální formát vzorku
void *buffer; // Buffer dekódovaných dat
Uint32 buffer_size; // Velikost bufferu v bytech
Sound_SampleFlags flags; // Flagy vzorku
} Sound_Sample;
Po skončení práce by se měly všechny používané zdroje uvolnit. Slouží k tomu funkce Sound_FreeSample(), stačí jí předat ukazatel na nahraný zvuk.
void Sound_FreeSample(Sound_Sample *sample);
Dekódování dat
Voláním funkce Sound_Decode() se dekódují ze vzorku v pořadí následující data. Jejich velikost bude většinou sample->buffer_size bytů a budou uložena do sample->buffer. Návratová hodnota dává informaci, kolik bytů bylo skutečně nahráno.
Uint32 Sound_Decode(Sound_Sample *sample);
Pokud nelze nahrát všech sample->buffer_size bytů, informace o důvodu se dají najít v sample->flags. Většinou se jedná o konec streamu (SOUND_SAMPLEFLAG_EOF) nebo o nějakou chybu (SOUND_SAMPLEFLAG_ERROR).
if(sample->flags & SOUND_SAMPLEFLAG_ERROR)
NecoUdelej();
Pro dekódování všech zvukových dat slouží funkce Sound_DecodeAll(), která do sample->buffer dynamicky alokuje potřebnou paměť a uloží do ní výsledná data, sample->buffer_size bude obsahovat jejich velikost. Opět by se měly testovat flagy ze sample->flags.
Uint32 Sound_DecodeAll(Sound_Sample *sample);
Při dekódování celého zvuku najednou si raději dávejte pozor na velikost alokované paměti, sami jistě přijdete na to, jak by to dopadlo u půlhodinové mp3.
Změna velikosti čtecího bufferu se dá uskutečnit funkcí Sound_SetBufferSize(). Pro hodnotu nové velikosti platí stejné zásady jako u Sound_NewSample().
int Sound_SetBufferSize(Sound_Sample *sample, Uint32 new_size);
V případě, že se nedá velikost změnit, bude se pracovat i nadále s původní. Při zkrácení budou data na konci zahozena a při prodloužení bude konec bufferu nedefinovaný do té doby, než se nahrají nová data.
Skoky na nové pozice
Základním skokem je přesun na začátek zvuku, který se vykoná funkcí Sound_Rewind(). Teoreticky by k chybě nemělo nikdy dojít.
int Sound_Rewind(Sound_Sample *sample);
Druhá funkce, Sound_Seek(), umožňuje přesun na libovolné místo definované časem v milisekundách od začátku.
int Sound_Seek(Sound_Sample *sample, Uint32 ms);
Některé dekodéry nemusejí přeskoky vůbec podporovat a některé pouze s určitými soubory, proto byste měli před dotazem otestovat flagy na SOUND_SAMPLEFLAG_CANSEEK. Pokud přesun selže, měl by se zvuk chovat, jako by volání nikdy nebylo provedeno. Při neošetřitelné chybě jsou flagy nastaveny na SOUND_SAMPLEFLAG_ERROR.
Ukázkové programy
Konverze zvuků
Program demonstruje přehrávání více zvuků najednou. Jeden bude hudbou ve smyčce na pozadí a druhý bude spouštěn vždy po stisku mezerníku. Při nahrávání funkcí LoadSound() se zvuky zkonvertují na stejný (hardwarový) formát. (Zdrojový kód se zvýrazněním syntaxe.)
SDL_sound
Program ukazuje nahrávání zvuku ve formátu .AU pomocí knihovny SDL_sound, jeho dekódování a následné přehrávání (opět pro jednoduchost smyčka). Je zde použit .AU, ale naprosto stejným způsobem lze nahrávat zvukové soubory jakýchkoli jiných podporovaných formátů (.MP3,.OGG, atd.), stačí jen změnit jméno souboru. (Zdrojový kód se zvýrazněním syntaxe.)
Download a odkazy
- Domovská stránka knihovny SDL_sound
- Příklad: Konverze zvuků
- Příklad: SDL_sound
- Offline verze článku včetně všech příloh
Pokračování
Dnes už na původně slibovanou knihovnu SDL_mixer nezbylo místo, takže příště…