Add start/stop audio api, fix delete buffer bug

On intermittent stop of OpenAL playback deleting the buffers without
unqueuing the current buffers is an invalid operation, causing invalid
audio states on alGetSource().
This commit is contained in:
Doyle Thai 2016-07-28 01:55:21 +10:00
parent 630f2eaa5d
commit 09972ced6e
4 changed files with 97 additions and 28 deletions

View File

@ -99,6 +99,7 @@ const i32 audio_init(AudioManager *audioManager)
for (i32 i = 0; i < ARRAY_COUNT(audioManager->sourceList); i++) for (i32 i = 0; i < ARRAY_COUNT(audioManager->sourceList); i++)
{ {
alGenSources(1, &audioManager->sourceList[i].id); alGenSources(1, &audioManager->sourceList[i].id);
AL_CHECK_ERROR();
// NOTE(doyle): If last entry, loop the free source to front of list // NOTE(doyle): If last entry, loop the free source to front of list
if (i + 1 >= ARRAY_COUNT(audioManager->sourceList)) if (i + 1 >= ARRAY_COUNT(audioManager->sourceList))
@ -111,10 +112,24 @@ const i32 audio_init(AudioManager *audioManager)
return 0; return 0;
} }
INTERNAL inline u32 getSourceId(AudioManager *audioManager,
AudioRenderer *audioRenderer)
{
u32 result = audioManager->sourceList[audioRenderer->sourceIndex].id;
return result;
}
INTERNAL i32 rendererAcquire(AudioManager *audioManager, INTERNAL i32 rendererAcquire(AudioManager *audioManager,
AudioRenderer *audioRenderer) AudioRenderer *audioRenderer)
{ {
i32 vacantSource = audioManager->freeSourceIndex; i32 vacantSource = audioManager->freeSourceIndex;
#ifdef DENGINE_DEBUG
ASSERT(audioManager && audioRenderer);
ASSERT(vacantSource >= 0);
#endif
if (audioManager->sourceList[vacantSource].nextFreeIndex == if (audioManager->sourceList[vacantSource].nextFreeIndex ==
AUDIO_NO_FREE_SOURCE) AUDIO_NO_FREE_SOURCE)
{ {
@ -144,17 +159,35 @@ INTERNAL i32 rendererAcquire(AudioManager *audioManager,
INTERNAL void rendererRelease(AudioManager *audioManager, INTERNAL void rendererRelease(AudioManager *audioManager,
AudioRenderer *audioRenderer) AudioRenderer *audioRenderer)
{ {
u32 alSourceId = getSourceId(audioManager, audioRenderer);
alSourceUnqueueBuffers(alSourceId, ARRAY_COUNT(audioRenderer->bufferId),
audioRenderer->bufferId);
AL_CHECK_ERROR();
alDeleteBuffers(ARRAY_COUNT(audioRenderer->bufferId),
audioRenderer->bufferId);
AL_CHECK_ERROR();
for (i32 i = 0; i < ARRAY_COUNT(audioRenderer->bufferId); i++)
{
audioRenderer->bufferId[i] = 0;
}
audioRenderer->audio = NULL;
audioRenderer->numPlays = 0;
u32 sourceIndexToFree = audioRenderer->sourceIndex; u32 sourceIndexToFree = audioRenderer->sourceIndex;
audioRenderer->sourceIndex = AUDIO_SOURCE_UNASSIGNED;
audioManager->sourceList[sourceIndexToFree].nextFreeIndex = audioManager->sourceList[sourceIndexToFree].nextFreeIndex =
audioManager->freeSourceIndex; audioManager->freeSourceIndex;
audioManager->freeSourceIndex = sourceIndexToFree; audioManager->freeSourceIndex = sourceIndexToFree;
audioRenderer->sourceIndex = AUDIO_SOURCE_UNASSIGNED;
} }
#define AUDIO_CHUNK_SIZE_ 65536 #define AUDIO_CHUNK_SIZE_ 65536
void audio_beginVorbisStream(AudioManager *audioManager, void audio_streamPlayVorbis(AudioManager *audioManager,
AudioRenderer *audioRenderer, AudioVorbis *vorbis, AudioRenderer *audioRenderer, AudioVorbis *vorbis,
i32 numPlays) i32 numPlays)
{ {
@ -162,14 +195,14 @@ void audio_beginVorbisStream(AudioManager *audioManager,
ASSERT(audioManager && audioRenderer && vorbis); ASSERT(audioManager && audioRenderer && vorbis);
if (numPlays != AUDIO_REPEAT_INFINITE && numPlays <= 0) if (numPlays != AUDIO_REPEAT_INFINITE && numPlays <= 0)
{ {
DEBUG_LOG("audio_beginVorbisStream() warning: Number of plays is less than 0"); DEBUG_LOG("audio_streamPlayVorbis() warning: Number of plays is less than 0");
} }
#endif #endif
i32 result = rendererAcquire(audioManager, audioRenderer); i32 result = rendererAcquire(audioManager, audioRenderer);
if (result) if (result)
{ {
DEBUG_LOG("audio_beginVorbisStream() failed: Could not acquire renderer"); DEBUG_LOG("audio_streamPlayVorbis() failed: Could not acquire renderer");
return; return;
} }
@ -181,7 +214,7 @@ void audio_beginVorbisStream(AudioManager *audioManager,
{ {
#ifdef DENGINE_DEBUG #ifdef DENGINE_DEBUG
DEBUG_LOG( DEBUG_LOG(
"audio_beginVorbisStream() warning: Unaccounted channel format"); "audio_streamPlayVorbis() warning: Unaccounted channel format");
ASSERT(INVALID_CODE_PATH); ASSERT(INVALID_CODE_PATH);
#endif #endif
} }
@ -190,17 +223,20 @@ void audio_beginVorbisStream(AudioManager *audioManager,
audioRenderer->numPlays = numPlays; audioRenderer->numPlays = numPlays;
} }
void audio_streamStopVorbis(AudioManager *audioManager,
AudioRenderer *audioRenderer)
{
u32 alSourceId = getSourceId(audioManager, audioRenderer);
alSourceStop(alSourceId);
AL_CHECK_ERROR();
rendererRelease(audioManager, audioRenderer);
}
void audio_updateAndPlay(AudioManager *audioManager, void audio_updateAndPlay(AudioManager *audioManager,
AudioRenderer *audioRenderer) AudioRenderer *audioRenderer)
{ {
AudioVorbis *audio = audioRenderer->audio; AudioVorbis *audio = audioRenderer->audio;
if (!audio) if (!audio) return;
{
#ifdef DENGINE_DEBUG
DEBUG_LOG("audio_updateAndPlay() early exit: No audio stream connected");
#endif
return;
}
if (audioRenderer->numPlays != AUDIO_REPEAT_INFINITE && if (audioRenderer->numPlays != AUDIO_REPEAT_INFINITE &&
audioRenderer->numPlays <= 0) audioRenderer->numPlays <= 0)
@ -209,27 +245,29 @@ void audio_updateAndPlay(AudioManager *audioManager,
return; return;
} }
u32 alSourceId = audioManager->sourceList[audioRenderer->sourceIndex].id; u32 alSourceId = getSourceId(audioManager, audioRenderer);
ALint audioState; ALint audioState;
alGetSourcei(alSourceId, AL_SOURCE_STATE, &audioState); alGetSourcei(alSourceId, AL_SOURCE_STATE, &audioState);
AL_CHECK_ERROR();
if (audioState == AL_STOPPED || audioState == AL_INITIAL) if (audioState == AL_STOPPED || audioState == AL_INITIAL)
{ {
// TODO(doyle): Delete and recreate fixes clicking when reusing buffers
if (audioState == AL_STOPPED) if (audioState == AL_STOPPED)
{ {
if (audioRenderer->numPlays != AUDIO_REPEAT_INFINITE) if (audioRenderer->numPlays != AUDIO_REPEAT_INFINITE)
audioRenderer->numPlays--; audioRenderer->numPlays--;
alDeleteBuffers(ARRAY_COUNT(audioRenderer->bufferId),
audioRenderer->bufferId);
if (audioRenderer->numPlays == AUDIO_REPEAT_INFINITE || if (audioRenderer->numPlays == AUDIO_REPEAT_INFINITE ||
audioRenderer->numPlays > 0) audioRenderer->numPlays > 0)
{ {
// TODO(doyle): Delete and recreate fixes clicking when reusing
// buffers
alDeleteBuffers(ARRAY_COUNT(audioRenderer->bufferId),
audioRenderer->bufferId);
AL_CHECK_ERROR();
alGenBuffers(ARRAY_COUNT(audioRenderer->bufferId), alGenBuffers(ARRAY_COUNT(audioRenderer->bufferId),
audioRenderer->bufferId); audioRenderer->bufferId);
AL_CHECK_ERROR();
} }
else else
{ {
@ -251,23 +289,28 @@ void audio_updateAndPlay(AudioManager *audioManager,
alBufferData(audioRenderer->bufferId[i], audioRenderer->format, alBufferData(audioRenderer->bufferId[i], audioRenderer->format,
audioChunk, AUDIO_CHUNK_SIZE_ * sizeof(i16), audioChunk, AUDIO_CHUNK_SIZE_ * sizeof(i16),
audio->info.sample_rate); audio->info.sample_rate);
AL_CHECK_ERROR();
} }
alSourceQueueBuffers(alSourceId, ARRAY_COUNT(audioRenderer->bufferId), alSourceQueueBuffers(alSourceId, ARRAY_COUNT(audioRenderer->bufferId),
audioRenderer->bufferId); audioRenderer->bufferId);
AL_CHECK_ERROR();
alSourcePlay(alSourceId); alSourcePlay(alSourceId);
AL_CHECK_ERROR();
} }
else if (audioState == AL_PLAYING) else if (audioState == AL_PLAYING)
{ {
ALint numProcessedBuffers; ALint numProcessedBuffers;
alGetSourcei(alSourceId, AL_BUFFERS_PROCESSED, alGetSourcei(alSourceId, AL_BUFFERS_PROCESSED,
&numProcessedBuffers); &numProcessedBuffers);
AL_CHECK_ERROR();
if (numProcessedBuffers > 0) if (numProcessedBuffers > 0)
{ {
ALint numBuffersToUnqueue = 1; ALint numBuffersToUnqueue = 1;
ALuint emptyBufferId; ALuint emptyBufferId;
alSourceUnqueueBuffers(alSourceId, numBuffersToUnqueue, alSourceUnqueueBuffers(alSourceId, numBuffersToUnqueue,
&emptyBufferId); &emptyBufferId);
AL_CHECK_ERROR();
i16 audioChunk[AUDIO_CHUNK_SIZE_] = {0}; i16 audioChunk[AUDIO_CHUNK_SIZE_] = {0};
i32 sampleCount = stb_vorbis_get_samples_short_interleaved( i32 sampleCount = stb_vorbis_get_samples_short_interleaved(
@ -280,7 +323,9 @@ void audio_updateAndPlay(AudioManager *audioManager,
alBufferData(emptyBufferId, audioRenderer->format, audioChunk, alBufferData(emptyBufferId, audioRenderer->format, audioChunk,
sampleCount * audio->info.channels * sizeof(i16), sampleCount * audio->info.channels * sizeof(i16),
audio->info.sample_rate); audio->info.sample_rate);
AL_CHECK_ERROR();
alSourceQueueBuffers(alSourceId, 1, &emptyBufferId); alSourceQueueBuffers(alSourceId, 1, &emptyBufferId);
AL_CHECK_ERROR();
} }
} }
} }

View File

@ -365,9 +365,11 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
Entity *soundscape = Entity *soundscape =
addEntity(arena, world, pos, size, type, dir, tex, collides); addEntity(arena, world, pos, size, type, dir, tex, collides);
world->soundscape = soundscape;
soundscape->audio = PLATFORM_MEM_ALLOC(arena, 1, AudioRenderer); soundscape->audio = PLATFORM_MEM_ALLOC(arena, 1, AudioRenderer);
soundscape->audio->sourceIndex = AUDIO_SOURCE_UNASSIGNED; soundscape->audio->sourceIndex = AUDIO_SOURCE_UNASSIGNED;
audio_beginVorbisStream(&state->audioManager, soundscape->audio, audio_streamPlayVorbis(&state->audioManager, soundscape->audio,
asset_getVorbis(assetManager, audiolist_battle), asset_getVorbis(assetManager, audiolist_battle),
AUDIO_REPEAT_INFINITE); AUDIO_REPEAT_INFINITE);
@ -494,16 +496,34 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
} }
// TODO(doyle): Revisit key input with state checking for last ended down // TODO(doyle): Revisit key input with state checking for last ended down
if (state->keys[GLFW_KEY_SPACE]) if (state->keys[GLFW_KEY_SPACE] && spaceBarWasDown == FALSE)
{ {
#if 0
Renderer *renderer = &state->renderer; Renderer *renderer = &state->renderer;
f32 yPos = CAST(f32)(rand() % CAST(i32)renderer->size.h); f32 yPos = CAST(f32)(rand() % CAST(i32)renderer->size.h);
f32 xModifier = 5.0f - CAST(f32)(rand() % 3); f32 xModifier = 5.0f - CAST(f32)(rand() % 3);
v2 pos = V2(renderer->size.w - (renderer->size.w / xModifier), yPos); v2 pos = V2(renderer->size.w - (renderer->size.w / xModifier), yPos);
addGenericMob(&state->arena, &state->assetManager, world, pos); addGenericMob(&state->arena, &state->assetManager, world, pos);
#endif
if (world->soundscape->audio->sourceIndex == AUDIO_SOURCE_UNASSIGNED)
{
audio_streamPlayVorbis(
&state->audioManager, world->soundscape->audio,
asset_getVorbis(&state->assetManager, audiolist_battle),
AUDIO_REPEAT_INFINITE);
}
else
{
audio_streamStopVorbis(&state->audioManager,
world->soundscape->audio);
}
spaceBarWasDown = TRUE; spaceBarWasDown = TRUE;
} }
else if (!state->keys[GLFW_KEY_SPACE])
{
spaceBarWasDown = FALSE;
}
} }
// NOTE(doyle): Clipping threshold for snapping velocity to 0 // NOTE(doyle): Clipping threshold for snapping velocity to 0

View File

@ -36,9 +36,11 @@ struct AudioRenderer
typedef struct AudioRenderer AudioRenderer; typedef struct AudioRenderer AudioRenderer;
const i32 audio_init(AudioManager *audioManager); const i32 audio_init(AudioManager *audioManager);
void audio_beginVorbisStream(AudioManager *audioManager, void audio_streamPlayVorbis(AudioManager *audioManager,
AudioRenderer *audioRenderer, AudioVorbis *vorbis, AudioRenderer *audioRenderer, AudioVorbis *vorbis,
i32 numPlays); i32 numPlays);
void audio_streamStopVorbis(AudioManager *audioManager,
AudioRenderer *audioRenderer);
void audio_updateAndPlay(AudioManager *audioManager, void audio_updateAndPlay(AudioManager *audioManager,
AudioRenderer *audioRenderer); AudioRenderer *audioRenderer);
#endif #endif

View File

@ -31,6 +31,8 @@ typedef struct World
i32 heroIndex; i32 heroIndex;
i32 freeEntityIndex; i32 freeEntityIndex;
u32 uniqueIdAccumulator; u32 uniqueIdAccumulator;
Entity *soundscape;
} World; } World;
typedef struct GameState typedef struct GameState