mirror of
https://github.com/love2d/megasource.git
synced 2026-08-17 19:24:09 +02:00
Add OpenAL-Soft 1.15.1.
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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>
|
||||
#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"
|
||||
|
||||
|
||||
LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples;
|
||||
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;
|
||||
|
||||
/* 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 audiofile;
|
||||
StreamPtr sound;
|
||||
|
||||
/* Open the file and get the first stream from it */
|
||||
audiofile = openAVFile(filename);
|
||||
sound = getAVAudioStream(audiofile, 0);
|
||||
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)
|
||||
{
|
||||
fprintf(stderr, "Error getting audio info for %s\n", filename);
|
||||
closeAVFile(audiofile);
|
||||
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);
|
||||
closeAVFile(audiofile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode the whole audio stream to a buffer. */
|
||||
data = decodeAVAudioStream(sound, &datalen);
|
||||
if(!data)
|
||||
{
|
||||
fprintf(stderr, "Failed to read audio from %s\n", filename);
|
||||
closeAVFile(audiofile);
|
||||
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);
|
||||
closeAVFile(audiofile);
|
||||
|
||||
/* 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,337 @@
|
||||
/*
|
||||
* 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>
|
||||
#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 "AL/efx-presets.h"
|
||||
|
||||
#include "common/alhelpers.h"
|
||||
#include "common/alffmpeg.h"
|
||||
|
||||
|
||||
LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples;
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
|
||||
|
||||
/* 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 audiofile;
|
||||
StreamPtr sound;
|
||||
|
||||
/* Open the file and get the first stream from it */
|
||||
audiofile = openAVFile(filename);
|
||||
sound = getAVAudioStream(audiofile, 0);
|
||||
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)
|
||||
{
|
||||
fprintf(stderr, "Error getting audio info for %s\n", filename);
|
||||
closeAVFile(audiofile);
|
||||
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);
|
||||
closeAVFile(audiofile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode the whole audio stream to a buffer. */
|
||||
data = decodeAVAudioStream(sound, &datalen);
|
||||
if(!data)
|
||||
{
|
||||
fprintf(stderr, "Failed to read audio from %s\n", filename);
|
||||
closeAVFile(audiofile);
|
||||
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);
|
||||
closeAVFile(audiofile);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* 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/alffmpeg.h"
|
||||
|
||||
|
||||
LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples;
|
||||
LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT = NULL;
|
||||
|
||||
|
||||
/* 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 NUM_BUFFERS 4
|
||||
#define BUFFER_SIZE 8192
|
||||
|
||||
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 */
|
||||
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;
|
||||
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 = openAVFile(filename);
|
||||
player->stream = getAVAudioStream(player->file, 0);
|
||||
if(!player->stream)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
player->file = NULL;
|
||||
player->stream = NULL;
|
||||
player->datasize = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Closes the audio file stream */
|
||||
static void ClosePlayerFile(StreamPlayer *player)
|
||||
{
|
||||
closeAVFile(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;
|
||||
|
||||
/* 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++)
|
||||
{
|
||||
/* Get some data to give it to the buffer */
|
||||
got = readAVAudioData(player->stream, player->data, player->datasize);
|
||||
if(got == 0) break;
|
||||
|
||||
alBufferSamplesSOFT(player->buffers[i], player->rate, player->format,
|
||||
BytesToFrames(got, player->channels, player->type),
|
||||
player->channels, player->type, player->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;
|
||||
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 */
|
||||
got = readAVAudioData(player->stream, player->data, player->datasize);
|
||||
if(got > 0)
|
||||
{
|
||||
alBufferSamplesSOFT(bufid, player->rate, player->format,
|
||||
BytesToFrames(got, player->channels, player->type),
|
||||
player->channels, player->type, player->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++)
|
||||
{
|
||||
if(!OpenPlayerFile(player, argv[i]))
|
||||
continue;
|
||||
|
||||
printf("Playing %s (%s, %s, %dhz)\n", argv[i],
|
||||
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,638 @@
|
||||
/*
|
||||
* FFmpeg Decoder 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 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;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#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 */
|
||||
@@ -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,47 @@
|
||||
#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
|
||||
|
||||
#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 */
|
||||
Reference in New Issue
Block a user