SDL: Hry nejen pro Linux (18)

30. 6. 2005
Doba čtení: 8 minut

Sdílet

V tomto dílu konverzemi zvuků dokončíme popis funkcí, které SDL poskytuje pro audio. Druhá část článku bude věnována rozšiřující knihovně SDL_sound, která slouží pro dekódování zvuků z .MP3, .MID, .OGG a dalších běžně rozšířených formátů.

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_BuildAudi­oCVT(), 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_Available­Decoders(), 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_SAMPLEF­LAG_EOF) nebo o nějakou chybu (SOUND_SAMPLEF­LAG_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_SetBuffer­Size(). Pro hodnotu nové velikosti platí stejné zásady jako u Sound_NewSam­ple().

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_SAMPLEF­LAG_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_SAMPLEF­LAG_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.)

bitcoin_skoleni

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

Pokračování

Dnes už na původně slibovanou knihovnu SDL_mixer nezbylo místo, takže příště…

Autor článku

Backend programátor ve společnosti Avast, kde vyvíjí a spravuje BigData systém pro příjem a analýzu událostí odesílaných z klientských aplikací založený na Apache Kafka a Apache Hadoop.