Fix OpenAL backend

This commit is contained in:
UnknownShadow200 2021-07-30 07:11:28 +10:00
parent 50f2ec067c
commit 0b08bf77bd

View File

@ -109,9 +109,9 @@ static ALenum (APIENTRY *_alcGetError)(void* device);
struct AudioContext { struct AudioContext {
ALuint source; ALuint source;
ALuint buffers[AUDIO_MAX_BUFFERS]; ALuint buffers[AUDIO_MAX_BUFFERS];
cc_bool available[AUDIO_MAX_BUFFERS]; ALuint freeIDs[AUDIO_MAX_BUFFERS];
struct AudioFormat format; struct AudioFormat format;
int count; int count, free;
ALenum dataFormat; ALenum dataFormat;
}; };
static void* audio_device; static void* audio_device;
@ -182,19 +182,25 @@ static void Backend_Free(void) {
audio_device = NULL; audio_device = NULL;
} }
void Audio_Init(struct AudioContext* ctx, int buffers) { static void ClearFree(struct AudioContext* ctx) {
int i; int i;
_alDistanceModel(AL_NONE); for (i = 0; i < AUDIO_MAX_BUFFERS; i++) {
ctx->source = -1; ctx->freeIDs[i] = 0;
for (i = 0; i < buffers; i++) {
ctx->available[i] = true;
} }
ctx->free = 0;
}
void Audio_Init(struct AudioContext* ctx, int buffers) {
_alDistanceModel(AL_NONE);
ClearFree(ctx);
ctx->source = 0;
ctx->count = buffers; ctx->count = buffers;
} }
static cc_result Backend_Reset(struct AudioContext* ctx) { static cc_result Backend_Reset(struct AudioContext* ctx) {
ALenum err; ALenum err;
ClearFree(ctx);
if (!ctx->source) return 0; if (!ctx->source) return 0;
_alDeleteSources(1, &ctx->source); _alDeleteSources(1, &ctx->source);
@ -213,7 +219,7 @@ static ALenum GetALFormat(int channels) {
} }
static cc_result Backend_SetFormat(struct AudioContext* ctx, struct AudioFormat* format) { static cc_result Backend_SetFormat(struct AudioContext* ctx, struct AudioFormat* format) {
ALenum err; ALenum i, err;
ctx->dataFormat = GetALFormat(format->channels); ctx->dataFormat = GetALFormat(format->channels);
if (!ctx->source) { if (!ctx->source) {
@ -222,14 +228,21 @@ static cc_result Backend_SetFormat(struct AudioContext* ctx, struct AudioFormat*
_alGenBuffers(ctx->count, ctx->buffers); _alGenBuffers(ctx->count, ctx->buffers);
if ((err = _alGetError())) return err; if ((err = _alGetError())) return err;
for (i = 0; i < ctx->count; i++) {
ctx->freeIDs[i] = ctx->buffers[i];
}
ctx->free = ctx->count;
} }
return 0; return 0;
} }
cc_result Audio_QueueData(struct AudioContext* ctx, int idx, void* data, cc_uint32 size) { cc_result Audio_QueueData(struct AudioContext* ctx, void* data, cc_uint32 size) {
ALuint buffer = ctx->buffers[idx]; ALuint buffer;
ALenum err; ALenum err;
ctx->available[idx] = false;
if (!ctx->free) return ERR_INVALID_ARGUMENT;
buffer = ctx->freeIDs[--ctx->free];
_alBufferData(buffer, ctx->dataFormat, data, size, ctx->format.sampleRate); _alBufferData(buffer, ctx->dataFormat, data, size, ctx->format.sampleRate);
if ((err = _alGetError())) return err; if ((err = _alGetError())) return err;
@ -250,11 +263,12 @@ static void Backend_Stop(struct AudioContext* ctx) {
_alGetError(); _alGetError();
} }
cc_result Audio_PollQueue(struct AudioContext* ctx, int idx, cc_bool* available) { cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) {
ALint i, processed = 0; ALint processed = 0;
ALuint buffer; ALuint buffer;
ALenum err; ALenum err;
*inUse = 0;
_alGetSourcei(ctx->source, AL_BUFFERS_PROCESSED, &processed); _alGetSourcei(ctx->source, AL_BUFFERS_PROCESSED, &processed);
if ((err = _alGetError())) return err; if ((err = _alGetError())) return err;
@ -262,23 +276,9 @@ cc_result Audio_PollQueue(struct AudioContext* ctx, int idx, cc_bool* available)
_alSourceUnqueueBuffers(ctx->source, 1, &buffer); _alSourceUnqueueBuffers(ctx->source, 1, &buffer);
if ((err = _alGetError())) return err; if ((err = _alGetError())) return err;
for (i = 0; i < ctx->count; i++) { ctx->freeIDs[ctx->free++] = buffer;
if (ctx->buffers[i] == buffer) ctx->available[i] = true;
} }
} *inUse = ctx->count - ctx->free; return 0;
*available = ctx->available[idx]; return 0;
}
cc_result Audio_Poll(struct AudioContext* ctx, cc_bool* finished) {
ALint state = 0;
cc_result res;
if (ctx->source == -1) { *finished = true; return 0; }
res = Audio_PollQueue(ctx, finished);
if (res) return res;
_alGetSourcei(ctx->source, AL_SOURCE_STATE, &state);
*finished = state != AL_PLAYING; return 0;
} }
#elif defined CC_BUILD_WINMM #elif defined CC_BUILD_WINMM
/*########################################################################################################################* /*########################################################################################################################*
@ -922,7 +922,7 @@ static void* music_waitable;
static volatile cc_bool music_pendingStop, music_joining; static volatile cc_bool music_pendingStop, music_joining;
static int music_minDelay, music_maxDelay; static int music_minDelay, music_maxDelay;
static cc_result Music_Buffer(int i, cc_int16* data, int maxSamples, struct VorbisState* ctx) { static cc_result Music_Buffer(cc_int16* data, int maxSamples, struct VorbisState* ctx) {
int samples = 0; int samples = 0;
cc_int16* cur; cc_int16* cur;
cc_result res = 0, res2; cc_result res = 0, res2;
@ -952,7 +952,7 @@ static cc_result Music_PlayOgg(struct Stream* source) {
int chunkSize, samplesPerSecond; int chunkSize, samplesPerSecond;
cc_int16* data = NULL; cc_int16* data = NULL;
int inUse, i, next; int inUse, i, cur;
cc_result res; cc_result res;
Ogg_Init(&ogg, source); Ogg_Init(&ogg, source);
@ -968,39 +968,44 @@ static cc_result Music_PlayOgg(struct Stream* source) {
chunkSize = fmt.channels * (fmt.sampleRate + vorbis.blockSizes[1]); chunkSize = fmt.channels * (fmt.sampleRate + vorbis.blockSizes[1]);
samplesPerSecond = fmt.channels * fmt.sampleRate; samplesPerSecond = fmt.channels * fmt.sampleRate;
cur = 0;
data = (cc_int16*)Mem_TryAlloc(chunkSize * AUDIO_MAX_BUFFERS, 2); data = (cc_int16*)Mem_TryAlloc(chunkSize * AUDIO_MAX_BUFFERS, 2);
if (!data) { res = ERR_OUT_OF_MEMORY; goto cleanup; } if (!data) { res = ERR_OUT_OF_MEMORY; goto cleanup; }
/* fill up with some samples before playing */ /* fill up with some samples before playing */
for (i = 0; i < AUDIO_MAX_BUFFERS && !res; i++) { for (i = 0; i < AUDIO_MAX_BUFFERS && !res; i++) {
res = Music_Buffer(i, &data[chunkSize * i], samplesPerSecond, &vorbis); res = Music_Buffer(&data[chunkSize * cur], samplesPerSecond, &vorbis);
cur = (cur + 1) % AUDIO_MAX_BUFFERS;
} }
if (music_pendingStop) goto cleanup; if (music_pendingStop) goto cleanup;
res = Audio_Play(&music_ctx); res = Audio_Play(&music_ctx);
if (res) goto cleanup; if (res) goto cleanup;
for (next = 0; ;) { while (!music_pendingStop) {
res = Audio_Poll(&music_ctx, &inUse); res = Audio_Poll(&music_ctx, &inUse);
if (res) { music_pendingStop = true; break; } if (res) { music_pendingStop = true; break; }
if (music_pendingStop) break;
if (inUse >= AUDIO_MAX_BUFFERS) { if (inUse >= AUDIO_MAX_BUFFERS) {
Thread_Sleep(10); continue; Thread_Sleep(10); continue;
} }
res = Music_Buffer(next, &data[chunkSize * next], samplesPerSecond, &vorbis); res = Music_Buffer(&data[chunkSize * cur], samplesPerSecond, &vorbis);
next = (next + 1) % AUDIO_MAX_BUFFERS; cur = (cur + 1) % AUDIO_MAX_BUFFERS;
/* need to specially handle last bit of audio */ /* need to specially handle last bit of audio */
if (res) break; if (res) break;
} }
if (music_pendingStop) {
Audio_Close(&music_ctx);
} else {
/* Wait until the buffers finished playing */ /* Wait until the buffers finished playing */
while (!music_pendingStop) { while (!music_pendingStop) {
if (Audio_Poll(&music_ctx, &inUse) || inUse > 0) break; if (Audio_Poll(&music_ctx, &inUse) || inUse > 0) break;
Thread_Sleep(10); Thread_Sleep(10);
} }
}
cleanup: cleanup:
Mem_Free(data); Mem_Free(data);