Update mini_al with experimental support for SDL/Emscripten.

This commit is contained in:
David Reid 2017-11-18 22:30:20 +10:00
parent f9144ac5b0
commit 84ef860443

288
src/external/mini_al.h vendored
View File

@ -233,8 +233,14 @@ extern "C" {
#define MAL_SUPPORT_OSS
#endif
#endif
#define MAL_SUPPORT_OPENAL // All platforms support OpenAL (at least for now).
#define MAL_SUPPORT_NULL // All platforms support the null device.
// Explicitly disable OpenAL for Emscripten - prefer SDL for this.
#if !defined(MAL_EMSCRIPTEN)
#define MAL_SUPPORT_OPENAL
#endif
#define MAL_SUPPORT_SDL // All platforms support SDL.
#define MAL_SUPPORT_NULL // All platforms support the null backend.
#if !defined(MAL_NO_WASAPI) && defined(MAL_SUPPORT_WASAPI)
@ -261,6 +267,9 @@ extern "C" {
#if !defined(MAL_NO_OPENAL) && defined(MAL_SUPPORT_OPENAL)
#define MAL_ENABLE_OPENAL
#endif
#if !defined(MAL_NO_SDL) && defined(MAL_SUPPORT_SDL)
#define MAL_ENABLE_SDL
#endif
#if !defined(MAL_NO_NULL) && defined(MAL_SUPPORT_NULL)
#define MAL_ENABLE_NULL
#endif
@ -469,7 +478,8 @@ typedef enum
mal_backend_alsa,
mal_backend_oss,
mal_backend_opensl,
mal_backend_openal
mal_backend_openal,
mal_backend_sdl
} mal_backend;
typedef enum
@ -522,6 +532,9 @@ typedef union
#ifdef MAL_SUPPORT_OPENAL
char openal[256]; // OpenAL seems to use human-readable device names as the ID.
#endif
#ifdef MAL_SUPPORT_SDL
int sdl; //
#endif
#ifdef MAL_SUPPORT_NULL
int nullbackend; // Always 0.
#endif
@ -844,11 +857,17 @@ struct mal_context
mal_uint32 isMCFormatsSupported : 1;
} openal;
#endif
#ifdef MAL_SUPPORT_SDL
struct
{
int _unused;
} sdl;
#endif
#ifdef MAL_SUPPORT_NULL
struct
{
int _unused;
} null_device;
} null_backend;
#endif
};
@ -1017,6 +1036,12 @@ struct mal_device
mal_bool32 breakFromMainLoop;
} openal;
#endif
#ifdef MAL_SUPPORT_SDL
struct
{
mal_uint32 deviceID;
} sdl;
#endif
#ifdef MAL_SUPPORT_NULL
struct
{
@ -1575,12 +1600,26 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form
#ifdef MAL_ENABLE_OPENAL
#define MAL_HAS_OPENAL // mini_al inlines the necessary OpenAL stuff.
#endif
#ifdef MAL_ENABLE_SDL
#define MAL_HAS_SDL
#ifdef __has_include
#ifdef MAL_EMSCRIPTEN
#if !__has_include(<SDL/SDL_audio.h>)
#undef MAL_HAS_SDL
#endif
#else
#if !__has_include(<SDL2/SDL_audio.h>)
#undef MAL_HAS_SDL
#endif
#endif
#endif
#endif
#ifdef MAL_ENABLE_NULL
#define MAL_HAS_NULL // Everything supports the null backend.
#endif
// Disable run-time linking on certain backends.
#if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTED)
#if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTEN)
#define MAL_NO_RUNTIME_LINKING
#endif
@ -7768,7 +7807,7 @@ mal_result mal_enumerate_devices__openal(mal_context* pContext, mal_device_type
return MAL_SUCCESS;
}
static void mal_device_uninit__openal(mal_device* pDevice)
void mal_device_uninit__openal(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@ -7784,7 +7823,7 @@ static void mal_device_uninit__openal(mal_device* pDevice)
mal_free(pDevice->openal.pIntermediaryBuffer);
}
static mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
{
if (pDevice->periods > MAL_MAX_PERIODS_OPENAL) {
pDevice->periods = MAL_MAX_PERIODS_OPENAL;
@ -8167,6 +8206,196 @@ static mal_result mal_device__main_loop__openal(mal_device* pDevice)
#endif // OpenAL
///////////////////////////////////////////////////////////////////////////////
//
// SDL Backend
//
///////////////////////////////////////////////////////////////////////////////
#ifdef MAL_HAS_SDL
#define SDL_MAIN_HANDLED
#ifdef MAL_EMSCRIPTEN
#include <SDL/SDL.h>
#else
#include <SDL2/SDL.h>
#endif
SDL_AudioFormat mal_format_to_sdl(mal_format format)
{
switch (format)
{
case mal_format_unknown: return 0;
case mal_format_u8: return AUDIO_U8;
case mal_format_s16: return AUDIO_S16;
case mal_format_s24: return AUDIO_S32; // Closest match.
case mal_format_s32: return AUDIO_S32;
default: return 0;
}
}
mal_format mal_format_from_sdl(SDL_AudioFormat format)
{
switch (format)
{
case AUDIO_U8: return mal_format_u8;
case AUDIO_S16: return mal_format_s16;
case AUDIO_S32: return mal_format_s32;
case AUDIO_F32: return mal_format_f32;
default: return mal_format_unknown;
}
}
mal_result mal_context_init__sdl(mal_context* pContext)
{
mal_assert(pContext != NULL);
int resultSDL = SDL_InitSubSystem(SDL_INIT_AUDIO);
if (resultSDL != 0) {
return MAL_ERROR;
}
return MAL_SUCCESS;
}
mal_result mal_context_uninit__sdl(mal_context* pContext)
{
mal_assert(pContext != NULL);
mal_assert(pContext->backend == mal_backend_sdl);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return MAL_SUCCESS;
}
mal_result mal_enumerate_devices__sdl(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo)
{
(void)pContext;
mal_uint32 infoSize = *pCount;
*pCount = 0;
// For now I'm restricting the SDL backend to default devices.
if (pInfo != NULL) {
if (infoSize > 0) {
if (type == mal_device_type_playback) {
pInfo->id.sdl = 0;
mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1);
} else {
pInfo->id.sdl = 0;
mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1);
}
pInfo += 1;
*pCount += 1;
}
} else {
*pCount += 1;
}
return MAL_SUCCESS;
}
void mal_device_uninit__sdl(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
#if 1
SDL_CloseAudioDevice(pDevice->sdl.deviceID);
#else
SDL_CloseAudio();
#endif
}
static void mal_audio_callback__sdl(void *pUserData, Uint8 *pBuffer, int bufferSizeInBytes)
{
mal_device* pDevice = (mal_device*)pUserData;
mal_assert(pDevice != NULL);
mal_uint32 bufferSizeInFrames = (mal_uint32)bufferSizeInBytes / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels;
if (pDevice->type == mal_device_type_playback) {
mal_device__read_frames_from_client(pDevice, bufferSizeInFrames, pBuffer);
} else {
mal_device__send_frames_to_client(pDevice, bufferSizeInFrames, pBuffer);
}
}
mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
{
mal_assert(pContext != NULL);
mal_assert(pConfig != NULL);
mal_assert(pDevice != NULL);
SDL_AudioSpec desiredSpec, obtainedSpec;
mal_zero_memory(&desiredSpec, sizeof(desiredSpec));
desiredSpec.freq = (int)pConfig->sampleRate;
desiredSpec.format = mal_format_to_sdl(pConfig->format);
desiredSpec.channels = (Uint8)pConfig->channels;
desiredSpec.samples = (Uint16)mal_next_power_of_2(pConfig->bufferSizeInFrames * pConfig->periods * pConfig->channels);
desiredSpec.callback = mal_audio_callback__sdl;
desiredSpec.userdata = pDevice;
// Fall back to f32 if we don't have an appropriate mapping between mini_al and SDL.
if (desiredSpec.format == 0) {
desiredSpec.format = AUDIO_F32;
}
// For now, only using the default device.
(void)pDeviceID;
#if 1
pDevice->sdl.deviceID = SDL_OpenAudioDevice(NULL, (type == mal_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, 0);
if (pDevice->sdl.deviceID == 0) {
return mal_post_error(pDevice, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
#else
pDevice->sdl.deviceID = SDL_OpenAudio(&desiredSpec, &obtainedSpec);
if (pDevice->sdl.deviceID != 0) {
return mal_post_error(pDevice, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
#endif
pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format);
pDevice->internalChannels = obtainedSpec.channels;
pDevice->internalSampleRate = (mal_uint32)obtainedSpec.freq;
pDevice->bufferSizeInFrames = obtainedSpec.samples / obtainedSpec.channels;
pDevice->periods = 1; // SDL doesn't seem to tell us what the period count is. Just set this 1.
return MAL_SUCCESS;
}
static mal_result mal_device__start_backend__sdl(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
#if 1
SDL_PauseAudioDevice(pDevice->sdl.deviceID, 0);
#else
SDL_PauseAudio(0);
#endif
return MAL_SUCCESS;
}
static mal_result mal_device__stop_backend__sdl(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
#if 1
SDL_PauseAudioDevice(pDevice->sdl.deviceID, 1);
#else
SDL_PauseAudio(1);
#endif
return MAL_SUCCESS;
}
#endif // SDL
mal_bool32 mal__is_channel_map_valid(const mal_channel* channelMap, mal_uint32 channels)
{
mal_assert(channels > 0);
@ -8585,6 +8814,7 @@ mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, con
mal_backend_oss,
mal_backend_opensl,
mal_backend_openal,
mal_backend_sdl,
mal_backend_null
};
@ -8642,6 +8872,12 @@ mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, con
result = mal_context_init__openal(pContext);
} break;
#endif
#ifdef MAL_HAS_SDL
case mal_backend_sdl:
{
result = mal_context_init__sdl(pContext);
} break;
#endif
#ifdef MAL_HAS_NULL
case mal_backend_null:
{
@ -8710,6 +8946,12 @@ mal_result mal_context_uninit(mal_context* pContext)
return mal_context_uninit__openal(pContext);
} break;
#endif
#ifdef MAL_HAS_SDL
case mal_backend_sdl:
{
return mal_context_uninit__sdl(pContext);
} break;
#endif
#ifdef MAL_HAS_NULL
case mal_backend_null:
{
@ -8775,6 +9017,12 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma
return mal_enumerate_devices__openal(pContext, type, pCount, pInfo);
} break;
#endif
#ifdef MAL_HAS_SDL
case mal_backend_sdl:
{
return mal_enumerate_devices__sdl(pContext, type, pCount, pInfo);
} break;
#endif
#ifdef MAL_HAS_NULL
case mal_backend_null:
{
@ -8934,6 +9182,12 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
result = mal_device_init__openal(pContext, type, pDeviceID, &config, pDevice);
} break;
#endif
#ifdef MAL_HAS_SDL
case mal_backend_sdl:
{
result = mal_device_init__sdl(pContext, type, pDeviceID, &config, pDevice);
} break;
#endif
#ifdef MAL_HAS_NULL
case mal_backend_null:
{
@ -8979,7 +9233,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
// Some backends don't require the worker thread.
if (pContext->backend != mal_backend_opensl) {
if (pContext->backend != mal_backend_opensl && pContext->backend != mal_backend_sdl) {
// The worker thread.
if (mal_thread_create(pContext, &pDevice->thread, mal_worker_thread, pDevice) != MAL_SUCCESS) {
mal_device_uninit(pDevice);
@ -9012,7 +9266,7 @@ void mal_device_uninit(mal_device* pDevice)
mal_device__set_state(pDevice, MAL_STATE_UNINITIALIZED);
// Wake up the worker thread and wait for it to properly terminate.
if (pDevice->pContext->backend != mal_backend_opensl) {
if (pDevice->pContext->backend != mal_backend_opensl && pDevice->pContext->backend != mal_backend_sdl) {
mal_event_signal(&pDevice->wakeupEvent);
mal_thread_wait(&pDevice->thread);
}
@ -9057,6 +9311,11 @@ void mal_device_uninit(mal_device* pDevice)
mal_device_uninit__openal(pDevice);
}
#endif
#ifdef MAL_HAS_SDL
if (pDevice->pContext->backend == mal_backend_sdl) {
mal_device_uninit__sdl(pDevice);
}
#endif
#ifdef MAL_HAS_NULL
if (pDevice->pContext->backend == mal_backend_null) {
mal_device_uninit__null(pDevice);
@ -9117,6 +9376,12 @@ mal_result mal_device_start(mal_device* pDevice)
mal_device__start_backend__opensl(pDevice);
mal_device__set_state(pDevice, MAL_STATE_STARTED);
} else
#endif
#ifdef MAL_HAS_SDL
if (pDevice->pContext->backend == mal_backend_sdl) {
mal_device__start_backend__sdl(pDevice);
mal_device__set_state(pDevice, MAL_STATE_STARTED);
} else
#endif
// Synchronous backends.
{
@ -9167,6 +9432,11 @@ mal_result mal_device_stop(mal_device* pDevice)
if (pDevice->pContext->backend == mal_backend_opensl) {
mal_device__stop_backend__opensl(pDevice);
} else
#endif
#ifdef MAL_HAS_SDL
if (pDevice->pContext->backend == mal_backend_sdl) {
mal_device__stop_backend__sdl(pDevice);
} else
#endif
// Synchronous backends.
{