diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/allatency.c | 59 | ||||
-rw-r--r-- | examples/alloopback.c | 244 | ||||
-rw-r--r-- | examples/alreverb.c | 76 | ||||
-rw-r--r-- | examples/alstream.c | 80 | ||||
-rw-r--r-- | examples/common/alffmpeg.c | 638 | ||||
-rw-r--r-- | examples/common/alffmpeg.h | 66 | ||||
-rw-r--r-- | examples/common/alhelpers.h | 4 | ||||
-rw-r--r-- | examples/common/sdl_sound.c | 164 | ||||
-rw-r--r-- | examples/common/sdl_sound.h | 43 |
9 files changed, 547 insertions, 827 deletions
diff --git a/examples/allatency.c b/examples/allatency.c index 48bf5b51..deb13d3b 100644 --- a/examples/allatency.c +++ b/examples/allatency.c @@ -26,37 +26,30 @@ #include <stdio.h> #include <assert.h> -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#else -#include <unistd.h> -#define Sleep(x) usleep((x)*1000) -#endif #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "common/alhelpers.h" -#include "common/alffmpeg.h" +#include "common/sdl_sound.h" -LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; -LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT; +static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; +static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT; -LPALSOURCEDSOFT alSourcedSOFT; -LPALSOURCE3DSOFT alSource3dSOFT; -LPALSOURCEDVSOFT alSourcedvSOFT; -LPALGETSOURCEDSOFT alGetSourcedSOFT; -LPALGETSOURCE3DSOFT alGetSource3dSOFT; -LPALGETSOURCEDVSOFT alGetSourcedvSOFT; -LPALSOURCEI64SOFT alSourcei64SOFT; -LPALSOURCE3I64SOFT alSource3i64SOFT; -LPALSOURCEI64VSOFT alSourcei64vSOFT; -LPALGETSOURCEI64SOFT alGetSourcei64SOFT; -LPALGETSOURCE3I64SOFT alGetSource3i64SOFT; -LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT; +static LPALSOURCEDSOFT alSourcedSOFT; +static LPALSOURCE3DSOFT alSource3dSOFT; +static LPALSOURCEDVSOFT alSourcedvSOFT; +static LPALGETSOURCEDSOFT alGetSourcedSOFT; +static LPALGETSOURCE3DSOFT alGetSource3dSOFT; +static LPALGETSOURCEDVSOFT alGetSourcedvSOFT; +static LPALSOURCEI64SOFT alSourcei64SOFT; +static LPALSOURCE3I64SOFT alSource3i64SOFT; +static LPALSOURCEI64VSOFT alSourcei64vSOFT; +static LPALGETSOURCEI64SOFT alGetSourcei64SOFT; +static LPALGETSOURCE3I64SOFT alGetSource3i64SOFT; +static LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT; /* LoadBuffer loads the named audio file into an OpenAL buffer object, and * returns the new buffer ID. */ @@ -66,24 +59,22 @@ static ALuint LoadSound(const char *filename) ALuint rate, buffer; size_t datalen; void *data; - FilePtr audiofile; - StreamPtr sound; + FilePtr sound; - /* Open the file and get the first stream from it */ - audiofile = openAVFile(filename); - sound = getAVAudioStream(audiofile, 0); + /* Open the audio file */ + sound = openAudioFile(filename, 1000); if(!sound) { fprintf(stderr, "Could not open audio in %s\n", filename); - closeAVFile(audiofile); + closeAudioFile(sound); return 0; } /* Get the sound format, and figure out the OpenAL format */ - if(getAVAudioInfo(sound, &rate, &channels, &type) != 0) + if(getAudioInfo(sound, &rate, &channels, &type) != 0) { fprintf(stderr, "Error getting audio info for %s\n", filename); - closeAVFile(audiofile); + closeAudioFile(sound); return 0; } @@ -92,16 +83,16 @@ static ALuint LoadSound(const char *filename) { fprintf(stderr, "Unsupported format (%s, %s) for %s\n", ChannelsName(channels), TypeName(type), filename); - closeAVFile(audiofile); + closeAudioFile(sound); return 0; } /* Decode the whole audio stream to a buffer. */ - data = decodeAVAudioStream(sound, &datalen); + data = decodeAudioStream(sound, &datalen); if(!data) { fprintf(stderr, "Failed to read audio from %s\n", filename); - closeAVFile(audiofile); + closeAudioFile(sound); return 0; } @@ -112,7 +103,7 @@ static ALuint LoadSound(const char *filename) alBufferSamplesSOFT(buffer, rate, format, BytesToFrames(datalen, channels, type), channels, type, data); free(data); - closeAVFile(audiofile); + closeAudioFile(sound); /* Check if an error occured, and clean up if so. */ err = alGetError(); diff --git a/examples/alloopback.c b/examples/alloopback.c new file mode 100644 index 00000000..649ef6ef --- /dev/null +++ b/examples/alloopback.c @@ -0,0 +1,244 @@ +/* + * OpenAL Loopback Example + * + * Copyright (c) 2013 by Chris Robinson <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This file contains an example for using the loopback device for custom + * output handling. + */ + +#include <stdio.h> +#include <assert.h> +#include <math.h> + +#include <SDL.h> + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "common/alhelpers.h" + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +typedef struct { + ALCdevice *Device; + ALCcontext *Context; + + ALCsizei FrameSize; +} PlaybackInfo; + +static LPALCLOOPBACKOPENDEVICESOFT alcLoopbackOpenDeviceSOFT; +static LPALCISRENDERFORMATSUPPORTEDSOFT alcIsRenderFormatSupportedSOFT; +static LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT; + + +void SDLCALL RenderSDLSamples(void *userdata, Uint8 *stream, int len) +{ + PlaybackInfo *playback = (PlaybackInfo*)userdata; + alcRenderSamplesSOFT(playback->Device, stream, len/playback->FrameSize); +} + + +/* Creates a one second buffer containing a sine wave, and returns the new + * buffer ID. */ +static ALuint CreateSineWave(void) +{ + ALshort data[44100]; + ALuint buffer; + ALenum err; + ALuint i; + + for(i = 0;i < 44100;i++) + data[i] = (ALshort)(sin(i * 441.0 / 44100.0 * 2.0*M_PI)*32767.0); + + /* Buffer the audio data into a new buffer object. */ + buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, AL_FORMAT_MONO16, data, sizeof(data), 44100); + + /* Check if an error occured, and clean up if so. */ + err = alGetError(); + if(err != AL_NO_ERROR) + { + fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); + if(alIsBuffer(buffer)) + alDeleteBuffers(1, &buffer); + return 0; + } + + return buffer; +} + + +int main() +{ + PlaybackInfo playback = { NULL, NULL, 0 }; + SDL_AudioSpec desired, obtained; + ALuint source, buffer; + ALCint attrs[16]; + ALenum state; + + /* Print out error if extension is missing. */ + if(!alcIsExtensionPresent(NULL, "ALC_SOFT_loopback")) + { + fprintf(stderr, "Error: ALC_SOFT_loopback not supported!\n"); + return 1; + } + + /* Define a macro to help load the function pointers. */ +#define LOAD_PROC(x) ((x) = alcGetProcAddress(NULL, #x)) + LOAD_PROC(alcLoopbackOpenDeviceSOFT); + LOAD_PROC(alcIsRenderFormatSupportedSOFT); + LOAD_PROC(alcRenderSamplesSOFT); +#undef LOAD_PROC + + if(SDL_Init(SDL_INIT_AUDIO) == -1) + { + fprintf(stderr, "Failed to init SDL audio: %s\n", SDL_GetError()); + return 1; + } + + /* Set up SDL audio with our requested format and callback. */ + desired.channels = 2; + desired.format = AUDIO_S16SYS; + desired.freq = 44100; + desired.padding = 0; + desired.samples = 4096; + desired.callback = RenderSDLSamples; + desired.userdata = &playback; + if(SDL_OpenAudio(&desired, &obtained) != 0) + { + SDL_Quit(); + fprintf(stderr, "Failed to open SDL audio: %s\n", SDL_GetError()); + return 1; + } + + /* Set up our OpenAL attributes based on what we got from SDL. */ + attrs[0] = ALC_FORMAT_CHANNELS_SOFT; + if(obtained.channels == 1) + attrs[1] = ALC_MONO_SOFT; + else if(obtained.channels == 2) + attrs[1] = ALC_STEREO_SOFT; + else + { + fprintf(stderr, "Unhandled SDL channel count: %d\n", obtained.channels); + goto error; + } + + attrs[2] = ALC_FORMAT_TYPE_SOFT; + if(obtained.format == AUDIO_U8) + attrs[3] = ALC_UNSIGNED_BYTE_SOFT; + else if(obtained.format == AUDIO_S8) + attrs[3] = ALC_BYTE_SOFT; + else if(obtained.format == AUDIO_U16SYS) + attrs[3] = ALC_UNSIGNED_SHORT_SOFT; + else if(obtained.format == AUDIO_S16SYS) + attrs[3] = ALC_SHORT_SOFT; + else + { + fprintf(stderr, "Unhandled SDL format: 0x%04x\n", obtained.format); + goto error; + } + + attrs[4] = ALC_FREQUENCY; + attrs[5] = obtained.freq; + + attrs[6] = 0; /* end of list */ + + /* Initialize OpenAL loopback device, using our format attributes. */ + playback.Device = alcLoopbackOpenDeviceSOFT(NULL); + if(!playback.Device) + { + fprintf(stderr, "Failed to open loopback device!\n"); + goto error; + } + /* Make sure the format is supported before setting them on the device. */ + if(alcIsRenderFormatSupportedSOFT(playback.Device, attrs[5], attrs[1], attrs[3]) == ALC_FALSE) + { + fprintf(stderr, "Render format not supported: %s, %s, %dhz\n", + ChannelsName(attrs[1]), TypeName(attrs[3]), attrs[5]); + goto error; + } + playback.Context = alcCreateContext(playback.Device, attrs); + if(!playback.Context || alcMakeContextCurrent(playback.Context) == ALC_FALSE) + { + fprintf(stderr, "Failed to set an OpenAL audio context\n"); + goto error; + } + playback.FrameSize = FramesToBytes(1, attrs[1], attrs[3]); + + /* Start SDL playing. Our callback (thus alcRenderSamplesSOFT) will now + * start being called regularly to update the AL playback state. */ + SDL_PauseAudio(0); + + /* Load the sound into a buffer. */ + buffer = CreateSineWave(); + if(!buffer) + { + SDL_CloseAudio(); + alcDestroyContext(playback.Context); + alcCloseDevice(playback.Device); + SDL_Quit(); + return 1; + } + + /* Create the source to play the sound with. */ + source = 0; + alGenSources(1, &source); + alSourcei(source, AL_BUFFER, buffer); + assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source"); + + /* Play the sound until it finishes. */ + alSourcePlay(source); + do { + Sleep(10); + alGetSourcei(source, AL_SOURCE_STATE, &state); + } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); + + /* All done. Delete resources, and close OpenAL. */ + alDeleteSources(1, &source); + alDeleteBuffers(1, &buffer); + + /* Stop SDL playing. */ + SDL_PauseAudio(1); + + /* Close up OpenAL and SDL. */ + SDL_CloseAudio(); + alcDestroyContext(playback.Context); + alcCloseDevice(playback.Device); + SDL_Quit(); + + return 0; + +error: + SDL_CloseAudio(); + if(playback.Context) + alcDestroyContext(playback.Context); + if(playback.Device) + alcCloseDevice(playback.Device); + SDL_Quit(); + + return 1; +} diff --git a/examples/alreverb.c b/examples/alreverb.c index 5001740e..420b1c55 100644 --- a/examples/alreverb.c +++ b/examples/alreverb.c @@ -26,13 +26,6 @@ #include <stdio.h> #include <assert.h> -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#else -#include <unistd.h> -#define Sleep(x) usleep((x)*1000) -#endif #include "AL/al.h" #include "AL/alc.h" @@ -40,37 +33,37 @@ #include "AL/efx-presets.h" #include "common/alhelpers.h" -#include "common/alffmpeg.h" +#include "common/sdl_sound.h" -LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; -LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT; +static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; +static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT; /* Effect object functions */ -LPALGENEFFECTS alGenEffects; -LPALDELETEEFFECTS alDeleteEffects; -LPALISEFFECT alIsEffect; -LPALEFFECTI alEffecti; -LPALEFFECTIV alEffectiv; -LPALEFFECTF alEffectf; -LPALEFFECTFV alEffectfv; -LPALGETEFFECTI alGetEffecti; -LPALGETEFFECTIV alGetEffectiv; -LPALGETEFFECTF alGetEffectf; -LPALGETEFFECTFV alGetEffectfv; +static LPALGENEFFECTS alGenEffects; +static LPALDELETEEFFECTS alDeleteEffects; +static LPALISEFFECT alIsEffect; +static LPALEFFECTI alEffecti; +static LPALEFFECTIV alEffectiv; +static LPALEFFECTF alEffectf; +static LPALEFFECTFV alEffectfv; +static LPALGETEFFECTI alGetEffecti; +static LPALGETEFFECTIV alGetEffectiv; +static LPALGETEFFECTF alGetEffectf; +static LPALGETEFFECTFV alGetEffectfv; /* Auxiliary Effect Slot object functions */ -LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; -LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; -LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; -LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; -LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; -LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; -LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; -LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; -LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; -LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; -LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; +static LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; +static LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; +static LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; +static LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; +static LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; +static LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; +static LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; +static LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; +static LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; +static LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; +static LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; /* LoadEffect loads the given reverb properties into a new OpenAL effect @@ -159,24 +152,21 @@ static ALuint LoadSound(const char *filename) ALuint rate, buffer; size_t datalen; void *data; - FilePtr audiofile; - StreamPtr sound; + FilePtr sound; /* Open the file and get the first stream from it */ - audiofile = openAVFile(filename); - sound = getAVAudioStream(audiofile, 0); + sound = openAudioFile(filename, 1000); if(!sound) { fprintf(stderr, "Could not open audio in %s\n", filename); - closeAVFile(audiofile); return 0; } /* Get the sound format, and figure out the OpenAL format */ - if(getAVAudioInfo(sound, &rate, &channels, &type) != 0) + if(getAudioInfo(sound, &rate, &channels, &type) != 0) { fprintf(stderr, "Error getting audio info for %s\n", filename); - closeAVFile(audiofile); + closeAudioFile(sound); return 0; } @@ -185,16 +175,16 @@ static ALuint LoadSound(const char *filename) { fprintf(stderr, "Unsupported format (%s, %s) for %s\n", ChannelsName(channels), TypeName(type), filename); - closeAVFile(audiofile); + closeAudioFile(sound); return 0; } /* Decode the whole audio stream to a buffer. */ - data = decodeAVAudioStream(sound, &datalen); + data = decodeAudioStream(sound, &datalen); if(!data) { fprintf(stderr, "Failed to read audio from %s\n", filename); - closeAVFile(audiofile); + closeAudioFile(sound); return 0; } @@ -205,7 +195,7 @@ static ALuint LoadSound(const char *filename) alBufferSamplesSOFT(buffer, rate, format, BytesToFrames(datalen, channels, type), channels, type, data); free(data); - closeAVFile(audiofile); + closeAudioFile(sound); /* Check if an error occured, and clean up if so. */ err = alGetError(); diff --git a/examples/alstream.c b/examples/alstream.c index 121cec00..2972d375 100644 --- a/examples/alstream.c +++ b/examples/alstream.c @@ -35,32 +35,26 @@ #include "AL/alext.h" #include "common/alhelpers.h" -#include "common/alffmpeg.h" +#include "common/sdl_sound.h" -LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; -LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT = NULL; +static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; +static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT; -/* Define the number of buffers and buffer size (in samples) to use. 4 buffers - * with 8192 samples each gives a nice per-chunk size, and lets the queue last - * for almost 3/4ths of a second for a 44.1khz stream. */ +/* Define the number of buffers and buffer size (in milliseconds) to use. 4 + * buffers with 200ms each gives a nice per-chunk size, and lets the queue last + * for almost one second. */ #define NUM_BUFFERS 4 -#define BUFFER_SIZE 8192 +#define BUFFER_TIME_MS 200 typedef struct StreamPlayer { /* These are the buffers and source to play out through OpenAL with */ ALuint buffers[NUM_BUFFERS]; ALuint source; - /* Handles for the audio stream */ + /* Handle for the audio file */ FilePtr file; - StreamPtr stream; - - /* A temporary data buffer for readAVAudioData to write to and pass to - * OpenAL with */ - ALbyte *data; - ALsizei datasize; /* The format of the output stream */ ALenum format; @@ -128,17 +122,15 @@ static int OpenPlayerFile(StreamPlayer *player, const char *filename) ClosePlayerFile(player); /* Open the file and get the first stream from it */ - player->file = openAVFile(filename); - player->stream = getAVAudioStream(player->file, 0); - if(!player->stream) + player->file = openAudioFile(filename, BUFFER_TIME_MS); + if(!player->file) { fprintf(stderr, "Could not open audio in %s\n", filename); goto error; } /* Get the stream format, and figure out the OpenAL format */ - if(getAVAudioInfo(player->stream, &player->rate, &player->channels, - &player->type) != 0) + if(getAudioInfo(player->file, &player->rate, &player->channels, &player->type) != 0) { fprintf(stderr, "Error getting audio info for %s\n", filename); goto error; @@ -153,23 +145,11 @@ static int OpenPlayerFile(StreamPlayer *player, const char *filename) goto error; } - /* Allocate enough space for the temp buffer, given the format */ - player->datasize = FramesToBytes(BUFFER_SIZE, player->channels, - player->type); - player->data = malloc(player->datasize); - if(player->data == NULL) - { - fprintf(stderr, "Error allocating %d bytes\n", player->datasize); - goto error; - } - return 1; error: - closeAVFile(player->file); + closeAudioFile(player->file); player->file = NULL; - player->stream = NULL; - player->datasize = 0; return 0; } @@ -177,20 +157,15 @@ error: /* Closes the audio file stream */ static void ClosePlayerFile(StreamPlayer *player) { - closeAVFile(player->file); + closeAudioFile(player->file); player->file = NULL; - player->stream = NULL; - - free(player->data); - player->data = NULL; - player->datasize = 0; } /* Prebuffers some audio from the file, and starts playing the source */ static int StartPlayer(StreamPlayer *player) { - size_t i, got; + size_t i; /* Rewind the source position and clear the buffer queue */ alSourceRewind(player->source); @@ -199,13 +174,16 @@ static int StartPlayer(StreamPlayer *player) /* Fill the buffer queue */ for(i = 0;i < NUM_BUFFERS;i++) { + uint8_t *data; + size_t got; + /* Get some data to give it to the buffer */ - got = readAVAudioData(player->stream, player->data, player->datasize); - if(got == 0) break; + data = getAudioData(player->file, &got); + if(!data) break; alBufferSamplesSOFT(player->buffers[i], player->rate, player->format, BytesToFrames(got, player->channels, player->type), - player->channels, player->type, player->data); + player->channels, player->type, data); } if(alGetError() != AL_NO_ERROR) { @@ -242,6 +220,7 @@ static int UpdatePlayer(StreamPlayer *player) while(processed > 0) { ALuint bufid; + uint8_t *data; size_t got; alSourceUnqueueBuffers(player->source, 1, &bufid); @@ -249,12 +228,12 @@ static int UpdatePlayer(StreamPlayer *player) /* Read the next chunk of data, refill the buffer, and queue it * back on the source */ - got = readAVAudioData(player->stream, player->data, player->datasize); - if(got > 0) + data = getAudioData(player->file, &got); + if(data != NULL) { alBufferSamplesSOFT(bufid, player->rate, player->format, BytesToFrames(got, player->channels, player->type), - player->channels, player->type, player->data); + player->channels, player->type, data); alSourceQueueBuffers(player->source, 1, &bufid); } if(alGetError() != AL_NO_ERROR) @@ -315,10 +294,19 @@ int main(int argc, char **argv) /* Play each file listed on the command line */ for(i = 1;i < argc;i++) { + const char *namepart; + if(!OpenPlayerFile(player, argv[i])) continue; - printf("Playing %s (%s, %s, %dhz)\n", argv[i], + /* Get the name portion, without the path, for display. */ + namepart = strrchr(argv[i], '/'); + if(namepart || (namepart=strrchr(argv[i], '\\'))) + namepart++; + else + namepart = argv[i]; + + printf("Playing: %s (%s, %s, %dhz)\n", namepart, TypeName(player->type), ChannelsName(player->channels), player->rate); fflush(stdout); diff --git a/examples/common/alffmpeg.c b/examples/common/alffmpeg.c deleted file mode 100644 index 16f73866..00000000 --- a/examples/common/alffmpeg.c +++ /dev/null @@ -1,638 +0,0 @@ -/* - * FFmpeg Decoder Helpers - * - * Copyright (c) 2011 by Chris Robinson <[email protected]> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* This file contains routines for helping to decode audio using libavformat - * and libavcodec (ffmpeg). There's very little OpenAL-specific code here. */ - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <signal.h> -#include <assert.h> - -#include "AL/al.h" -#include "AL/alc.h" -#include "AL/alext.h" - -#include "alhelpers.h" -#include "alffmpeg.h" - - -static size_t NextPowerOf2(size_t value) -{ - size_t powerOf2 = 1; - - if(value) - { - value--; - while(value) - { - value >>= 1; - powerOf2 <<= 1; - } - } - return powerOf2; -} - - -struct MemData { - char *buffer; - size_t length; - size_t pos; -}; - -static int MemData_read(void *opaque, uint8_t *buf, int buf_size) -{ - struct MemData *membuf = (struct MemData*)opaque; - int rem = membuf->length - membuf->pos; - - if(rem > buf_size) - rem = buf_size; - - memcpy(buf, &membuf->buffer[membuf->pos], rem); - membuf->pos += rem; - - return rem; -} - -static int MemData_write(void *opaque, uint8_t *buf, int buf_size) -{ - struct MemData *membuf = (struct MemData*)opaque; - int rem = membuf->length - membuf->pos; - - if(rem > buf_size) - rem = buf_size; - - memcpy(&membuf->buffer[membuf->pos], buf, rem); - membuf->pos += rem; - - return rem; -} - -static int64_t MemData_seek(void *opaque, int64_t offset, int whence) -{ - struct MemData *membuf = (struct MemData*)opaque; - - whence &= ~AVSEEK_FORCE; - switch(whence) - { - case SEEK_SET: - if(offset < 0 || (uint64_t)offset > membuf->length) - return -1; - membuf->pos = offset; - break; - - case SEEK_CUR: - if((offset >= 0 && (uint64_t)offset > membuf->length-membuf->pos) || - (offset < 0 && (uint64_t)(-offset) > membuf->pos)) - return -1; - membuf->pos += offset; - break; - - case SEEK_END: - if(offset > 0 || (uint64_t)(-offset) > membuf->length) - return -1; - membuf->pos = membuf->length + offset; - break; - - case AVSEEK_SIZE: - return membuf->length; - - default: - return -1; - } - - return membuf->pos; -} - - -struct PacketList { - AVPacket pkt; - struct PacketList *next; -}; - -struct MyStream { - AVCodecContext *CodecCtx; - int StreamIdx; - - struct PacketList *Packets; - - AVFrame *Frame; - - const uint8_t *FrameData; - size_t FrameDataSize; - - FilePtr parent; -}; - -struct MyFile { - AVFormatContext *FmtCtx; - - StreamPtr *Streams; - size_t StreamsSize; - - struct MemData membuf; -}; - - -static int done_init = 0; - -FilePtr openAVFile(const char *fname) -{ - FilePtr file; - - /* We need to make sure ffmpeg is initialized. Optionally silence warning - * output from the lib */ - if(!done_init) {av_register_all(); - av_log_set_level(AV_LOG_ERROR); - done_init = 1;} - - file = (FilePtr)calloc(1, sizeof(*file)); - if(file && avformat_open_input(&file->FmtCtx, fname, NULL, NULL) == 0) - { - /* After opening, we must search for the stream information because not - * all formats will have it in stream headers */ - if(avformat_find_stream_info(file->FmtCtx, NULL) >= 0) - return file; - avformat_close_input(&file->FmtCtx); - } - - free(file); - return NULL; -} - -FilePtr openAVData(const char *name, char *buffer, size_t buffer_len) -{ - FilePtr file; - - if(!done_init) {av_register_all(); - av_log_set_level(AV_LOG_ERROR); - done_init = 1;} - - if(!name) - name = ""; - - file = (FilePtr)calloc(1, sizeof(*file)); - if(file && (file->FmtCtx=avformat_alloc_context()) != NULL) - { - file->membuf.buffer = buffer; - file->membuf.length = buffer_len; - file->membuf.pos = 0; - - file->FmtCtx->pb = avio_alloc_context(NULL, 0, 0, &file->membuf, - MemData_read, MemData_write, - MemData_seek); - if(file->FmtCtx->pb && avformat_open_input(&file->FmtCtx, name, NULL, NULL) == 0) - { - if(avformat_find_stream_info(file->FmtCtx, NULL) >= 0) - return file; - avformat_close_input(&file->FmtCtx); - } - if(file->FmtCtx) - avformat_free_context(file->FmtCtx); - file->FmtCtx = NULL; - } - - free(file); - return NULL; -} - -FilePtr openAVCustom(const char *name, void *user_data, - int (*read_packet)(void *user_data, uint8_t *buf, int buf_size), - int (*write_packet)(void *user_data, uint8_t *buf, int buf_size), - int64_t (*seek)(void *user_data, int64_t offset, int whence)) -{ - FilePtr file; - - if(!done_init) {av_register_all(); - av_log_set_level(AV_LOG_ERROR); - done_init = 1;} - - if(!name) - name = ""; - - file = (FilePtr)calloc(1, sizeof(*file)); - if(file && (file->FmtCtx=avformat_alloc_context()) != NULL) - { - file->FmtCtx->pb = avio_alloc_context(NULL, 0, 0, user_data, - read_packet, write_packet, seek); - if(file->FmtCtx->pb && avformat_open_input(&file->FmtCtx, name, NULL, NULL) == 0) - { - if(avformat_find_stream_info(file->FmtCtx, NULL) >= 0) - return file; - avformat_close_input(&file->FmtCtx); - } - if(file->FmtCtx) - avformat_free_context(file->FmtCtx); - file->FmtCtx = NULL; - } - - free(file); - return NULL; -} - - -void clearAVAudioData(StreamPtr stream) -{ - while(stream->Packets) - { - struct PacketList *self; - - self = stream->Packets; - stream->Packets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - } -} - - -void closeAVFile(FilePtr file) -{ - size_t i; - - if(!file) return; - - for(i = 0;i < file->StreamsSize;i++) - { - StreamPtr stream = file->Streams[i]; - - while(stream->Packets) - { - struct PacketList *self; - - self = stream->Packets; - stream->Packets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - } - - avcodec_close(stream->CodecCtx); - av_free(stream->Frame); - free(stream); - } - free(file->Streams); - - avformat_close_input(&file->FmtCtx); - free(file); -} - - -int getAVFileInfo(FilePtr file, int *numaudiostreams) -{ - unsigned int i; - int audiocount = 0; - - if(!file) return 1; - for(i = 0;i < file->FmtCtx->nb_streams;i++) - { - if(file->FmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) - audiocount++; - } - *numaudiostreams = audiocount; - return 0; -} - -StreamPtr getAVAudioStream(FilePtr file, int streamnum) -{ - unsigned int i; - if(!file) return NULL; - for(i = 0;i < file->FmtCtx->nb_streams;i++) - { - if(file->FmtCtx->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO) - continue; - - if(streamnum == 0) - { - StreamPtr stream; - AVCodec *codec; - void *temp; - size_t j; - - /* Found the requested stream. Check if a handle to this stream - * already exists and return it if it does */ - for(j = 0;j < file->StreamsSize;j++) - { - if(file->Streams[j]->StreamIdx == (int)i) - return file->Streams[j]; - } - - /* Doesn't yet exist. Now allocate a new stream object and fill in - * its info */ - stream = (StreamPtr)calloc(1, sizeof(*stream)); - if(!stream) return NULL; - - stream->parent = file; - stream->CodecCtx = file->FmtCtx->streams[i]->codec; - stream->StreamIdx = i; - - /* Try to find the codec for the given codec ID, and open it */ - codec = avcodec_find_decoder(stream->CodecCtx->codec_id); - if(!codec || avcodec_open2(stream->CodecCtx, codec, NULL) < 0) - { - free(stream); - return NULL; - } - - /* Allocate space for the decoded data to be stored in before it - * gets passed to the app */ - stream->Frame = avcodec_alloc_frame(); - if(!stream->Frame) - { - avcodec_close(stream->CodecCtx); - free(stream); - return NULL; - } - stream->FrameData = NULL; - stream->FrameDataSize = 0; - - /* Append the new stream object to the stream list. The original - * pointer will remain valid if realloc fails, so we need to use - * another pointer to watch for errors and not leak memory */ - temp = realloc(file->Streams, (file->StreamsSize+1) * - sizeof(*file->Streams)); - if(!temp) - { - avcodec_close(stream->CodecCtx); - av_free(stream->Frame); - free(stream); - return NULL; - } - file->Streams = (StreamPtr*)temp; - file->Streams[file->StreamsSize++] = stream; - return stream; - } - streamnum--; - } - return NULL; -} - -int getAVAudioInfo(StreamPtr stream, ALuint *rate, ALenum *channels, ALenum *type) -{ - if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) - return 1; - - /* Get the sample type for OpenAL given the format detected by ffmpeg. */ - if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_U8) - *type = AL_UNSIGNED_BYTE_SOFT; - else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) - *type = AL_SHORT_SOFT; - else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_S32) - *type = AL_INT_SOFT; - else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_FLT) - *type = AL_FLOAT_SOFT; - else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_DBL) - *type = AL_DOUBLE_SOFT; - else - { - fprintf(stderr, "Unsupported ffmpeg sample format: %s\n", - av_get_sample_fmt_name(stream->CodecCtx->sample_fmt)); - return 1; - } - - /* Get the OpenAL channel configuration using the channel layout detected - * by ffmpeg. NOTE: some file types may not specify a channel layout. In - * that case, one must be guessed based on the channel count. */ - if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_MONO) - *channels = AL_MONO_SOFT; - else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) - *channels = AL_STEREO_SOFT; - else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) - *channels = AL_QUAD_SOFT; - else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) - *channels = AL_5POINT1_SOFT; - else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) - *channels = AL_7POINT1_SOFT; - else if(stream->CodecCtx->channel_layout == 0) - { - /* Unknown channel layout. Try to guess. */ - if(stream->CodecCtx->channels == 1) - *channels = AL_MONO_SOFT; - else if(stream->CodecCtx->channels == 2) - *channels = AL_STEREO_SOFT; - else - { - fprintf(stderr, "Unsupported ffmpeg raw channel count: %d\n", - stream->CodecCtx->channels); - return 1; - } - } - else - { - char str[1024]; - av_get_channel_layout_string(str, sizeof(str), stream->CodecCtx->channels, - stream->CodecCtx->channel_layout); - fprintf(stderr, "Unsupported ffmpeg channel layout: %s\n", str); - return 1; - } - - *rate = stream->CodecCtx->sample_rate; - - return 0; -} - - -/* Used by getAV*Data to search for more compressed data, and buffer it in the - * correct stream. It won't buffer data for streams that the app doesn't have a - * handle for. */ -static int getNextPacket(FilePtr file, int streamidx) -{ - struct PacketList *packet; - - packet = (struct PacketList*)av_malloc(sizeof(*packet)); - packet->next = NULL; - -next_packet: - while(av_read_frame(file->FmtCtx, &packet->pkt) >= 0) - { - StreamPtr *iter = file->Streams; - StreamPtr *iter_end = iter + file->StreamsSize; - - /* Check each stream the user has a handle for, looking for the one - * this packet belongs to */ - while(iter != iter_end) - { - if((*iter)->StreamIdx == packet->pkt.stream_index) - { - struct PacketList **last; - - last = &(*iter)->Packets; - while(*last != NULL) - last = &(*last)->next; - - *last = packet; - if((*iter)->StreamIdx == streamidx) - return 1; - - packet = (struct PacketList*)av_malloc(sizeof(*packet)); - packet->next = NULL; - goto next_packet; - } - iter++; - } - /* Free the packet and look for another */ - av_free_packet(&packet->pkt); - } - - av_free(packet); - return 0; -} - -uint8_t *getAVAudioData(StreamPtr stream, size_t *length) -{ - int got_frame; - int len; - - if(length) *length = 0; - - if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) - return NULL; - -next_packet: - if(!stream->Packets && !getNextPacket(stream->parent, stream->StreamIdx)) - return NULL; - - /* Decode some data, and check for errors */ - avcodec_get_frame_defaults(stream->Frame); - while((len=avcodec_decode_audio4(stream->CodecCtx, stream->Frame, - &got_frame, &stream->Packets->pkt)) < 0) - { - struct PacketList *self; - - /* Error? Drop it and try the next, I guess... */ - self = stream->Packets; - stream->Packets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - - if(!stream->Packets) - goto next_packet; - } - - if(len < stream->Packets->pkt.size) - { - /* Move the unread data to the front and clear the end bits */ - int remaining = stream->Packets->pkt.size - len; - memmove(stream->Packets->pkt.data, &stream->Packets->pkt.data[len], - remaining); - memset(&stream->Packets->pkt.data[remaining], 0, - stream->Packets->pkt.size - remaining); - stream->Packets->pkt.size -= len; - } - else - { - struct PacketList *self; - - self = stream->Packets; - stream->Packets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - } - - if(!got_frame || stream->Frame->nb_samples == 0) - goto next_packet; - - /* Set the output buffer size */ - *length = av_samples_get_buffer_size(NULL, stream->CodecCtx->channels, - stream->Frame->nb_samples, - stream->CodecCtx->sample_fmt, 1); - - return stream->Frame->data[0]; -} - -size_t readAVAudioData(StreamPtr stream, void *data, size_t length) -{ - size_t dec = 0; - - if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) - return 0; - - while(dec < length) - { - /* If there's no decoded data, find some */ - if(stream->FrameDataSize == 0) - { - stream->FrameData = getAVAudioData(stream, &stream->FrameDataSize); - if(!stream->FrameData) - break; - } - - if(stream->FrameDataSize > 0) - { - /* Get the amount of bytes remaining to be written, and clamp to - * the amount of decoded data we have */ - size_t rem = length-dec; - if(rem > stream->FrameDataSize) - rem = stream->FrameDataSize; - - /* Copy the data to the app's buffer and increment */ - if(data != NULL) - { - memcpy(data, stream->FrameData, rem); - data = (char*)data + rem; - } - dec += rem; - - /* If there's any decoded data left, move it to the front of the - * buffer for next time */ - stream->FrameData += rem; - stream->FrameDataSize -= rem; - } - } - - /* Return the number of bytes we were able to get */ - return dec; -} - -void *decodeAVAudioStream(StreamPtr stream, size_t *length) -{ - char *outbuf = NULL; - size_t buflen = 0; - void *inbuf; - size_t got; - - *length = 0; - if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) - return NULL; - - while((inbuf=getAVAudioData(stream, &got)) != NULL && got > 0) - { - void *ptr; - - ptr = realloc(outbuf, NextPowerOf2(buflen+got)); - if(ptr == NULL) - break; - outbuf = (char*)ptr; - - memcpy(&outbuf[buflen], inbuf, got); - buflen += got; - } - outbuf = (char*)realloc(outbuf, buflen); - - *length = buflen; - return outbuf; -} diff --git a/examples/common/alffmpeg.h b/examples/common/alffmpeg.h deleted file mode 100644 index 27e49ab4..00000000 --- a/examples/common/alffmpeg.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef ALFFMPEG_H -#define ALFFMPEG_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#include <libavcodec/avcodec.h> -#include <libavformat/avformat.h> - -/* Opaque handles to files and streams. Apps don't need to concern themselves - * with the internals */ -typedef struct MyFile *FilePtr; -typedef struct MyStream *StreamPtr; - -/* Opens a file with ffmpeg and sets up the streams' information */ -FilePtr openAVFile(const char *fname); - -/* Opens a named file image with ffmpeg and sets up the streams' information */ -FilePtr openAVData(const char *name, char *buffer, size_t buffer_len); - -/* Opens a named data stream with ffmpeg, using the specified data pointer and - * callbacks, and sets up the streams' information */ -FilePtr openAVCustom(const char *name, void *user_data, - int (*read_packet)(void *user_data, uint8_t *buf, int buf_size), - int (*write_packet)(void *user_data, uint8_t *buf, int buf_size), - int64_t (*seek)(void *user_data, int64_t offset, int whence)); - -/* Closes/frees an opened file and any of its streams */ -void closeAVFile(FilePtr file); - -/* Reports certain information from the file, eg, the number of audio - * streams. Returns 0 on success. */ -int getAVFileInfo(FilePtr file, int *numaudiostreams); - -/* Retrieves a handle for the given audio stream number (generally 0, but some - * files can have multiple audio streams in one file). */ -StreamPtr getAVAudioStream(FilePtr file, int streamnum); - -/* Returns information about the given audio stream. Returns 0 on success. */ -int getAVAudioInfo(StreamPtr stream, ALuint *rate, ALenum *channels, ALenum *type); - -/* Returns a pointer to the next available packet of decoded audio. Any data - * from a previously-decoded packet is dropped. The size (in bytes) of the - * returned data buffer is stored in 'length', and the returned pointer is only - * valid until the next call to getAVAudioData or readAVAudioData. */ -uint8_t *getAVAudioData(StreamPtr stream, size_t *length); - -/* The "meat" function. Decodes audio and writes, at most, length bytes into - * the provided data buffer. Will only return less for end-of-stream or error - * conditions. Returns the number of bytes written. */ -size_t readAVAudioData(StreamPtr stream, void *data, size_t length); - -/* Decodes all remaining data from the stream and returns a buffer containing - * the audio data, with the size stored in 'length'. The returned pointer must - * be freed with a call to free(). Note that since this decodes the whole - * stream, using it on lengthy streams (eg, music) will use a lot of memory. - * Such streams are better handled using getAVAudioData or readAVAudioData to - * keep smaller chunks in memory at any given time. */ -void *decodeAVAudioStream(StreamPtr stream, size_t *length); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* ALFFMPEG_H */ diff --git a/examples/common/alhelpers.h b/examples/common/alhelpers.h index eda8925e..62ed5be2 100644 --- a/examples/common/alhelpers.h +++ b/examples/common/alhelpers.h @@ -9,6 +9,10 @@ #include <windows.h> #endif +#include "AL/alc.h" +#include "AL/al.h" +#include "AL/alext.h" + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/examples/common/sdl_sound.c b/examples/common/sdl_sound.c new file mode 100644 index 00000000..79a5bf32 --- /dev/null +++ b/examples/common/sdl_sound.c @@ -0,0 +1,164 @@ +/* + * SDL_sound Decoder Helpers + * + * Copyright (c) 2013 by Chris Robinson <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This file contains routines for helping to decode audio using SDL_sound. + * There's very little OpenAL-specific code here. + */ +#include "sdl_sound.h" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <assert.h> + +#include <SDL_sound.h> + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "alhelpers.h" + + +static int done_init = 0; + +FilePtr openAudioFile(const char *fname, size_t buftime_ms) +{ + FilePtr file; + ALuint rate; + Uint32 bufsize; + ALenum chans, type; + + /* We need to make sure SDL_sound is initialized. */ + if(!done_init) + { + Sound_Init(); + done_init = 1; + } + + file = Sound_NewSampleFromFile(fname, NULL, 0); + if(!file) + { + fprintf(stderr, "Failed to open %s: %s\n", fname, Sound_GetError()); + return NULL; + } + + if(getAudioInfo(file, &rate, &chans, &type) != 0) + { + Sound_FreeSample(file); + return NULL; + } + + bufsize = FramesToBytes((ALsizei)(buftime_ms/1000.0*rate), chans, type); + if(Sound_SetBufferSize(file, bufsize) == 0) + { + fprintf(stderr, "Failed to set buffer size to %u bytes: %s\n", bufsize, Sound_GetError()); + Sound_FreeSample(file); + return NULL; + } + + return file; +} + +void closeAudioFile(FilePtr file) +{ + if(file) + Sound_FreeSample(file); +} + + +int getAudioInfo(FilePtr file, ALuint *rate, ALenum *channels, ALenum *type) +{ + if(file->actual.channels == 1) + *channels = AL_MONO_SOFT; + else if(file->actual.channels == 2) + *channels = AL_STEREO_SOFT; + else + { + fprintf(stderr, "Unsupported channel count: %d\n", file->actual.channels); + return 1; + } + + if(file->actual.format == AUDIO_U8) + *type = AL_UNSIGNED_BYTE_SOFT; + else if(file->actual.format == AUDIO_S8) + *type = AL_BYTE_SOFT; + else if(file->actual.format == AUDIO_U16LSB || file->actual.format == AUDIO_U16MSB) + *type = AL_UNSIGNED_SHORT_SOFT; + else if(file->actual.format == AUDIO_S16LSB || file->actual.format == AUDIO_S16MSB) + *type = AL_SHORT_SOFT; + else + { + fprintf(stderr, "Unsupported sample format: 0x%04x\n", file->actual.format); + return 1; + } + + *rate = file->actual.rate; + + return 0; +} + + +uint8_t *getAudioData(FilePtr file, size_t *length) +{ + *length = Sound_Decode(file); + if(*length == 0) + return NULL; + if((file->actual.format == AUDIO_U16LSB && AUDIO_U16LSB != AUDIO_U16SYS) || + (file->actual.format == AUDIO_U16MSB && AUDIO_U16MSB != AUDIO_U16SYS) || + (file->actual.format == AUDIO_S16LSB && AUDIO_S16LSB != AUDIO_S16SYS) || + (file->actual.format == AUDIO_S16MSB && AUDIO_S16MSB != AUDIO_S16SYS)) + { + /* Swap bytes if the decoded endianness doesn't match the system. */ + char *buffer = file->buffer; + size_t i; + for(i = 0;i < *length;i+=2) + { + char b = buffer[i]; + buffer[i] = buffer[i+1]; + buffer[i+1] = b; + } + } + return file->buffer; +} + +void *decodeAudioStream(FilePtr file, size_t *length) +{ + Uint32 got; + char *mem; + + got = Sound_DecodeAll(file); + if(got == 0) + { + *length = 0; + return NULL; + } + + mem = malloc(got); + memcpy(mem, file->buffer, got); + + *length = got; + return mem; +} diff --git a/examples/common/sdl_sound.h b/examples/common/sdl_sound.h new file mode 100644 index 00000000..e93ab92b --- /dev/null +++ b/examples/common/sdl_sound.h @@ -0,0 +1,43 @@ +#ifndef EXAMPLES_SDL_SOUND_H +#define EXAMPLES_SDL_SOUND_H + +#include "AL/al.h" + +#include <SDL_sound.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Opaque handles to files and streams. Apps don't need to concern themselves + * with the internals */ +typedef Sound_Sample *FilePtr; + +/* Opens a file with SDL_sound, and specifies the size of the sample buffer in + * milliseconds. */ +FilePtr openAudioFile(const char *fname, size_t buftime_ms); + +/* Closes/frees an opened file */ +void closeAudioFile(FilePtr file); + +/* Returns information about the given audio stream. Returns 0 on success. */ +int getAudioInfo(FilePtr file, ALuint *rate, ALenum *channels, ALenum *type); + +/* Returns a pointer to the next available chunk of decoded audio. The size (in + * bytes) of the returned data buffer is stored in 'length', and the returned + * pointer is only valid until the next call to getAudioData. */ +uint8_t *getAudioData(FilePtr file, size_t *length); + +/* Decodes all remaining data from the stream and returns a buffer containing + * the audio data, with the size stored in 'length'. The returned pointer must + * be freed with a call to free(). Note that since this decodes the whole + * stream, using it on lengthy streams (eg, music) will use a lot of memory. + * Such streams are better handled using getAudioData to keep smaller chunks in + * memory at any given time. */ +void *decodeAudioStream(FilePtr, size_t *length); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* EXAMPLES_SDL_SOUND_H */ |