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:
parent
630f2eaa5d
commit
09972ced6e
87
src/Audio.c
87
src/Audio.c
@ -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 sourceIndexToFree = audioRenderer->sourceIndex;
|
|
||||||
|
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;
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,11 +365,13 @@ 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);
|
||||||
|
|
||||||
/* Init hero entity */
|
/* Init hero entity */
|
||||||
world->heroIndex = world->freeEntityIndex;
|
world->heroIndex = world->freeEntityIndex;
|
||||||
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user