free audio voices properly

This commit is contained in:
Jamiras 2022-03-25 22:03:18 -06:00 committed by Autechre
parent 08c1c05f82
commit f63c11ebaf

View File

@ -183,13 +183,14 @@ struct audio_mixer_voice
unsigned type;
float volume;
bool repeat;
};
/* TODO/FIXME - static globals */
static struct audio_mixer_voice s_voices[AUDIO_MIXER_MAX_VOICES] = {0};
static unsigned s_rate = 0;
static void audio_mixer_release(audio_mixer_voice_t* voice);
#ifdef HAVE_RWAV
static bool wav_to_float(const rwav_t* wav, float** pcm, size_t samples_out)
{
@ -319,7 +320,7 @@ void audio_mixer_done(void)
unsigned i;
for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++)
s_voices[i].type = AUDIO_MIXER_TYPE_NONE;
audio_mixer_release(&s_voices[i]);
}
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size,
@ -562,14 +563,6 @@ static bool audio_mixer_play_ogg(
goto error;
}
/* "system" menu sounds may reuse the same voice without freeing anything first, so do that here if needed */
if (voice->types.ogg.stream)
stb_vorbis_close(voice->types.ogg.stream);
if (voice->types.ogg.resampler && voice->types.ogg.resampler_data)
voice->types.ogg.resampler->free(voice->types.ogg.resampler_data);
if (voice->types.ogg.buffer)
memalign_free(voice->types.ogg.buffer);
voice->types.ogg.resampler = resamp;
voice->types.ogg.resampler_data = resampler_data;
voice->types.ogg.buffer = (float*)ogg_buffer;
@ -585,6 +578,17 @@ error:
stb_vorbis_close(stb_vorbis);
return false;
}
static void audio_mixer_release_ogg(audio_mixer_voice_t* voice)
{
if (voice->types.ogg.stream)
stb_vorbis_close(voice->types.ogg.stream);
if (voice->types.ogg.resampler && voice->types.ogg.resampler_data)
voice->types.ogg.resampler->free(voice->types.ogg.resampler_data);
if (voice->types.ogg.buffer)
memalign_free(voice->types.ogg.buffer);
}
#endif
#ifdef HAVE_IBXM
@ -642,12 +646,6 @@ static bool audio_mixer_play_mod(
goto error;
}
/* FIXME: stopping and then starting a mod stream will crash here in dispose_replay (ASAN says struct replay is misaligned?) */
if (voice->types.mod.stream)
dispose_replay(voice->types.mod.stream);
if (voice->types.mod.buffer)
memalign_free(voice->types.mod.buffer);
voice->types.mod.buffer = (int*)mod_buffer;
voice->types.mod.buf_samples = buf_samples;
voice->types.mod.stream = replay;
@ -664,6 +662,14 @@ error:
return false;
}
static void audio_mixer_release_mod(audio_mixer_voice_t* voice)
{
if (voice->types.mod.stream)
dispose_replay(voice->types.mod.stream);
if (voice->types.mod.buffer)
memalign_free(voice->types.mod.buffer);
}
#endif
#ifdef HAVE_DR_FLAC
@ -711,13 +717,6 @@ static bool audio_mixer_play_flac(
goto error;
}
if (voice->types.flac.stream)
drflac_close(voice->types.flac.stream);
if (voice->types.flac.resampler && voice->types.flac.resampler_data)
voice->types.flac.resampler->free(voice->types.flac.resampler_data);
if (voice->types.flac.buffer)
memalign_free(voice->types.flac.buffer);
voice->types.flac.resampler = resamp;
voice->types.flac.resampler_data = resampler_data;
voice->types.flac.buffer = (float*)flac_buffer;
@ -733,6 +732,16 @@ error:
drflac_close(dr_flac);
return false;
}
static void audio_mixer_release_flac(audio_mixer_voice_t* voice)
{
if (voice->types.flac.stream)
drflac_close(voice->types.flac.stream);
if (voice->types.flac.resampler && voice->types.flac.resampler_data)
voice->types.flac.resampler->free(voice->types.flac.resampler_data);
if (voice->types.flac.buffer)
memalign_free(voice->types.flac.buffer);
}
#endif
#ifdef HAVE_DR_MP3
@ -751,12 +760,6 @@ static bool audio_mixer_play_mp3(
const retro_resampler_t* resamp = NULL;
bool res;
if (voice->types.mp3.stream.pData)
{
drmp3_uninit(&voice->types.mp3.stream);
memset(&voice->types.mp3.stream, 0, sizeof(voice->types.mp3.stream));
}
res = drmp3_init_memory(&voice->types.mp3.stream, (const unsigned char*)sound->types.mp3.data, sound->types.mp3.size, NULL);
if (!res)
@ -789,12 +792,6 @@ static bool audio_mixer_play_mp3(
goto error;
}
/* "system" menu sounds may reuse the same voice without freeing anything first, so do that here if needed */
if (voice->types.mp3.resampler && voice->types.mp3.resampler_data)
voice->types.mp3.resampler->free(voice->types.mp3.resampler_data);
if (voice->types.mp3.buffer)
memalign_free(voice->types.mp3.buffer);
voice->types.mp3.resampler = resamp;
voice->types.mp3.resampler_data = resampler_data;
voice->types.mp3.buffer = (float*)mp3_buffer;
@ -809,6 +806,17 @@ error:
drmp3_uninit(&voice->types.mp3.stream);
return false;
}
static void audio_mixer_release_mp3(audio_mixer_voice_t* voice)
{
if (voice->types.mp3.resampler && voice->types.mp3.resampler_data)
voice->types.mp3.resampler->free(voice->types.mp3.resampler_data);
if (voice->types.mp3.buffer)
memalign_free(voice->types.mp3.buffer);
if (voice->types.mp3.stream.pData)
drmp3_uninit(&voice->types.mp3.stream);
}
#endif
audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound,
@ -829,6 +837,9 @@ audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound,
if (voice->type != AUDIO_MIXER_TYPE_NONE)
continue;
/* claim the voice, also helps with cleanup on error */
voice->type = sound->type;
switch (sound->type)
{
case AUDIO_MIXER_TYPE_WAV:
@ -866,18 +877,55 @@ audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound,
if (res)
{
voice->type = sound->type;
voice->repeat = repeat;
voice->volume = volume;
voice->sound = sound;
voice->stop_cb = stop_cb;
}
else
{
audio_mixer_release(voice);
voice = NULL;
}
return voice;
}
static void audio_mixer_release(audio_mixer_voice_t* voice)
{
if (!voice)
return;
switch (voice->type)
{
#ifdef HAVE_STB_VORBIS
case AUDIO_MIXER_TYPE_OGG:
audio_mixer_release_ogg(voice);
break;
#endif
#ifdef HAVE_IBXM
case AUDIO_MIXER_TYPE_MOD:
audio_mixer_release_mod(voice);
break;
#endif
#ifdef HAVE_DR_FLAC
case AUDIO_MIXER_TYPE_FLAC:
audio_mixer_release_flac(voice);
break;
#endif
#ifdef HAVE_DR_MP3
case AUDIO_MIXER_TYPE_MP3:
audio_mixer_release_mp3(voice);
break;
#endif
default:
break;
}
memset(&voice->types, 0, sizeof(voice->types));
voice->type = AUDIO_MIXER_TYPE_NONE;
}
void audio_mixer_stop(audio_mixer_voice_t* voice)
{
audio_mixer_stop_cb_t stop_cb = NULL;
@ -888,7 +936,7 @@ void audio_mixer_stop(audio_mixer_voice_t* voice)
stop_cb = voice->stop_cb;
sound = voice->sound;
voice->type = AUDIO_MIXER_TYPE_NONE;
audio_mixer_release(voice);
if (stop_cb)
stop_cb(sound, AUDIO_MIXER_SOUND_STOPPED);
@ -928,7 +976,7 @@ again:
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
voice->type = AUDIO_MIXER_TYPE_NONE;
audio_mixer_release(voice);
}
else
{
@ -974,7 +1022,7 @@ again:
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
voice->type = AUDIO_MIXER_TYPE_NONE;
audio_mixer_release(voice);
goto cleanup;
}
@ -1052,7 +1100,7 @@ again:
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
voice->type = AUDIO_MIXER_TYPE_NONE;
audio_mixer_release(voice);
return;
}
@ -1116,7 +1164,7 @@ again:
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
voice->type = AUDIO_MIXER_TYPE_NONE;
audio_mixer_release(voice);
return;
}
@ -1187,7 +1235,7 @@ again:
if (voice->stop_cb)
voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);
voice->type = AUDIO_MIXER_TYPE_NONE;
audio_mixer_release(voice);
return;
}