Audio
Ein Spiel ohne Geräusche kann auf Dauer keinen Spaß machen. Man muss ja nicht gleich einen hitverdächtigen Song hinzufügen, aber einige kleine Soundeffekte können nicht schaden. Das Thema Sound & Audio wird in der Spieleprogrammierung recht selten behandelt. Ein Grund dafür ist, dass dieses Thema schon einiges an Grundverständnis über Musik & Computer abverlangt. So dürfte auch klar sein, dass diese Einführung hier nicht mehr ist, als eine kurze oberflächliche Einführung, wie Sie den einen oder anderen Ton zu Ihrer Anwendung hinzufügen können. Den meisten unter Ihnen dürfte das aber in der Regel ausreichen (dem Autor auf jeden Fall, da dieser sich als außerordentlich unmusikalisch einstuft). Daher orientiert sich der Autor ausnahmsweise mal mehr oder weniger an der Dokumentation von SDL, in dem Vertrauen, dass die Autoren dabei schon wissen, wovon Sie reden.
Hinweis
Da hier, abgesehen von der Funktion SDL_OpenAudio(), die Funktionen nicht genauer erklärt werden, sei hierzu die Dokumentation bzw. Manual-Page für die Syntaxbeschreibung und den einzelnen Parametern empfohlen.
Vereinfacht: Musik auf dem Computer ist eine Übersetzung von Wellen, die Sie in einer Serie von Werten hören, wovon jeder Wert eine Schwingungsweite der Welle entspricht. Werden diese Werte in einen Stream der Soundkarte geschickt, kann eine Annäherung an den Orginalwellen wieder erzeugt werden.
SDL unterstützt hierbei das Audio-Format 8 und 16 Bit mit einer Frequenz zwischen 11025 Hz und 44100 Hz. Diese Werte sind abhängig von der Hardware. Sofern die Hardware das Audioformat oder die Frequenz nicht unterstützt, kann diese auch emuliert werden. Gewöhnlich unterstützt aber jede gängige Hardware das Audio-Format 16 Bit und eine Frequenz von 22050 Hz.
Um etwas auf dem Lautsprecher auszugeben, müssen Sie zuerst mal das Audiogerät öffnen. Dies erledigen Sie mit der Funktion:
int SDL_OpenAudio( SDL_AudioSpec *desired,
SDL_AudioSpec *obtained);
Als Parameter müssen Sie hierbei mindestens die Angaben für desired vom Typ SDL_AudioSpec machen. SDL_AudioSpec ist eine Struktur, die wie folgt definiert ist:
typedef struct {
int freq;
Uint16 format;
Uint8 channels;
Uint8 silence;
Uint16 samples;
Uint32 size;
void (*callback)(void *userdata, Uint8 *stream, int len);
void *userdata;
} SDL_AudioSpec;
Um also ein Audiogerät zu öffnen, müssen Sie diese Struktur erst mal mit entsprechenden Werten füllen. Mit freq geben Sie die Anzahl der Samples an, die an das Soundgerät pro Sekunde gesendet werden. Je höher dieser Wert ist, umso besser. Gewöhnliche Werte sind hierbei 11025 Hz, 22050 Hz und 44100 Hz. Mit format geben Sie die Größe in Bits und den Typ des Samples an. Mögliche Werte und deren Bedeutung sind hierbei:
| Parameter format | Bedeutung |
| AUDIO_U8 | Unsigned 8 –Bit Samples |
| AUDIO_S8 | Signed 8 Bit Samples |
| AUDIO_U16 AUDIO_U16LSB | Unsigned 16 Bit little-endian Samples |
| AUDIO_S16 AUDIO_S16LSB | Signed 16 Bit little-endian Samples |
| AUDIO_U16MSB | Unsigned 16 Bit big-endian Samples |
| AUDIO_S16MSB | Signed 16 Bit big-endian Samples |
| AUDIO_U16SYS | Entweder AUDIO_U16LSB oder AUDIO_U16MSB; abhängig von der Anordnung der Byte-Reihenfolge des Systems (little- oder big-endian) |
| AUDIO_S16SYS | Entweder AUDIO_S16LSB oder AUDIO_S16MSB; abhängig von der Anordnung der Byte-Reihenfolge des Systems (little- oder big-endian) |
Mit channels geben Sie die Anzahl der Sound-Kanäle an. 1 steht hierbei für Mono (Single Channel) und 2 für Stereo (Dual Channel). silence ist ein Wert für einen Audio-Puffer, der berechnet wird.
Mit samples geben Sie die gewünschte Größe des Audio-Puffers eines Samples an. Dieser Wert sollte immer ein typischer Wert hoch 2 sein. Gewöhnliche Werte variieren zwischen 512 und 8192, was aber auch abhängig von der Geschwindigkeit der Anwendung und der CPU ist. Kleinere Werte bewirken eine schnellere Antwortzeit, können allerdings auch bewirken, dass der Puffer nicht in der geforderten Zeit gefüllt wird, wenn die Anwendung bspw. umfangreiche Berechnungen ausführt. size ist wieder eine Audio-Puffergröße in Bytes, welche berechnet wird. callback benötigen Sie, um die Adresse einer Callback-Funktion zu übergeben. Diese müssen Sie schreiben, da hiermit die Audio-Daten gemischt und in den Audio-Stream geschrieben werden. Erst danach kann das Audio-Format und die Abtastrate ausgewählt und das Audio-Gerät geöffnet werden. userdata ist ein Zeiger auf den ersten Parameter der Callback-Funktion.
Bevor Sie jetzt den Sound abspielen können, muss zuvor noch die Funktion SDL_PauseAudio(0) aufgerufen werden. Dies ist nötig, da sonst sofort die Callback-Funktion angesprungen wird, bevor noch irgendetwas initialisiert werden konnte. Damit können Sie sicher die Callback-Funktion mit Daten initialisieren, nachdem ein Audiogerät geöffnet wurde. Jetzt, wo Sie ein Audiogerät geöffnet haben, benötigen Sie als Nächstes die Funktion SDL_LoadWAV(), um die Audio-Daten zu laden. Dann müssen Sie eine Struktur vom Typ SDL_AudioCVT mit der Funktion SDL_Build_AudioCVT() mit Werten initialisieren, um eine Konvertierung durchzuführen. Dabei handelt es sich um die Werte des Quell- und Zielformats für die anschießende Konvertierung. Ebenfalls werden die Werte über die Anzahl der Kanäle (1 = Mono, 2 = Stereo) und die Frequenz (Samples pro Sekunde) jeweils vom Quell- und Zielformat benötigt. Die so mit Werten bestückte Struktur SDL_AudioCVT kann anschließend als Parameter für die Funktion SDL_ConvertAudio verwendet werden, um die Audio-Daten in das gewünschte Format zu konvertieren.
Anschließend sollten Sie mit SDL_LockAudio() die Callback-Funktion sperren, um so Initialisierungen der Daten, die Sie von der Konvertierung mit SDL_ConvertAudio() erhalten haben, durchzuführen. Nachdem Sie die Callback-Funktion mit SDL_UnlockAudio() wieder freigegeben haben, sollte die von Ihnen zur Verfügung gestellte Mixer-Funktion (Callback-Funktion) den Sound ausgeben. Am Ende sollten Sie das Audio-Gerät mit SDL_CloseAudio() wieder freigeben.
Hinweis
Ich denke, es versteht sich fast schon von selbst, dass Sie beim SDL_Init() das Flag SDL_INIT_AUDIO mit angeben müssen.
