Latest compatible version of Classicube from the original GitHub repository (https://github.com/ClassiCube/ClassiCube) that can be compiled on Classicube for PowerMac PPC running Mac OS X 10.4.
This commit is contained in:
327
src/Audio_OpenAL.c
Normal file
327
src/Audio_OpenAL.c
Normal file
@@ -0,0 +1,327 @@
|
||||
#include "Core.h"
|
||||
|
||||
#if CC_AUD_BACKEND == CC_AUD_BACKEND_OPENAL
|
||||
/* Simpler to just include subset of OpenAL actually use here instead of including */
|
||||
|
||||
/* === BEGIN OPENAL HEADERS === */
|
||||
#if defined CC_BUILD_WIN
|
||||
#define APIENTRY __cdecl
|
||||
#else
|
||||
#define APIENTRY
|
||||
#endif
|
||||
|
||||
#define AL_NONE 0
|
||||
#define AL_GAIN 0x100A
|
||||
#define AL_SOURCE_STATE 0x1010
|
||||
#define AL_PLAYING 0x1012
|
||||
#define AL_BUFFERS_QUEUED 0x1015
|
||||
#define AL_BUFFERS_PROCESSED 0x1016
|
||||
#define AL_FORMAT_MONO16 0x1101
|
||||
#define AL_FORMAT_STEREO16 0x1103
|
||||
|
||||
#define AL_INVALID_NAME 0xA001
|
||||
#define AL_INVALID_ENUM 0xA002
|
||||
#define AL_INVALID_VALUE 0xA003
|
||||
#define AL_INVALID_OPERATION 0xA004
|
||||
#define AL_OUT_OF_MEMORY 0xA005
|
||||
|
||||
typedef char ALboolean;
|
||||
typedef int ALint;
|
||||
typedef unsigned int ALuint;
|
||||
typedef int ALsizei;
|
||||
typedef int ALenum;
|
||||
|
||||
/* Apologies for the ugly dynamic symbol definitions here */
|
||||
static ALenum (APIENTRY *_alGetError)(void);
|
||||
static void (APIENTRY *_alGenSources)(ALsizei n, ALuint* sources);
|
||||
static void (APIENTRY *_alDeleteSources)(ALsizei n, const ALuint* sources);
|
||||
static void (APIENTRY *_alGetSourcei)(ALuint source, ALenum param, ALint* value);
|
||||
static void (APIENTRY *_alSourcef)(ALuint source, ALenum param, float value);
|
||||
static void (APIENTRY *_alSourcePlay) (ALuint source);
|
||||
static void (APIENTRY *_alSourcePause)(ALuint source);
|
||||
static void (APIENTRY *_alSourceStop) (ALuint source);
|
||||
static void (APIENTRY *_alSourceQueueBuffers)(ALuint source, ALsizei nb, const ALuint* buffers);
|
||||
static void (APIENTRY *_alSourceUnqueueBuffers)(ALuint source, ALsizei nb, ALuint* buffers);
|
||||
static void (APIENTRY *_alGenBuffers)(ALsizei n, ALuint* buffers);
|
||||
static void (APIENTRY *_alDeleteBuffers)(ALsizei n, const ALuint* buffers);
|
||||
static void (APIENTRY *_alBufferData)(ALuint buffer, ALenum format, const void* data, ALsizei size, ALsizei freq);
|
||||
|
||||
static void (APIENTRY *_alDistanceModel)(ALenum distanceModel);
|
||||
static void* (APIENTRY *_alcCreateContext)(void* device, const ALint* attrlist);
|
||||
static ALboolean (APIENTRY *_alcMakeContextCurrent)(void* context);
|
||||
static void (APIENTRY *_alcDestroyContext)(void* context);
|
||||
static void* (APIENTRY *_alcOpenDevice)(const char* devicename);
|
||||
static ALboolean (APIENTRY *_alcCloseDevice)(void* device);
|
||||
static ALenum (APIENTRY *_alcGetError)(void* device);
|
||||
/* === END OPENAL HEADERS === */
|
||||
|
||||
#include "Audio.h"
|
||||
struct AudioContext {
|
||||
ALuint source;
|
||||
ALuint buffers[AUDIO_MAX_BUFFERS];
|
||||
ALuint freeIDs[AUDIO_MAX_BUFFERS];
|
||||
int count, free, sampleRate;
|
||||
ALenum format;
|
||||
};
|
||||
|
||||
#define AUDIO_COMMON_ALLOC
|
||||
#include "_AudioBase.h"
|
||||
#include "Funcs.h"
|
||||
|
||||
static void* audio_device;
|
||||
static void* audio_context;
|
||||
|
||||
#if defined CC_BUILD_WIN
|
||||
static const cc_string alLib = String_FromConst("openal32.dll");
|
||||
#elif defined CC_BUILD_MACOS
|
||||
static const cc_string alLib = String_FromConst("/System/Library/Frameworks/OpenAL.framework/Versions/A/OpenAL");
|
||||
#elif defined CC_BUILD_IOS
|
||||
static const cc_string alLib = String_FromConst("/System/Library/Frameworks/OpenAL.framework/OpenAL");
|
||||
#elif defined CC_BUILD_NETBSD
|
||||
static const cc_string alLib = String_FromConst("/usr/pkg/lib/libopenal.so");
|
||||
#elif defined CC_BUILD_BSD
|
||||
static const cc_string alLib = String_FromConst("libopenal.so");
|
||||
#else
|
||||
static const cc_string alLib = String_FromConst("libopenal.so.1");
|
||||
#endif
|
||||
|
||||
static cc_bool LoadALFuncs(void) {
|
||||
static const struct DynamicLibSym funcs[] = {
|
||||
DynamicLib_ReqSym(alcCreateContext), DynamicLib_ReqSym(alcMakeContextCurrent),
|
||||
DynamicLib_ReqSym(alcDestroyContext), DynamicLib_ReqSym(alcOpenDevice),
|
||||
DynamicLib_ReqSym(alcCloseDevice), DynamicLib_ReqSym(alcGetError),
|
||||
|
||||
DynamicLib_ReqSym(alGetError),
|
||||
DynamicLib_ReqSym(alGenSources), DynamicLib_ReqSym(alDeleteSources),
|
||||
DynamicLib_ReqSym(alGetSourcei), DynamicLib_ReqSym(alSourcef),
|
||||
DynamicLib_ReqSym(alSourcePlay), DynamicLib_ReqSym(alSourceStop),
|
||||
DynamicLib_ReqSym(alSourceQueueBuffers), DynamicLib_ReqSym(alSourceUnqueueBuffers),
|
||||
DynamicLib_ReqSym(alGenBuffers), DynamicLib_ReqSym(alDeleteBuffers),
|
||||
DynamicLib_ReqSym(alBufferData), DynamicLib_ReqSym(alDistanceModel),
|
||||
DynamicLib_OptSym(alSourcePlay)
|
||||
};
|
||||
void* lib;
|
||||
|
||||
return DynamicLib_LoadAll(&alLib, funcs, Array_Elems(funcs), &lib);
|
||||
}
|
||||
|
||||
static cc_result CreateALContext(void) {
|
||||
ALenum err;
|
||||
audio_device = _alcOpenDevice(NULL);
|
||||
if ((err = _alcGetError(audio_device))) return err;
|
||||
if (!audio_device) return AL_ERR_INIT_DEVICE;
|
||||
|
||||
audio_context = _alcCreateContext(audio_device, NULL);
|
||||
if ((err = _alcGetError(audio_device))) return err;
|
||||
if (!audio_context) return AL_ERR_INIT_CONTEXT;
|
||||
|
||||
_alcMakeContextCurrent(audio_context);
|
||||
return _alcGetError(audio_device);
|
||||
}
|
||||
|
||||
cc_bool AudioBackend_Init(void) {
|
||||
static const cc_string msg = String_FromConst("Failed to init OpenAL. No audio will play.");
|
||||
cc_result res;
|
||||
if (audio_device) return true;
|
||||
if (!LoadALFuncs()) { Logger_WarnFunc(&msg); return false; }
|
||||
|
||||
res = CreateALContext();
|
||||
if (res) { Audio_Warn(res, "initing OpenAL"); return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioBackend_Tick(void) { }
|
||||
|
||||
void AudioBackend_Free(void) {
|
||||
if (!audio_device) return;
|
||||
_alcMakeContextCurrent(NULL);
|
||||
|
||||
if (audio_context) _alcDestroyContext(audio_context);
|
||||
if (audio_device) _alcCloseDevice(audio_device);
|
||||
|
||||
audio_context = NULL;
|
||||
audio_device = NULL;
|
||||
}
|
||||
|
||||
cc_result Audio_Init(struct AudioContext* ctx, int buffers) {
|
||||
ALenum i, err;
|
||||
_alDistanceModel(AL_NONE);
|
||||
ctx->source = 0;
|
||||
ctx->count = buffers;
|
||||
|
||||
_alGetError(); /* Reset error state */
|
||||
_alGenSources(1, &ctx->source);
|
||||
if ((err = _alGetError())) return err;
|
||||
|
||||
_alGenBuffers(buffers, ctx->buffers);
|
||||
if ((err = _alGetError())) return err;
|
||||
|
||||
for (i = 0; i < buffers; i++) {
|
||||
ctx->freeIDs[i] = ctx->buffers[i];
|
||||
}
|
||||
ctx->free = buffers;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Audio_Stop(struct AudioContext* ctx) {
|
||||
_alSourceStop(ctx->source);
|
||||
}
|
||||
|
||||
static void Audio_Reset(struct AudioContext* ctx) {
|
||||
_alDeleteSources(1, &ctx->source);
|
||||
_alDeleteBuffers(ctx->count, ctx->buffers);
|
||||
ctx->source = 0;
|
||||
}
|
||||
|
||||
static void ClearFree(struct AudioContext* ctx) {
|
||||
int i;
|
||||
for (i = 0; i < AUDIO_MAX_BUFFERS; i++) {
|
||||
ctx->freeIDs[i] = 0;
|
||||
}
|
||||
ctx->free = 0;
|
||||
}
|
||||
|
||||
void Audio_Close(struct AudioContext* ctx) {
|
||||
if (ctx->source) {
|
||||
Audio_Stop(ctx);
|
||||
Audio_Reset(ctx);
|
||||
_alGetError(); /* Reset error state */
|
||||
}
|
||||
ClearFree(ctx);
|
||||
ctx->count = 0;
|
||||
}
|
||||
|
||||
cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) {
|
||||
ctx->sampleRate = Audio_AdjustSampleRate(sampleRate, playbackRate);
|
||||
|
||||
if (channels == 1) {
|
||||
ctx->format = AL_FORMAT_MONO16;
|
||||
} else if (channels == 2) {
|
||||
ctx->format = AL_FORMAT_STEREO16;
|
||||
} else {
|
||||
return ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Audio_SetVolume(struct AudioContext* ctx, int volume) {
|
||||
_alSourcef(ctx->source, AL_GAIN, volume / 100.0f);
|
||||
_alGetError(); /* Reset error state */
|
||||
}
|
||||
|
||||
cc_result Audio_QueueChunk(struct AudioContext* ctx, struct AudioChunk* chunk) {
|
||||
ALuint buffer;
|
||||
ALenum err;
|
||||
|
||||
if (!ctx->free) return ERR_INVALID_ARGUMENT;
|
||||
buffer = ctx->freeIDs[--ctx->free];
|
||||
_alGetError(); /* Reset error state */
|
||||
|
||||
_alBufferData(buffer, ctx->format, chunk->data, chunk->size, ctx->sampleRate);
|
||||
if ((err = _alGetError())) return err;
|
||||
_alSourceQueueBuffers(ctx->source, 1, &buffer);
|
||||
if ((err = _alGetError())) return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) {
|
||||
ALint processed = 0;
|
||||
ALuint buffer;
|
||||
ALenum err;
|
||||
|
||||
*inUse = 0;
|
||||
if (!ctx->source) return 0;
|
||||
|
||||
_alGetError(); /* Reset error state */
|
||||
_alGetSourcei(ctx->source, AL_BUFFERS_PROCESSED, &processed);
|
||||
if ((err = _alGetError())) return err;
|
||||
|
||||
if (processed > 0) {
|
||||
_alSourceUnqueueBuffers(ctx->source, 1, &buffer);
|
||||
if ((err = _alGetError())) return err;
|
||||
|
||||
ctx->freeIDs[ctx->free++] = buffer;
|
||||
}
|
||||
*inUse = ctx->count - ctx->free; return 0;
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*------------------------------------------------------Stream context-----------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
cc_result StreamContext_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) {
|
||||
return Audio_SetFormat(ctx, channels, sampleRate, playbackRate);
|
||||
}
|
||||
|
||||
cc_result StreamContext_Enqueue(struct AudioContext* ctx, struct AudioChunk* chunk) {
|
||||
return Audio_QueueChunk(ctx, chunk);
|
||||
}
|
||||
|
||||
cc_result StreamContext_Play(struct AudioContext* ctx) {
|
||||
_alSourcePlay(ctx->source);
|
||||
return _alGetError();
|
||||
}
|
||||
|
||||
cc_result StreamContext_Pause(struct AudioContext* ctx) {
|
||||
if (!_alSourcePause) return ERR_NOT_SUPPORTED;
|
||||
|
||||
_alSourcePause(ctx->source);
|
||||
return _alGetError();
|
||||
}
|
||||
|
||||
cc_result StreamContext_Update(struct AudioContext* ctx, int* inUse) {
|
||||
return Audio_Poll(ctx, inUse);
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*------------------------------------------------------Sound context------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
cc_bool SoundContext_FastPlay(struct AudioContext* ctx, struct AudioData* data) {
|
||||
/* Channels/Sample rate is per buffer, not a per source property */
|
||||
return true;
|
||||
}
|
||||
|
||||
cc_result SoundContext_PlayData(struct AudioContext* ctx, struct AudioData* data) {
|
||||
cc_result res;
|
||||
|
||||
if ((res = Audio_SetFormat(ctx, data->channels, data->sampleRate, data->rate))) return res;
|
||||
if ((res = Audio_QueueChunk(ctx, &data->chunk))) return res;
|
||||
_alSourcePlay(ctx->source);
|
||||
|
||||
return _alGetError();
|
||||
}
|
||||
|
||||
cc_result SoundContext_PollBusy(struct AudioContext* ctx, cc_bool* isBusy) {
|
||||
int inUse = 1;
|
||||
cc_result res;
|
||||
if ((res = Audio_Poll(ctx, &inUse))) return res;
|
||||
|
||||
*isBusy = inUse > 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*--------------------------------------------------------Audio misc-------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
static const char* GetError(cc_result res) {
|
||||
switch (res) {
|
||||
case AL_ERR_INIT_CONTEXT: return "Failed to init OpenAL context";
|
||||
case AL_ERR_INIT_DEVICE: return "Failed to init OpenAL device";
|
||||
case AL_INVALID_NAME: return "Invalid parameter name";
|
||||
case AL_INVALID_ENUM: return "Invalid parameter";
|
||||
case AL_INVALID_VALUE: return "Invalid parameter value";
|
||||
case AL_INVALID_OPERATION: return "Invalid operation";
|
||||
case AL_OUT_OF_MEMORY: return "OpenAL out of memory";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cc_bool Audio_DescribeError(cc_result res, cc_string* dst) {
|
||||
const char* err = GetError(res);
|
||||
if (err) String_AppendConst(dst, err);
|
||||
return err != NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user