Updated openal-soft to version 1.15.1

This commit is contained in:
fysx
2014-01-30 10:19:28 +01:00
parent 33d979caf6
commit f0fa37f4ad
164 changed files with 38848 additions and 21244 deletions
@@ -0,0 +1,204 @@
/*
* OpenAL Source Latency Example
*
* Copyright (c) 2012 by Chris Robinson <chris.kcat@gmail.com>
*
* 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 checking the latency of a sound. */
#include <stdio.h>
#include <assert.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "common/alhelpers.h"
#include "common/sdl_sound.h"
static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples;
static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT;
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. */
static ALuint LoadSound(const char *filename)
{
ALenum err, format, type, channels;
ALuint rate, buffer;
size_t datalen;
void *data;
FilePtr sound;
/* Open the audio file */
sound = openAudioFile(filename, 1000);
if(!sound)
{
fprintf(stderr, "Could not open audio in %s\n", filename);
closeAudioFile(sound);
return 0;
}
/* Get the sound format, and figure out the OpenAL format */
if(getAudioInfo(sound, &rate, &channels, &type) != 0)
{
fprintf(stderr, "Error getting audio info for %s\n", filename);
closeAudioFile(sound);
return 0;
}
format = GetFormat(channels, type, alIsBufferFormatSupportedSOFT);
if(format == AL_NONE)
{
fprintf(stderr, "Unsupported format (%s, %s) for %s\n",
ChannelsName(channels), TypeName(type), filename);
closeAudioFile(sound);
return 0;
}
/* Decode the whole audio stream to a buffer. */
data = decodeAudioStream(sound, &datalen);
if(!data)
{
fprintf(stderr, "Failed to read audio from %s\n", filename);
closeAudioFile(sound);
return 0;
}
/* Buffer the audio data into a new buffer object, then free the data and
* close the file. */
buffer = 0;
alGenBuffers(1, &buffer);
alBufferSamplesSOFT(buffer, rate, format, BytesToFrames(datalen, channels, type),
channels, type, data);
free(data);
closeAudioFile(sound);
/* 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(int argc, char **argv)
{
ALuint source, buffer;
ALdouble offsets[2];
ALenum state;
/* Print out usage if no file was specified */
if(argc < 2)
{
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
return 1;
}
/* Initialize OpenAL with the default device, and check for EFX support. */
if(InitAL() != 0)
return 1;
if(!alIsExtensionPresent("AL_SOFT_source_latency"))
{
fprintf(stderr, "Error: AL_SOFT_source_latency not supported\n");
CloseAL();
return 1;
}
/* Define a macro to help load the function pointers. */
#define LOAD_PROC(x) ((x) = alGetProcAddress(#x))
LOAD_PROC(alSourcedSOFT);
LOAD_PROC(alSource3dSOFT);
LOAD_PROC(alSourcedvSOFT);
LOAD_PROC(alGetSourcedSOFT);
LOAD_PROC(alGetSource3dSOFT);
LOAD_PROC(alGetSourcedvSOFT);
LOAD_PROC(alSourcei64SOFT);
LOAD_PROC(alSource3i64SOFT);
LOAD_PROC(alSourcei64vSOFT);
LOAD_PROC(alGetSourcei64SOFT);
LOAD_PROC(alGetSource3i64SOFT);
LOAD_PROC(alGetSourcei64vSOFT);
if(alIsExtensionPresent("AL_SOFT_buffer_samples"))
{
LOAD_PROC(alBufferSamplesSOFT);
LOAD_PROC(alIsBufferFormatSupportedSOFT);
}
#undef LOAD_PROC
/* Load the sound into a buffer. */
buffer = LoadSound(argv[1]);
if(!buffer)
{
CloseAL();
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);
/* Get the source offset and latency. AL_SEC_OFFSET_LATENCY_SOFT will
* place the offset (in seconds) in offsets[0], and the time until that
* offset will be heard (in seconds) in offsets[1]. */
alGetSourcedvSOFT(source, AL_SEC_OFFSET_LATENCY_SOFT, offsets);
printf("\rOffset: %f - Latency:%3u ms ", offsets[0], (ALuint)(offsets[1]*1000));
fflush(stdout);
} while(alGetError() == AL_NO_ERROR && state == AL_PLAYING);
printf("\n");
/* All done. Delete resources, and close OpenAL. */
alDeleteSources(1, &source);
alDeleteBuffers(1, &buffer);
CloseAL();
return 0;
}
@@ -0,0 +1,244 @@
/*
* OpenAL Loopback Example
*
* Copyright (c) 2013 by Chris Robinson <chris.kcat@gmail.com>
*
* 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;
}
+327
View File
@@ -0,0 +1,327 @@
/*
* OpenAL Reverb Example
*
* Copyright (c) 2012 by Chris Robinson <chris.kcat@gmail.com>
*
* 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 applying reverb to a sound. */
#include <stdio.h>
#include <assert.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "AL/efx-presets.h"
#include "common/alhelpers.h"
#include "common/sdl_sound.h"
static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples;
static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT;
/* Effect object functions */
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 */
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
* object, and returns the new effect ID. */
static ALuint LoadEffect(const EFXEAXREVERBPROPERTIES *reverb)
{
ALuint effect = 0;
ALenum err;
/* Create the effect object and check if we can do EAX reverb. */
alGenEffects(1, &effect);
if(alGetEnumValue("AL_EFFECT_EAXREVERB") != 0)
{
printf("Using EAX Reverb\n");
/* EAX Reverb is available. Set the EAX effect type then load the
* reverb properties. */
alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
alEffectf(effect, AL_EAXREVERB_DENSITY, reverb->flDensity);
alEffectf(effect, AL_EAXREVERB_DIFFUSION, reverb->flDiffusion);
alEffectf(effect, AL_EAXREVERB_GAIN, reverb->flGain);
alEffectf(effect, AL_EAXREVERB_GAINHF, reverb->flGainHF);
alEffectf(effect, AL_EAXREVERB_GAINLF, reverb->flGainLF);
alEffectf(effect, AL_EAXREVERB_DECAY_TIME, reverb->flDecayTime);
alEffectf(effect, AL_EAXREVERB_DECAY_HFRATIO, reverb->flDecayHFRatio);
alEffectf(effect, AL_EAXREVERB_DECAY_LFRATIO, reverb->flDecayLFRatio);
alEffectf(effect, AL_EAXREVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain);
alEffectf(effect, AL_EAXREVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay);
alEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN, reverb->flReflectionsPan);
alEffectf(effect, AL_EAXREVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain);
alEffectf(effect, AL_EAXREVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay);
alEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN, reverb->flLateReverbPan);
alEffectf(effect, AL_EAXREVERB_ECHO_TIME, reverb->flEchoTime);
alEffectf(effect, AL_EAXREVERB_ECHO_DEPTH, reverb->flEchoDepth);
alEffectf(effect, AL_EAXREVERB_MODULATION_TIME, reverb->flModulationTime);
alEffectf(effect, AL_EAXREVERB_MODULATION_DEPTH, reverb->flModulationDepth);
alEffectf(effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF);
alEffectf(effect, AL_EAXREVERB_HFREFERENCE, reverb->flHFReference);
alEffectf(effect, AL_EAXREVERB_LFREFERENCE, reverb->flLFReference);
alEffectf(effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor);
alEffecti(effect, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);
}
else
{
printf("Using Standard Reverb\n");
/* No EAX Reverb. Set the standard reverb effect type then load the
* available reverb properties. */
alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
alEffectf(effect, AL_REVERB_DENSITY, reverb->flDensity);
alEffectf(effect, AL_REVERB_DIFFUSION, reverb->flDiffusion);
alEffectf(effect, AL_REVERB_GAIN, reverb->flGain);
alEffectf(effect, AL_REVERB_GAINHF, reverb->flGainHF);
alEffectf(effect, AL_REVERB_DECAY_TIME, reverb->flDecayTime);
alEffectf(effect, AL_REVERB_DECAY_HFRATIO, reverb->flDecayHFRatio);
alEffectf(effect, AL_REVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain);
alEffectf(effect, AL_REVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay);
alEffectf(effect, AL_REVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain);
alEffectf(effect, AL_REVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay);
alEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF);
alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor);
alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);
}
/* 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(alIsEffect(effect))
alDeleteEffects(1, &effect);
return 0;
}
return effect;
}
/* LoadBuffer loads the named audio file into an OpenAL buffer object, and
* returns the new buffer ID. */
static ALuint LoadSound(const char *filename)
{
ALenum err, format, type, channels;
ALuint rate, buffer;
size_t datalen;
void *data;
FilePtr sound;
/* Open the file and get the first stream from it */
sound = openAudioFile(filename, 1000);
if(!sound)
{
fprintf(stderr, "Could not open audio in %s\n", filename);
return 0;
}
/* Get the sound format, and figure out the OpenAL format */
if(getAudioInfo(sound, &rate, &channels, &type) != 0)
{
fprintf(stderr, "Error getting audio info for %s\n", filename);
closeAudioFile(sound);
return 0;
}
format = GetFormat(channels, type, alIsBufferFormatSupportedSOFT);
if(format == AL_NONE)
{
fprintf(stderr, "Unsupported format (%s, %s) for %s\n",
ChannelsName(channels), TypeName(type), filename);
closeAudioFile(sound);
return 0;
}
/* Decode the whole audio stream to a buffer. */
data = decodeAudioStream(sound, &datalen);
if(!data)
{
fprintf(stderr, "Failed to read audio from %s\n", filename);
closeAudioFile(sound);
return 0;
}
/* Buffer the audio data into a new buffer object, then free the data and
* close the file. */
buffer = 0;
alGenBuffers(1, &buffer);
alBufferSamplesSOFT(buffer, rate, format, BytesToFrames(datalen, channels, type),
channels, type, data);
free(data);
closeAudioFile(sound);
/* 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(int argc, char **argv)
{
EFXEAXREVERBPROPERTIES reverb = EFX_REVERB_PRESET_GENERIC;
ALuint source, buffer, effect, slot;
ALenum state;
/* Print out usage if no file was specified */
if(argc < 2)
{
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
return 1;
}
/* Initialize OpenAL with the default device, and check for EFX support. */
if(InitAL() != 0)
return 1;
if(!alcIsExtensionPresent(alcGetContextsDevice(alcGetCurrentContext()), "ALC_EXT_EFX"))
{
fprintf(stderr, "Error: EFX not supported\n");
CloseAL();
return 1;
}
/* Define a macro to help load the function pointers. */
#define LOAD_PROC(x) ((x) = alGetProcAddress(#x))
LOAD_PROC(alGenEffects);
LOAD_PROC(alDeleteEffects);
LOAD_PROC(alIsEffect);
LOAD_PROC(alEffecti);
LOAD_PROC(alEffectiv);
LOAD_PROC(alEffectf);
LOAD_PROC(alEffectfv);
LOAD_PROC(alGetEffecti);
LOAD_PROC(alGetEffectiv);
LOAD_PROC(alGetEffectf);
LOAD_PROC(alGetEffectfv);
LOAD_PROC(alGenAuxiliaryEffectSlots);
LOAD_PROC(alDeleteAuxiliaryEffectSlots);
LOAD_PROC(alIsAuxiliaryEffectSlot);
LOAD_PROC(alAuxiliaryEffectSloti);
LOAD_PROC(alAuxiliaryEffectSlotiv);
LOAD_PROC(alAuxiliaryEffectSlotf);
LOAD_PROC(alAuxiliaryEffectSlotfv);
LOAD_PROC(alGetAuxiliaryEffectSloti);
LOAD_PROC(alGetAuxiliaryEffectSlotiv);
LOAD_PROC(alGetAuxiliaryEffectSlotf);
LOAD_PROC(alGetAuxiliaryEffectSlotfv);
if(alIsExtensionPresent("AL_SOFT_buffer_samples"))
{
LOAD_PROC(alBufferSamplesSOFT);
LOAD_PROC(alIsBufferFormatSupportedSOFT);
}
#undef LOAD_PROC
/* Load the sound into a buffer. */
buffer = LoadSound(argv[1]);
if(!buffer)
{
CloseAL();
return 1;
}
/* Load the reverb into an effect. */
effect = LoadEffect(&reverb);
if(!effect)
{
alDeleteBuffers(1, &buffer);
CloseAL();
return 1;
}
/* Create the effect slot object. This is what "plays" an effect on sources
* that connect to it. */
slot = 0;
alGenAuxiliaryEffectSlots(1, &slot);
/* Tell the effect slot to use the loaded effect object. Note that the this
* effectively copies the effect properties. You can modify or delete the
* effect object afterward without affecting the effect slot.
*/
alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, effect);
assert(alGetError()==AL_NO_ERROR && "Failed to set effect slot");
/* Create the source to play the sound with. */
source = 0;
alGenSources(1, &source);
alSourcei(source, AL_BUFFER, buffer);
/* Connect the source to the effect slot. This tells the source to use the
* effect slot 'slot', on send #0 with the AL_FILTER_NULL filter object.
*/
alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot, 0, AL_FILTER_NULL);
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);
alDeleteAuxiliaryEffectSlots(1, &slot);
alDeleteEffects(1, &effect);
alDeleteBuffers(1, &buffer);
CloseAL();
return 0;
}
+335
View File
@@ -0,0 +1,335 @@
/*
* OpenAL Audio Stream Example
*
* Copyright (c) 2011 by Chris Robinson <chris.kcat@gmail.com>
*
* 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 a relatively simple streaming audio player. */
#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 "common/alhelpers.h"
#include "common/sdl_sound.h"
static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples;
static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT;
/* 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_TIME_MS 200
typedef struct StreamPlayer {
/* These are the buffers and source to play out through OpenAL with */
ALuint buffers[NUM_BUFFERS];
ALuint source;
/* Handle for the audio file */
FilePtr file;
/* The format of the output stream */
ALenum format;
ALenum channels;
ALenum type;
ALuint rate;
} StreamPlayer;
static StreamPlayer *NewPlayer(void);
static void DeletePlayer(StreamPlayer *player);
static int OpenPlayerFile(StreamPlayer *player, const char *filename);
static void ClosePlayerFile(StreamPlayer *player);
static int StartPlayer(StreamPlayer *player);
static int UpdatePlayer(StreamPlayer *player);
/* Creates a new player object, and allocates the needed OpenAL source and
* buffer objects. Error checking is simplified for the purposes of this
* example, and will cause an abort if needed. */
static StreamPlayer *NewPlayer(void)
{
StreamPlayer *player;
player = malloc(sizeof(*player));
assert(player != NULL);
memset(player, 0, sizeof(*player));
/* Generate the buffers and source */
alGenBuffers(NUM_BUFFERS, player->buffers);
assert(alGetError() == AL_NO_ERROR && "Could not create buffers");
alGenSources(1, &player->source);
assert(alGetError() == AL_NO_ERROR && "Could not create source");
/* Set parameters so mono sources play out the front-center speaker and
* won't distance attenuate. */
alSource3i(player->source, AL_POSITION, 0, 0, -1);
alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(player->source, AL_ROLLOFF_FACTOR, 0);
assert(alGetError() == AL_NO_ERROR && "Could not set source parameters");
return player;
}
/* Destroys a player object, deleting the source and buffers. No error handling
* since these calls shouldn't fail with a properly-made player object. */
static void DeletePlayer(StreamPlayer *player)
{
ClosePlayerFile(player);
alDeleteSources(1, &player->source);
alDeleteBuffers(NUM_BUFFERS, player->buffers);
if(alGetError() != AL_NO_ERROR)
fprintf(stderr, "Failed to delete object IDs\n");
memset(player, 0, sizeof(*player));
free(player);
}
/* Opens the first audio stream of the named file. If a file is already open,
* it will be closed first. */
static int OpenPlayerFile(StreamPlayer *player, const char *filename)
{
ClosePlayerFile(player);
/* Open the file and get the first stream from it */
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(getAudioInfo(player->file, &player->rate, &player->channels, &player->type) != 0)
{
fprintf(stderr, "Error getting audio info for %s\n", filename);
goto error;
}
player->format = GetFormat(player->channels, player->type, alIsBufferFormatSupportedSOFT);
if(player->format == 0)
{
fprintf(stderr, "Unsupported format (%s, %s) for %s\n",
ChannelsName(player->channels), TypeName(player->type),
filename);
goto error;
}
return 1;
error:
closeAudioFile(player->file);
player->file = NULL;
return 0;
}
/* Closes the audio file stream */
static void ClosePlayerFile(StreamPlayer *player)
{
closeAudioFile(player->file);
player->file = NULL;
}
/* Prebuffers some audio from the file, and starts playing the source */
static int StartPlayer(StreamPlayer *player)
{
size_t i;
/* Rewind the source position and clear the buffer queue */
alSourceRewind(player->source);
alSourcei(player->source, AL_BUFFER, 0);
/* 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 */
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, data);
}
if(alGetError() != AL_NO_ERROR)
{
fprintf(stderr, "Error buffering for playback\n");
return 0;
}
/* Now queue and start playback! */
alSourceQueueBuffers(player->source, i, player->buffers);
alSourcePlay(player->source);
if(alGetError() != AL_NO_ERROR)
{
fprintf(stderr, "Error starting playback\n");
return 0;
}
return 1;
}
static int UpdatePlayer(StreamPlayer *player)
{
ALint processed, state;
/* Get relevant source info */
alGetSourcei(player->source, AL_SOURCE_STATE, &state);
alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed);
if(alGetError() != AL_NO_ERROR)
{
fprintf(stderr, "Error checking source state\n");
return 0;
}
/* Unqueue and handle each processed buffer */
while(processed > 0)
{
ALuint bufid;
uint8_t *data;
size_t got;
alSourceUnqueueBuffers(player->source, 1, &bufid);
processed--;
/* Read the next chunk of data, refill the buffer, and queue it
* back on the source */
data = getAudioData(player->file, &got);
if(data != NULL)
{
alBufferSamplesSOFT(bufid, player->rate, player->format,
BytesToFrames(got, player->channels, player->type),
player->channels, player->type, data);
alSourceQueueBuffers(player->source, 1, &bufid);
}
if(alGetError() != AL_NO_ERROR)
{
fprintf(stderr, "Error buffering data\n");
return 0;
}
}
/* Make sure the source hasn't underrun */
if(state != AL_PLAYING && state != AL_PAUSED)
{
ALint queued;
/* If no buffers are queued, playback is finished */
alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued);
if(queued == 0)
return 0;
alSourcePlay(player->source);
if(alGetError() != AL_NO_ERROR)
{
fprintf(stderr, "Error restarting playback\n");
return 0;
}
}
return 1;
}
int main(int argc, char **argv)
{
StreamPlayer *player;
int i;
/* Print out usage if no file was specified */
if(argc < 2)
{
fprintf(stderr, "Usage: %s <filenames...>\n", argv[0]);
return 1;
}
if(InitAL() != 0)
return 1;
if(alIsExtensionPresent("AL_SOFT_buffer_samples"))
{
printf("AL_SOFT_buffer_samples supported!\n");
alBufferSamplesSOFT = alGetProcAddress("alBufferSamplesSOFT");
alIsBufferFormatSupportedSOFT = alGetProcAddress("alIsBufferFormatSupportedSOFT");
}
else
printf("AL_SOFT_buffer_samples not supported\n");
player = NewPlayer();
/* Play each file listed on the command line */
for(i = 1;i < argc;i++)
{
const char *namepart;
if(!OpenPlayerFile(player, argv[i]))
continue;
/* 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);
if(!StartPlayer(player))
{
ClosePlayerFile(player);
continue;
}
while(UpdatePlayer(player))
Sleep(10);
/* All done with this file. Close it and go to the next */
ClosePlayerFile(player);
}
printf("Done.\n");
/* All files done. Delete the player, and close OpenAL */
DeletePlayer(player);
player = NULL;
CloseAL();
return 0;
}
@@ -0,0 +1,327 @@
/*
* OpenAL Helpers
*
* Copyright (c) 2011 by Chris Robinson <chris.kcat@gmail.com>
*
* 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 to help with some menial OpenAL-related tasks,
* such as opening a device and setting up a context, closing the device and
* destroying its context, converting between frame counts and byte lengths,
* finding an appropriate buffer format, and getting readable strings for
* channel configs and sample types. */
#include <stdio.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "alhelpers.h"
/* InitAL opens the default device and sets up a context using default
* attributes, making the program ready to call OpenAL functions. */
int InitAL(void)
{
ALCdevice *device;
ALCcontext *ctx;
/* Open and initialize a device with default settings */
device = alcOpenDevice(NULL);
if(!device)
{
fprintf(stderr, "Could not open a device!\n");
return 1;
}
ctx = alcCreateContext(device, NULL);
if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE)
{
if(ctx != NULL)
alcDestroyContext(ctx);
alcCloseDevice(device);
fprintf(stderr, "Could not set a context!\n");
return 1;
}
printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER));
return 0;
}
/* CloseAL closes the device belonging to the current context, and destroys the
* context. */
void CloseAL(void)
{
ALCdevice *device;
ALCcontext *ctx;
ctx = alcGetCurrentContext();
if(ctx == NULL)
return;
device = alcGetContextsDevice(ctx);
alcMakeContextCurrent(NULL);
alcDestroyContext(ctx);
alcCloseDevice(device);
}
/* GetFormat retrieves a compatible buffer format given the channel config and
* sample type. If an alIsBufferFormatSupportedSOFT-compatible function is
* provided, it will be called to find the closest-matching format from
* AL_SOFT_buffer_samples. Returns AL_NONE (0) if no supported format can be
* found. */
ALenum GetFormat(ALenum channels, ALenum type, LPALISBUFFERFORMATSUPPORTEDSOFT palIsBufferFormatSupportedSOFT)
{
ALenum format = AL_NONE;
/* If using AL_SOFT_buffer_samples, try looking through its formats */
if(palIsBufferFormatSupportedSOFT)
{
/* AL_SOFT_buffer_samples is more lenient with matching formats. The
* specified sample type does not need to match the returned format,
* but it is nice to try to get something close. */
if(type == AL_UNSIGNED_BYTE_SOFT || type == AL_BYTE_SOFT)
{
if(channels == AL_MONO_SOFT) format = AL_MONO8_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO8_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD8_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT;
}
else if(type == AL_UNSIGNED_SHORT_SOFT || type == AL_SHORT_SOFT)
{
if(channels == AL_MONO_SOFT) format = AL_MONO16_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO16_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD16_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT;
}
else if(type == AL_UNSIGNED_BYTE3_SOFT || type == AL_BYTE3_SOFT ||
type == AL_UNSIGNED_INT_SOFT || type == AL_INT_SOFT ||
type == AL_FLOAT_SOFT || type == AL_DOUBLE_SOFT)
{
if(channels == AL_MONO_SOFT) format = AL_MONO32F_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT;
}
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
format = AL_NONE;
/* A matching format was not found or supported. Try 32-bit float. */
if(format == AL_NONE)
{
if(channels == AL_MONO_SOFT) format = AL_MONO32F_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT;
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
format = AL_NONE;
}
/* 32-bit float not supported. Try 16-bit int. */
if(format == AL_NONE)
{
if(channels == AL_MONO_SOFT) format = AL_MONO16_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO16_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD16_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT;
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
format = AL_NONE;
}
/* 16-bit int not supported. Try 8-bit int. */
if(format == AL_NONE)
{
if(channels == AL_MONO_SOFT) format = AL_MONO8_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO8_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD8_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT;
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
format = AL_NONE;
}
return format;
}
/* We use the AL_EXT_MCFORMATS extension to provide output of Quad, 5.1,
* and 7.1 channel configs, AL_EXT_FLOAT32 for 32-bit float samples, and
* AL_EXT_DOUBLE for 64-bit float samples. */
if(type == AL_UNSIGNED_BYTE_SOFT)
{
if(channels == AL_MONO_SOFT)
format = AL_FORMAT_MONO8;
else if(channels == AL_STEREO_SOFT)
format = AL_FORMAT_STEREO8;
else if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if(channels == AL_QUAD_SOFT)
format = alGetEnumValue("AL_FORMAT_QUAD8");
else if(channels == AL_5POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_51CHN8");
else if(channels == AL_6POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_61CHN8");
else if(channels == AL_7POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_71CHN8");
}
}
else if(type == AL_SHORT_SOFT)
{
if(channels == AL_MONO_SOFT)
format = AL_FORMAT_MONO16;
else if(channels == AL_STEREO_SOFT)
format = AL_FORMAT_STEREO16;
else if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if(channels == AL_QUAD_SOFT)
format = alGetEnumValue("AL_FORMAT_QUAD16");
else if(channels == AL_5POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_51CHN16");
else if(channels == AL_6POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_61CHN16");
else if(channels == AL_7POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_71CHN16");
}
}
else if(type == AL_FLOAT_SOFT && alIsExtensionPresent("AL_EXT_FLOAT32"))
{
if(channels == AL_MONO_SOFT)
format = alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
else if(channels == AL_STEREO_SOFT)
format = alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
else if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if(channels == AL_QUAD_SOFT)
format = alGetEnumValue("AL_FORMAT_QUAD32");
else if(channels == AL_5POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_51CHN32");
else if(channels == AL_6POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_61CHN32");
else if(channels == AL_7POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_71CHN32");
}
}
else if(type == AL_DOUBLE_SOFT && alIsExtensionPresent("AL_EXT_DOUBLE"))
{
if(channels == AL_MONO_SOFT)
format = alGetEnumValue("AL_FORMAT_MONO_DOUBLE");
else if(channels == AL_STEREO_SOFT)
format = alGetEnumValue("AL_FORMAT_STEREO_DOUBLE");
}
/* NOTE: It seems OSX returns -1 from alGetEnumValue for unknown enums, as
* opposed to 0. Correct it. */
if(format == -1)
format = 0;
return format;
}
void AL_APIENTRY wrap_BufferSamples(ALuint buffer, ALuint samplerate,
ALenum internalformat, ALsizei samples,
ALenum channels, ALenum type,
const ALvoid *data)
{
alBufferData(buffer, internalformat, data,
FramesToBytes(samples, channels, type),
samplerate);
}
const char *ChannelsName(ALenum chans)
{
switch(chans)
{
case AL_MONO_SOFT: return "Mono";
case AL_STEREO_SOFT: return "Stereo";
case AL_REAR_SOFT: return "Rear";
case AL_QUAD_SOFT: return "Quadraphonic";
case AL_5POINT1_SOFT: return "5.1 Surround";
case AL_6POINT1_SOFT: return "6.1 Surround";
case AL_7POINT1_SOFT: return "7.1 Surround";
}
return "Unknown Channels";
}
const char *TypeName(ALenum type)
{
switch(type)
{
case AL_BYTE_SOFT: return "S8";
case AL_UNSIGNED_BYTE_SOFT: return "U8";
case AL_SHORT_SOFT: return "S16";
case AL_UNSIGNED_SHORT_SOFT: return "U16";
case AL_INT_SOFT: return "S32";
case AL_UNSIGNED_INT_SOFT: return "U32";
case AL_FLOAT_SOFT: return "Float32";
case AL_DOUBLE_SOFT: return "Float64";
}
return "Unknown Type";
}
ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type)
{
switch(channels)
{
case AL_MONO_SOFT: size *= 1; break;
case AL_STEREO_SOFT: size *= 2; break;
case AL_REAR_SOFT: size *= 2; break;
case AL_QUAD_SOFT: size *= 4; break;
case AL_5POINT1_SOFT: size *= 6; break;
case AL_6POINT1_SOFT: size *= 7; break;
case AL_7POINT1_SOFT: size *= 8; break;
}
switch(type)
{
case AL_BYTE_SOFT: size *= sizeof(ALbyte); break;
case AL_UNSIGNED_BYTE_SOFT: size *= sizeof(ALubyte); break;
case AL_SHORT_SOFT: size *= sizeof(ALshort); break;
case AL_UNSIGNED_SHORT_SOFT: size *= sizeof(ALushort); break;
case AL_INT_SOFT: size *= sizeof(ALint); break;
case AL_UNSIGNED_INT_SOFT: size *= sizeof(ALuint); break;
case AL_FLOAT_SOFT: size *= sizeof(ALfloat); break;
case AL_DOUBLE_SOFT: size *= sizeof(ALdouble); break;
}
return size;
}
ALsizei BytesToFrames(ALsizei size, ALenum channels, ALenum type)
{
return size / FramesToBytes(1, channels, type);
}
@@ -0,0 +1,51 @@
#ifndef ALHELPERS_H
#define ALHELPERS_H
#ifndef _WIN32
#include <unistd.h>
#define Sleep(x) usleep((x)*1000)
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "AL/alc.h"
#include "AL/al.h"
#include "AL/alext.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Some helper functions to get the name from the channel and type enums. */
const char *ChannelsName(ALenum chans);
const char *TypeName(ALenum type);
/* Helpers to convert frame counts and byte lengths. */
ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type);
ALsizei BytesToFrames(ALsizei size, ALenum channels, ALenum type);
/* Retrieves a compatible buffer format given the channel configuration and
* sample type. If an alIsBufferFormatSupportedSOFT-compatible function is
* provided, it will be called to find the closest-matching format from
* AL_SOFT_buffer_samples. Returns AL_NONE (0) if no supported format can be
* found. */
ALenum GetFormat(ALenum channels, ALenum type, LPALISBUFFERFORMATSUPPORTEDSOFT palIsBufferFormatSupportedSOFT);
/* Loads samples into a buffer using the standard alBufferData call, but with a
* LPALBUFFERSAMPLESSOFT-compatible prototype. Assumes internalformat is valid
* for alBufferData, and that channels and type match it. */
void AL_APIENTRY wrap_BufferSamples(ALuint buffer, ALuint samplerate,
ALenum internalformat, ALsizei samples,
ALenum channels, ALenum type,
const ALvoid *data);
/* Easy device init/deinit functions. InitAL returns 0 on success. */
int InitAL(void);
void CloseAL(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* ALHELPERS_H */
@@ -0,0 +1,164 @@
/*
* SDL_sound Decoder Helpers
*
* Copyright (c) 2013 by Chris Robinson <chris.kcat@gmail.com>
*
* 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;
}
@@ -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 */