mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
SDL: Include format information in device enumeration.
This commit is contained in:
@@ -70,10 +70,10 @@ MA_ENABLE_ONLY_SPECIFIC_BACKENDS) and it's supported at compile time (MA_SUPPORT
|
||||
#include <SDL2/SDL.h>
|
||||
#endif
|
||||
|
||||
typedef SDL_AudioCallback MA_SDL_AudioCallback;
|
||||
typedef SDL_AudioSpec MA_SDL_AudioSpec;
|
||||
typedef SDL_AudioFormat MA_SDL_AudioFormat;
|
||||
typedef SDL_AudioDeviceID MA_SDL_AudioDeviceID;
|
||||
typedef SDL_AudioCallback MA_SDL_AudioCallback;
|
||||
typedef SDL_AudioSpec MA_SDL_AudioSpec;
|
||||
typedef SDL_AudioFormat MA_SDL_AudioFormat;
|
||||
typedef SDL_AudioDeviceID MA_SDL_AudioDeviceID;
|
||||
#else
|
||||
typedef void (* MA_SDL_AudioCallback)(void* userdata, ma_uint8* stream, int len);
|
||||
typedef ma_uint16 MA_SDL_AudioFormat;
|
||||
@@ -93,24 +93,28 @@ MA_ENABLE_ONLY_SPECIFIC_BACKENDS) and it's supported at compile time (MA_SUPPORT
|
||||
} MA_SDL_AudioSpec;
|
||||
#endif
|
||||
|
||||
typedef int (* MA_PFN_SDL_InitSubSystem)(ma_uint32 flags);
|
||||
typedef void (* MA_PFN_SDL_QuitSubSystem)(ma_uint32 flags);
|
||||
typedef int (* MA_PFN_SDL_GetNumAudioDevices)(int iscapture);
|
||||
typedef const char* (* MA_PFN_SDL_GetAudioDeviceName)(int index, int iscapture);
|
||||
typedef void (* MA_PFN_SDL_CloseAudioDevice)(MA_SDL_AudioDeviceID dev);
|
||||
typedef MA_SDL_AudioDeviceID (* MA_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const MA_SDL_AudioSpec* desired, MA_SDL_AudioSpec* obtained, int allowed_changes);
|
||||
typedef void (* MA_PFN_SDL_PauseAudioDevice)(MA_SDL_AudioDeviceID dev, int pause_on);
|
||||
typedef int (* MA_PFN_SDL_InitSubSystem )(ma_uint32 flags);
|
||||
typedef void (* MA_PFN_SDL_QuitSubSystem )(ma_uint32 flags);
|
||||
typedef int (* MA_PFN_SDL_GetDefaultAudioInfo)(char** name, MA_SDL_AudioSpec* spec, int iscapture);
|
||||
typedef int (* MA_PFN_SDL_GetNumAudioDevices )(int iscapture);
|
||||
typedef const char* (* MA_PFN_SDL_GetAudioDeviceName )(int index, int iscapture);
|
||||
typedef int (* MA_PFN_SDL_GetAudioDeviceSpec )(int index, int iscapture, MA_SDL_AudioSpec* spec);
|
||||
typedef void (* MA_PFN_SDL_CloseAudioDevice )(MA_SDL_AudioDeviceID dev);
|
||||
typedef MA_SDL_AudioDeviceID (* MA_PFN_SDL_OpenAudioDevice )(const char* device, int iscapture, const MA_SDL_AudioSpec* desired, MA_SDL_AudioSpec* obtained, int allowed_changes);
|
||||
typedef void (* MA_PFN_SDL_PauseAudioDevice )(MA_SDL_AudioDeviceID dev, int pause_on);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ma_handle hSDL; /* A handle to the SDL2 shared object. We dynamically load function pointers at runtime so we can avoid linking. */
|
||||
MA_PFN_SDL_InitSubSystem SDL_InitSubSystem;
|
||||
MA_PFN_SDL_QuitSubSystem SDL_QuitSubSystem;
|
||||
MA_PFN_SDL_GetNumAudioDevices SDL_GetNumAudioDevices;
|
||||
MA_PFN_SDL_GetAudioDeviceName SDL_GetAudioDeviceName;
|
||||
MA_PFN_SDL_CloseAudioDevice SDL_CloseAudioDevice;
|
||||
MA_PFN_SDL_OpenAudioDevice SDL_OpenAudioDevice;
|
||||
MA_PFN_SDL_PauseAudioDevice SDL_PauseAudioDevice;
|
||||
MA_PFN_SDL_InitSubSystem SDL_InitSubSystem;
|
||||
MA_PFN_SDL_QuitSubSystem SDL_QuitSubSystem;
|
||||
MA_PFN_SDL_GetDefaultAudioInfo SDL_GetDefaultAudioInfo;
|
||||
MA_PFN_SDL_GetNumAudioDevices SDL_GetNumAudioDevices;
|
||||
MA_PFN_SDL_GetAudioDeviceName SDL_GetAudioDeviceName;
|
||||
MA_PFN_SDL_GetAudioDeviceSpec SDL_GetAudioDeviceSpec;
|
||||
MA_PFN_SDL_CloseAudioDevice SDL_CloseAudioDevice;
|
||||
MA_PFN_SDL_OpenAudioDevice SDL_OpenAudioDevice;
|
||||
MA_PFN_SDL_PauseAudioDevice SDL_PauseAudioDevice;
|
||||
} ma_context_state_sdl;
|
||||
|
||||
typedef struct
|
||||
@@ -219,23 +223,27 @@ static ma_result ma_context_init__sdl(ma_context* pContext, const void* pContext
|
||||
}
|
||||
|
||||
/* Now that we have the handle to the shared object we can go ahead and load some function pointers. */
|
||||
pContextStateSDL->SDL_InitSubSystem = (MA_PFN_SDL_InitSubSystem )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_InitSubSystem");
|
||||
pContextStateSDL->SDL_QuitSubSystem = (MA_PFN_SDL_QuitSubSystem )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_QuitSubSystem");
|
||||
pContextStateSDL->SDL_GetNumAudioDevices = (MA_PFN_SDL_GetNumAudioDevices)ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetNumAudioDevices");
|
||||
pContextStateSDL->SDL_GetAudioDeviceName = (MA_PFN_SDL_GetAudioDeviceName)ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetAudioDeviceName");
|
||||
pContextStateSDL->SDL_CloseAudioDevice = (MA_PFN_SDL_CloseAudioDevice )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_CloseAudioDevice");
|
||||
pContextStateSDL->SDL_OpenAudioDevice = (MA_PFN_SDL_OpenAudioDevice )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_OpenAudioDevice");
|
||||
pContextStateSDL->SDL_PauseAudioDevice = (MA_PFN_SDL_PauseAudioDevice )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_PauseAudioDevice");
|
||||
pContextStateSDL->SDL_InitSubSystem = (MA_PFN_SDL_InitSubSystem )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_InitSubSystem");
|
||||
pContextStateSDL->SDL_QuitSubSystem = (MA_PFN_SDL_QuitSubSystem )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_QuitSubSystem");
|
||||
pContextStateSDL->SDL_GetDefaultAudioInfo = (MA_PFN_SDL_GetDefaultAudioInfo)ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetDefaultAudioInfo");
|
||||
pContextStateSDL->SDL_GetNumAudioDevices = (MA_PFN_SDL_GetNumAudioDevices )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetNumAudioDevices");
|
||||
pContextStateSDL->SDL_GetAudioDeviceName = (MA_PFN_SDL_GetAudioDeviceName )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetAudioDeviceName");
|
||||
pContextStateSDL->SDL_GetAudioDeviceSpec = (MA_PFN_SDL_GetAudioDeviceSpec )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_GetAudioDeviceSpec");
|
||||
pContextStateSDL->SDL_CloseAudioDevice = (MA_PFN_SDL_CloseAudioDevice )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_CloseAudioDevice");
|
||||
pContextStateSDL->SDL_OpenAudioDevice = (MA_PFN_SDL_OpenAudioDevice )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_OpenAudioDevice");
|
||||
pContextStateSDL->SDL_PauseAudioDevice = (MA_PFN_SDL_PauseAudioDevice )ma_dlsym(pLog, pContextStateSDL->hSDL, "SDL_PauseAudioDevice");
|
||||
}
|
||||
#else
|
||||
{
|
||||
pContextStateSDL->SDL_InitSubSystem = SDL_InitSubSystem;
|
||||
pContextStateSDL->SDL_QuitSubSystem = SDL_QuitSubSystem;
|
||||
pContextStateSDL->SDL_GetNumAudioDevices = SDL_GetNumAudioDevices;
|
||||
pContextStateSDL->SDL_GetAudioDeviceName = SDL_GetAudioDeviceName;
|
||||
pContextStateSDL->SDL_CloseAudioDevice = SDL_CloseAudioDevice;
|
||||
pContextStateSDL->SDL_OpenAudioDevice = SDL_OpenAudioDevice;
|
||||
pContextStateSDL->SDL_PauseAudioDevice = SDL_PauseAudioDevice;
|
||||
pContextStateSDL->SDL_InitSubSystem = SDL_InitSubSystem;
|
||||
pContextStateSDL->SDL_QuitSubSystem = SDL_QuitSubSystem;
|
||||
pContextStateSDL->SDL_GetDefaultAudioInfo = SDL_GetDefaultAudioInfo;
|
||||
pContextStateSDL->SDL_GetNumAudioDevices = SDL_GetNumAudioDevices;
|
||||
pContextStateSDL->SDL_GetAudioDeviceName = SDL_GetAudioDeviceName;
|
||||
pContextStateSDL->SDL_GetAudioDeviceSpec = SDL_GetAudioDeviceSpec;
|
||||
pContextStateSDL->SDL_CloseAudioDevice = SDL_CloseAudioDevice;
|
||||
pContextStateSDL->SDL_OpenAudioDevice = SDL_OpenAudioDevice;
|
||||
pContextStateSDL->SDL_PauseAudioDevice = SDL_PauseAudioDevice;
|
||||
}
|
||||
#endif /* MA_NO_RUNTIME_LINKING */
|
||||
|
||||
@@ -266,6 +274,21 @@ static void ma_context_uninit__sdl(ma_context* pContext)
|
||||
ma_free(pContextStateSDL, ma_context_get_allocation_callbacks(pContext));
|
||||
}
|
||||
|
||||
static void ma_add_native_format_from_AudioSpec__sdl(ma_device_info* pDeviceInfo, const MA_SDL_AudioSpec* pAudioSpec)
|
||||
{
|
||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_from_sdl(pAudioSpec->format);
|
||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = pAudioSpec->channels;
|
||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = pAudioSpec->freq;
|
||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
|
||||
|
||||
/* If miniaudio does not support the format, just use f32 as the native format (SDL will do the necessary conversions for us). */
|
||||
if (pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format == ma_format_unknown) {
|
||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_f32;
|
||||
}
|
||||
|
||||
pDeviceInfo->nativeDataFormatCount = 1;
|
||||
}
|
||||
|
||||
static ma_result ma_context_enumerate_devices__sdl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData)
|
||||
{
|
||||
ma_context_state_sdl* pContextStateSDL = ma_context_get_backend_state__sdl(pContext);
|
||||
@@ -280,13 +303,24 @@ static ma_result ma_context_enumerate_devices__sdl(ma_context* pContext, ma_enum
|
||||
int deviceCount = pContextStateSDL->SDL_GetNumAudioDevices(0);
|
||||
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
|
||||
ma_device_info deviceInfo;
|
||||
MA_SDL_AudioSpec audioSpec;
|
||||
|
||||
memset(&deviceInfo, 0, sizeof(deviceInfo));
|
||||
|
||||
/* Default. */
|
||||
if (iDevice == 0) { /* <-- Is this correct? Should we instead compare against the name from SDL_GetDefaultAudioInfo()? */
|
||||
deviceInfo.isDefault = MA_TRUE;
|
||||
}
|
||||
|
||||
/* ID. */
|
||||
deviceInfo.id.custom.i = iDevice;
|
||||
|
||||
/* Name. */
|
||||
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pContextStateSDL->SDL_GetAudioDeviceName(iDevice, 0), (size_t)-1);
|
||||
|
||||
if (iDevice == 0) {
|
||||
deviceInfo.isDefault = MA_TRUE;
|
||||
/* Data Format. */
|
||||
if (pContextStateSDL->SDL_GetAudioDeviceSpec(iDevice, 0, &audioSpec) == 0) {
|
||||
ma_add_native_format_from_AudioSpec__sdl(&deviceInfo, &audioSpec);
|
||||
}
|
||||
|
||||
cbResult = callback(ma_device_type_playback, &deviceInfo, pCallbackUserData);
|
||||
@@ -302,13 +336,24 @@ static ma_result ma_context_enumerate_devices__sdl(ma_context* pContext, ma_enum
|
||||
int deviceCount = pContextStateSDL->SDL_GetNumAudioDevices(1);
|
||||
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
|
||||
ma_device_info deviceInfo;
|
||||
MA_SDL_AudioSpec audioSpec;
|
||||
|
||||
memset(&deviceInfo, 0, sizeof(deviceInfo));
|
||||
|
||||
/* Default. */
|
||||
if (iDevice == 0) { /* <-- Is this correct? Should we instead compare against the name from SDL_GetDefaultAudioInfo()? */
|
||||
deviceInfo.isDefault = MA_TRUE;
|
||||
}
|
||||
|
||||
/* ID. */
|
||||
deviceInfo.id.custom.i = iDevice;
|
||||
|
||||
/* Name. */
|
||||
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pContextStateSDL->SDL_GetAudioDeviceName(iDevice, 1), (size_t)-1);
|
||||
|
||||
if (iDevice == 0) {
|
||||
deviceInfo.isDefault = MA_TRUE;
|
||||
/* Data Format. */
|
||||
if (pContextStateSDL->SDL_GetAudioDeviceSpec(iDevice, 1, &audioSpec) == 0) {
|
||||
ma_add_native_format_from_AudioSpec__sdl(&deviceInfo, &audioSpec);
|
||||
}
|
||||
|
||||
cbResult = callback(ma_device_type_capture, &deviceInfo, pCallbackUserData);
|
||||
@@ -322,91 +367,6 @@ static ma_result ma_context_enumerate_devices__sdl(ma_context* pContext, ma_enum
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_context_get_device_info__sdl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
||||
{
|
||||
ma_context_state_sdl* pContextStateSDL = ma_context_get_backend_state__sdl(pContext);
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
MA_SDL_AudioSpec desiredSpec;
|
||||
MA_SDL_AudioSpec obtainedSpec;
|
||||
MA_SDL_AudioDeviceID tempDeviceID;
|
||||
const char* pDeviceName;
|
||||
#endif
|
||||
|
||||
if (pDeviceID == NULL) {
|
||||
if (deviceType == ma_device_type_playback) {
|
||||
pDeviceInfo->id.custom.i = 0;
|
||||
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "Default Playback Device", (size_t)-1);
|
||||
} else {
|
||||
pDeviceInfo->id.custom.i = 0;
|
||||
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "Default Capture Device", (size_t)-1);
|
||||
}
|
||||
} else {
|
||||
pDeviceInfo->id.custom.i = pDeviceID->custom.i;
|
||||
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pContextStateSDL->SDL_GetAudioDeviceName(pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1), (size_t)-1);
|
||||
}
|
||||
|
||||
if (pDeviceInfo->id.custom.i == 0) {
|
||||
pDeviceInfo->isDefault = MA_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
To get an accurate idea on the backend's native format we need to open the device. Not ideal, but it's the only way. An
|
||||
alternative to this is to report all channel counts, sample rates and formats, but that doesn't offer a good representation
|
||||
of the device's _actual_ ideal format.
|
||||
|
||||
Note: With Emscripten, it looks like non-zero values need to be specified for desiredSpec. Whatever is specified in
|
||||
desiredSpec will be used by SDL since it uses it just does it's own format conversion internally. Therefore, from what
|
||||
I can tell, there's no real way to know the device's actual format which means I'm just going to fall back to the full
|
||||
range of channels and sample rates on Emscripten builds.
|
||||
*/
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
/* Good practice to prioritize the best format first so that the application can use the first data format as their chosen one if desired. */
|
||||
pDeviceInfo->nativeDataFormatCount = 3;
|
||||
pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
|
||||
pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channel counts supported. */
|
||||
pDeviceInfo->nativeDataFormats[0].sampleRate = 0; /* All sample rates supported. */
|
||||
pDeviceInfo->nativeDataFormats[0].flags = 0;
|
||||
pDeviceInfo->nativeDataFormats[1].format = ma_format_s32;
|
||||
pDeviceInfo->nativeDataFormats[1].channels = 0; /* All channel counts supported. */
|
||||
pDeviceInfo->nativeDataFormats[1].sampleRate = 0; /* All sample rates supported. */
|
||||
pDeviceInfo->nativeDataFormats[1].flags = 0;
|
||||
pDeviceInfo->nativeDataFormats[2].format = ma_format_u8;
|
||||
pDeviceInfo->nativeDataFormats[2].channels = 0; /* All channel counts supported. */
|
||||
pDeviceInfo->nativeDataFormats[2].sampleRate = 0; /* All sample rates supported. */
|
||||
pDeviceInfo->nativeDataFormats[2].flags = 0;
|
||||
#else
|
||||
memset(&desiredSpec, 0, sizeof(desiredSpec));
|
||||
|
||||
pDeviceName = NULL;
|
||||
if (pDeviceID != NULL) {
|
||||
pDeviceName = pContextStateSDL->SDL_GetAudioDeviceName(pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1);
|
||||
}
|
||||
|
||||
tempDeviceID = pContextStateSDL->SDL_OpenAudioDevice(pDeviceName, (deviceType == ma_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
if (tempDeviceID == 0) {
|
||||
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to open SDL device.");
|
||||
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
||||
}
|
||||
|
||||
pContextStateSDL->SDL_CloseAudioDevice(tempDeviceID);
|
||||
|
||||
/* Only reporting a single native data format. It'll be whatever SDL decides is the best. */
|
||||
pDeviceInfo->nativeDataFormatCount = 1;
|
||||
pDeviceInfo->nativeDataFormats[0].format = ma_format_from_sdl(obtainedSpec.format);
|
||||
pDeviceInfo->nativeDataFormats[0].channels = obtainedSpec.channels;
|
||||
pDeviceInfo->nativeDataFormats[0].sampleRate = obtainedSpec.freq;
|
||||
pDeviceInfo->nativeDataFormats[0].flags = 0;
|
||||
|
||||
/* If miniaudio does not support the format, just use f32 as the native format (SDL will do the necessary conversions for us). */
|
||||
if (pDeviceInfo->nativeDataFormats[0].format == ma_format_unknown) {
|
||||
pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;
|
||||
}
|
||||
#endif /* __EMSCRIPTEN__ */
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void ma_audio_callback_capture__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
|
||||
{
|
||||
@@ -472,7 +432,7 @@ static ma_result ma_device_init_internal__sdl(ma_device* pDevice, ma_context_sta
|
||||
desiredSpec.channels = (ma_uint8)pDescriptor->channels;
|
||||
desiredSpec.samples = (ma_uint16)pDescriptor->periodSizeInFrames;
|
||||
desiredSpec.callback = (deviceType == ma_device_type_capture) ? ma_audio_callback_capture__sdl : ma_audio_callback_playback__sdl;
|
||||
desiredSpec.userdata = pDeviceStateSDL;
|
||||
desiredSpec.userdata = pDevice;
|
||||
|
||||
/* We'll fall back to f32 if we don't have an appropriate mapping between SDL and miniaudio. */
|
||||
if (desiredSpec.format == 0) {
|
||||
@@ -613,7 +573,7 @@ static ma_device_backend_vtable ma_gDeviceBackendVTable_SDL =
|
||||
ma_context_init__sdl,
|
||||
ma_context_uninit__sdl,
|
||||
ma_context_enumerate_devices__sdl,
|
||||
ma_context_get_device_info__sdl,
|
||||
NULL,
|
||||
ma_device_init__sdl,
|
||||
ma_device_uninit__sdl,
|
||||
ma_device_start__sdl,
|
||||
|
||||
Reference in New Issue
Block a user