diff --git a/examples/custom_backend.c b/examples/custom_backend.c index 5910a83a..91111a1b 100644 --- a/examples/custom_backend.c +++ b/examples/custom_backend.c @@ -45,7 +45,6 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin int main(int argc, char** argv) { ma_result result; - ma_context_config contextConfig; ma_context context; ma_device_config deviceConfig; ma_device device; @@ -53,15 +52,6 @@ int main(int argc, char** argv) ma_waveform sineWave; char name[256]; - /* - We're just using ma_backend_custom in this example for demonstration purposes, but a more realistic use case would probably want to include - other backends as well for robustness. - */ - ma_backend backends[] = { - ma_backend_custom - }; - - /* Here is where we would set up the SDL-specific context-level config. The custom SDL backend allows this to be null, but we're defining it here just for the sake of demonstration. Whether or not this is required depends on the backend. If you're not sure, @@ -73,26 +63,18 @@ int main(int argc, char** argv) /* You must include an entry for each backend you're using, even if the config is NULL. This is how miniaudio knows about your custom backend. - */ - ma_device_backend_spec pCustomContextConfigs[] = { - { MA_DEVICE_BACKEND_VTABLE_SDL, &sdlContextConfig, NULL } - }; - #if 0 + For stock backends, you can just leave the config pointer as NULL and fill out any backend-specific config options in + the ma_context_config structure. Same with device configs. + */ ma_device_backend_config backends[] = { { ma_device_backend_sdl, &sdlContextConfig }, { ma_device_backend_wasapi, NULL }, { ma_device_backend_pulseaudio, NULL } }; - #endif - contextConfig = ma_context_config_init(); - contextConfig.custom.pBackends = pCustomContextConfigs; - contextConfig.custom.count = (sizeof(pCustomContextConfigs) / sizeof(pCustomContextConfigs[0])); - - - result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &contextConfig, &context); + result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), NULL, &context); if (result != MA_SUCCESS) { return -1; } @@ -113,20 +95,23 @@ int main(int argc, char** argv) /* Unlike with contexts, if your backend does not require a device-level config, you can just leave it out of this list entirely. */ - ma_device_backend_spec pCustomDeviceConfigs[] = { - { MA_DEVICE_BACKEND_VTABLE_SDL, &sdlDeviceConfig, NULL } + ma_device_backend_config pBackendDeviceConfigs[] = + { + { ma_device_backend_sdl, &sdlDeviceConfig }, + { ma_device_backend_wasapi, NULL }, + { ma_device_backend_pulseaudio, NULL } }; deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.format = DEVICE_FORMAT; - deviceConfig.playback.channels = DEVICE_CHANNELS; - deviceConfig.capture.format = DEVICE_FORMAT; - deviceConfig.capture.channels = DEVICE_CHANNELS; - deviceConfig.sampleRate = DEVICE_SAMPLE_RATE; - deviceConfig.dataCallback = data_callback; - deviceConfig.pUserData = &sineWave; - deviceConfig.custom.pBackends = pCustomDeviceConfigs; - deviceConfig.custom.count = sizeof(pCustomDeviceConfigs) / sizeof(pCustomDeviceConfigs[0]); + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = DEVICE_CHANNELS; + deviceConfig.capture.format = DEVICE_FORMAT; + deviceConfig.capture.channels = DEVICE_CHANNELS; + deviceConfig.sampleRate = DEVICE_SAMPLE_RATE; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &sineWave; + deviceConfig.pBackendConfigs = pBackendDeviceConfigs; + deviceConfig.backendConfigCount = sizeof(pBackendDeviceConfigs) / sizeof(pBackendDeviceConfigs[0]); result = ma_device_init(&context, &deviceConfig, &device); if (result != MA_SUCCESS) { diff --git a/examples/simple_loopback.c b/examples/simple_loopback.c index f36a2124..ff902b68 100644 --- a/examples/simple_loopback.c +++ b/examples/simple_loopback.c @@ -31,8 +31,8 @@ int main(int argc, char** argv) ma_device device; /* Loopback mode is currently only supported on WASAPI. */ - ma_backend backends[] = { - ma_backend_wasapi + ma_device_backend_config backends[] = { + { ma_device_backend_wasapi, NULL } }; if (argc < 2) { diff --git a/extras/backends/sdl/backend_sdl.c b/extras/backends/sdl/backend_sdl.c index f6ae9451..edd88a53 100644 --- a/extras/backends/sdl/backend_sdl.c +++ b/extras/backends/sdl/backend_sdl.c @@ -15,6 +15,11 @@ which requires the `-s USE_SDL=2` option. #include "backend_sdl.h" #include /* memset() */ +#include + +#ifndef MA_SDL_ASSERT +#define MA_SDL_ASSERT(cond) assert(cond) +#endif /* Support SDL on everything. */ #define MA_SUPPORT_SDL @@ -56,24 +61,6 @@ MA_ENABLE_ONLY_SPECIFIC_BACKENDS) and it's supported at compile time (MA_SUPPORT #define MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004 #define MA_SDL_AUDIO_ALLOW_ANY_CHANGE (MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE | MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE) -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_proc SDL_InitSubSystem; - ma_proc SDL_QuitSubSystem; - ma_proc SDL_GetNumAudioDevices; - ma_proc SDL_GetAudioDeviceName; - ma_proc SDL_CloseAudioDevice; - ma_proc SDL_OpenAudioDevice; - ma_proc SDL_PauseAudioDevice; -} ma_context_data_sdl; - -typedef struct -{ - int deviceIDPlayback; - int deviceIDCapture; -} ma_device_data_sdl; - /* If we are linking at compile time we'll just #include SDL.h. Otherwise we can just redeclare some stuff to avoid the need for development packages to be installed. */ #ifdef MA_NO_RUNTIME_LINKING #define SDL_MAIN_HANDLED @@ -114,6 +101,35 @@ typedef void (* MA_PFN_SDL_CloseAudioDevice)(MA_SDL_AudioDeviceI 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_context_state_sdl; + +typedef struct +{ + struct + { + int deviceID; + ma_format format; + ma_uint32 channels; + } capture; + struct + { + int deviceID; + ma_format format; + ma_uint32 channels; + } playback; +} ma_device_state_sdl; + + MA_SDL_AudioFormat ma_format_to_sdl(ma_format format) { switch (format) @@ -140,26 +156,134 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) } } -static ma_result ma_context_enumerate_devices__sdl(void* pUserData, ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData) + +static ma_context_state_sdl* ma_context_get_backend_state__sdl(ma_context* pContext) { - ma_context_data_sdl* pContextDataSDL; + return (ma_context_state_sdl*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_sdl* ma_device_get_backend_state__sdl(ma_device* pDevice) +{ + return (ma_device_state_sdl*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__sdl(ma_device_backend_info* pBackendInfo) +{ + MA_SDL_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "SDL2"; +} + +static ma_result ma_context_init__sdl(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_sdl* pContextStateSDL; + const ma_context_config_sdl* pContextConfigSDL = (ma_context_config_sdl*)pContextBackendConfig; + ma_log* pLog = ma_context_get_log(pContext); + int resultSDL; + + /* The context config is not currently being used for this backend. */ + (void)pContextConfigSDL; + + + /* Allocate our SDL-specific context data. */ + pContextStateSDL = (ma_context_state_sdl*)ma_calloc(sizeof(*pContextStateSDL), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateSDL == NULL) { + return MA_OUT_OF_MEMORY; + } + + #ifndef MA_NO_RUNTIME_LINKING + { + /* We'll use a list of possible shared object names for easier extensibility. */ + size_t iName; + const char* pSDLNames[] = { + #if defined(_WIN32) + "SDL2.dll" + #elif defined(__APPLE__) + "SDL2.framework/SDL2" + #else + "libSDL2-2.0.so.0" + #endif + }; + + /* Check if we have SDL2 installed somewhere. If not it's not usable and we need to abort. */ + for (iName = 0; iName < ma_countof(pSDLNames); iName += 1) { + pContextStateSDL->hSDL = ma_dlopen(pLog, pSDLNames[iName]); + if (pContextStateSDL->hSDL != NULL) { + break; + } + } + + if (pContextStateSDL->hSDL == NULL) { + ma_free(pContextStateSDL, ma_context_get_allocation_callbacks(pContext)); + return MA_NO_BACKEND; /* SDL2 could not be loaded. */ + } + + /* 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"); + } + #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; + } + #endif /* MA_NO_RUNTIME_LINKING */ + + resultSDL = pContextStateSDL->SDL_InitSubSystem(MA_SDL_INIT_AUDIO); + if (resultSDL != 0) { + ma_dlclose(pLog, pContextStateSDL->hSDL); + ma_free(pContextStateSDL, ma_context_get_allocation_callbacks(pContext)); + return MA_ERROR; + } + + *ppContextState = pContextStateSDL; + + return MA_SUCCESS; +} + +static void ma_context_uninit__sdl(ma_context* pContext) +{ + ma_context_state_sdl* pContextStateSDL = ma_context_get_backend_state__sdl(pContext); + + MA_SDL_ASSERT(pContextStateSDL != NULL); + + pContextStateSDL->SDL_QuitSubSystem(MA_SDL_INIT_AUDIO); + + /* Close the handle to the SDL shared object last. */ + ma_dlclose(ma_context_get_log(pContext), pContextStateSDL->hSDL); + pContextStateSDL->hSDL = NULL; + + ma_free(pContextStateSDL, ma_context_get_allocation_callbacks(pContext)); +} + +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); ma_bool32 isTerminated = MA_FALSE; ma_bool32 cbResult; int iDevice; - (void)pUserData; - - pContextDataSDL = (ma_context_data_sdl*)pContext->pBackendData; + MA_SDL_ASSERT(pContextStateSDL != NULL); /* Playback */ if (!isTerminated) { - int deviceCount = ((MA_PFN_SDL_GetNumAudioDevices)pContextDataSDL->SDL_GetNumAudioDevices)(0); + int deviceCount = pContextStateSDL->SDL_GetNumAudioDevices(0); for (iDevice = 0; iDevice < deviceCount; ++iDevice) { ma_device_info deviceInfo; memset(&deviceInfo, 0, sizeof(deviceInfo)); deviceInfo.id.custom.i = iDevice; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MA_PFN_SDL_GetAudioDeviceName)pContextDataSDL->SDL_GetAudioDeviceName)(iDevice, 0), (size_t)-1); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pContextStateSDL->SDL_GetAudioDeviceName(iDevice, 0), (size_t)-1); if (iDevice == 0) { deviceInfo.isDefault = MA_TRUE; @@ -175,13 +299,13 @@ static ma_result ma_context_enumerate_devices__sdl(void* pUserData, ma_context* /* Capture */ if (!isTerminated) { - int deviceCount = ((MA_PFN_SDL_GetNumAudioDevices)pContextDataSDL->SDL_GetNumAudioDevices)(1); + int deviceCount = pContextStateSDL->SDL_GetNumAudioDevices(1); for (iDevice = 0; iDevice < deviceCount; ++iDevice) { ma_device_info deviceInfo; memset(&deviceInfo, 0, sizeof(deviceInfo)); deviceInfo.id.custom.i = iDevice; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MA_PFN_SDL_GetAudioDeviceName)pContextDataSDL->SDL_GetAudioDeviceName)(iDevice, 1), (size_t)-1); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pContextStateSDL->SDL_GetAudioDeviceName(iDevice, 1), (size_t)-1); if (iDevice == 0) { deviceInfo.isDefault = MA_TRUE; @@ -198,9 +322,9 @@ static ma_result ma_context_enumerate_devices__sdl(void* pUserData, ma_context* return MA_SUCCESS; } -static ma_result ma_context_get_device_info__sdl(void* pUserData, ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +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_data_sdl* pContextDataSDL; + ma_context_state_sdl* pContextStateSDL = ma_context_get_backend_state__sdl(pContext); #if !defined(__EMSCRIPTEN__) MA_SDL_AudioSpec desiredSpec; @@ -209,10 +333,6 @@ static ma_result ma_context_get_device_info__sdl(void* pUserData, ma_context* pC const char* pDeviceName; #endif - (void)pUserData; - - pContextDataSDL = (ma_context_data_sdl*)pContext->pBackendData; - if (pDeviceID == NULL) { if (deviceType == ma_device_type_playback) { pDeviceInfo->id.custom.i = 0; @@ -223,7 +343,7 @@ static ma_result ma_context_get_device_info__sdl(void* pUserData, ma_context* pC } } else { pDeviceInfo->id.custom.i = pDeviceID->custom.i; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MA_PFN_SDL_GetAudioDeviceName)pContextDataSDL->SDL_GetAudioDeviceName)(pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1), (size_t)-1); + 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) { @@ -260,16 +380,16 @@ static ma_result ma_context_get_device_info__sdl(void* pUserData, ma_context* pC pDeviceName = NULL; if (pDeviceID != NULL) { - pDeviceName = ((MA_PFN_SDL_GetAudioDeviceName)pContextDataSDL->SDL_GetAudioDeviceName)(pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1); + pDeviceName = pContextStateSDL->SDL_GetAudioDeviceName(pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1); } - tempDeviceID = ((MA_PFN_SDL_OpenAudioDevice)pContextDataSDL->SDL_OpenAudioDevice)(pDeviceName, (deviceType == ma_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE); + 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; } - ((MA_PFN_SDL_CloseAudioDevice)pContextDataSDL->SDL_CloseAudioDevice)(tempDeviceID); + pContextStateSDL->SDL_CloseAudioDevice(tempDeviceID); /* Only reporting a single native data format. It'll be whatever SDL decides is the best. */ pDeviceInfo->nativeDataFormatCount = 1; @@ -291,33 +411,26 @@ static ma_result ma_context_get_device_info__sdl(void* pUserData, ma_context* pC void ma_audio_callback_capture__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes) { ma_device* pDevice = (ma_device*)pUserData; - ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + ma_device_state_sdl* pDeviceStateSDL = ma_device_get_backend_state__sdl(pDevice); + ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceStateSDL->capture.format, pDeviceStateSDL->capture.channels)); } void ma_audio_callback_playback__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes) { ma_device* pDevice = (ma_device*)pUserData; - ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + ma_device_state_sdl* pDeviceStateSDL = ma_device_get_backend_state__sdl(pDevice); + ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceStateSDL->playback.format, pDeviceStateSDL->playback.channels)); } -static ma_result ma_device_init_internal__sdl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor) +static ma_result ma_device_init_internal__sdl(ma_device* pDevice, ma_context_state_sdl* pContextStateSDL, ma_device_state_sdl* pDeviceStateSDL, const ma_device_config_sdl* pDeviceConfigSDL, ma_device_type deviceType, ma_device_descriptor* pDescriptor) { - ma_context_data_sdl* pContextDataSDL; - ma_device_data_sdl* pDeviceDataSDL; - const ma_device_config_sdl* pDeviceConfigSDL; MA_SDL_AudioSpec desiredSpec; MA_SDL_AudioSpec obtainedSpec; const char* pDeviceName; int deviceID; - pContextDataSDL = (ma_context_data_sdl*)pDevice->pContext->pBackendData; - pDeviceDataSDL = (ma_device_data_sdl*)pDevice->pBackendData; - - /* Grab the SDL backend config. This is not currently used. */ - pDeviceConfigSDL = (const ma_device_config_sdl*)ma_device_config_find_custom_backend_config(pConfig, MA_DEVICE_BACKEND_VTABLE_SDL); (void)pDeviceConfigSDL; - /* SDL is a little bit awkward with specifying the buffer size, You need to specify the size of the buffer in frames, but since we may have requested a period size in milliseconds we'll need to convert, which depends on the sample rate. But there's a possibility that @@ -343,7 +456,7 @@ static ma_result ma_device_init_internal__sdl(ma_device* pDevice, const ma_devic A helper function called ma_calculate_buffer_size_in_frames_from_descriptor() is available to do all of this for you which is what we'll be using here. */ - pDescriptor->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile); + pDescriptor->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, ma_performance_profile_low_latency); /* SDL wants the buffer size to be a power of 2 for some reason. */ if (pDescriptor->periodSizeInFrames > 32768) { @@ -359,8 +472,8 @@ static ma_result ma_device_init_internal__sdl(ma_device* pDevice, const ma_devic desiredSpec.format = ma_format_to_sdl(pDescriptor->format); desiredSpec.channels = (ma_uint8)pDescriptor->channels; desiredSpec.samples = (ma_uint16)pDescriptor->periodSizeInFrames; - desiredSpec.callback = (pConfig->deviceType == ma_device_type_capture) ? ma_audio_callback_capture__sdl : ma_audio_callback_playback__sdl; - desiredSpec.userdata = pDevice; + desiredSpec.callback = (deviceType == ma_device_type_capture) ? ma_audio_callback_capture__sdl : ma_audio_callback_playback__sdl; + desiredSpec.userdata = pDeviceStateSDL; /* We'll fall back to f32 if we don't have an appropriate mapping between SDL and miniaudio. */ if (desiredSpec.format == 0) { @@ -369,21 +482,15 @@ static ma_result ma_device_init_internal__sdl(ma_device* pDevice, const ma_devic pDeviceName = NULL; if (pDescriptor->pDeviceID != NULL) { - pDeviceName = ((MA_PFN_SDL_GetAudioDeviceName)pContextDataSDL->SDL_GetAudioDeviceName)(pDescriptor->pDeviceID->custom.i, (pConfig->deviceType == ma_device_type_playback) ? 0 : 1); + pDeviceName = pContextStateSDL->SDL_GetAudioDeviceName(pDescriptor->pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1); } - deviceID = ((MA_PFN_SDL_OpenAudioDevice)pContextDataSDL->SDL_OpenAudioDevice)(pDeviceName, (pConfig->deviceType == ma_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE); + deviceID = pContextStateSDL->SDL_OpenAudioDevice(pDeviceName, (deviceType == ma_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE); if (deviceID == 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to open SDL2 device."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } - if (pConfig->deviceType == ma_device_type_playback) { - pDeviceDataSDL->deviceIDPlayback = deviceID; - } else { - pDeviceDataSDL->deviceIDCapture = deviceID; - } - /* The descriptor needs to be updated with our actual settings. */ pDescriptor->format = ma_format_from_sdl(obtainedSpec.format); pDescriptor->channels = obtainedSpec.channels; @@ -392,210 +499,109 @@ static ma_result ma_device_init_internal__sdl(ma_device* pDevice, const ma_devic pDescriptor->periodSizeInFrames = obtainedSpec.samples; pDescriptor->periodCount = 1; /* SDL doesn't use the notion of period counts, so just set to 1. */ + if (deviceType == ma_device_type_playback) { + pDeviceStateSDL->playback.deviceID = deviceID; + pDeviceStateSDL->playback.format = pDescriptor->format; + pDeviceStateSDL->playback.channels = pDescriptor->channels; + } else { + pDeviceStateSDL->capture.deviceID = deviceID; + pDeviceStateSDL->capture.format = pDescriptor->format; + pDeviceStateSDL->capture.channels = pDescriptor->channels; + } + return MA_SUCCESS; } -static ma_result ma_device_init__sdl(void* pUserData, ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__sdl(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { - ma_context_data_sdl* pContextDataSDL; - ma_device_data_sdl* pDeviceDataSDL; + ma_device_state_sdl* pDeviceStateSDL; + ma_device_config_sdl* pDeviceConfigSDL = (ma_device_config_sdl*)pDeviceBackendConfig; + ma_context_state_sdl* pContextStateSDL = ma_context_get_backend_state__sdl(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result; - - (void)pUserData; - - pContextDataSDL = (ma_context_data_sdl*)pDevice->pContext->pBackendData; /* SDL does not support loopback mode, so must return MA_DEVICE_TYPE_NOT_SUPPORTED if it's requested. */ - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* We need to allocate our backend-specific data. */ - pDeviceDataSDL = (ma_device_data_sdl*)ma_calloc(sizeof(*pDeviceDataSDL), &pDevice->pContext->allocationCallbacks); - if (pDeviceDataSDL == NULL) { + pDeviceStateSDL = (ma_device_state_sdl*)ma_calloc(sizeof(*pDeviceStateSDL), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateSDL == NULL) { return MA_OUT_OF_MEMORY; } - pDevice->pBackendData = pDeviceDataSDL; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_internal__sdl(pDevice, pConfig, pDescriptorCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + result = ma_device_init_internal__sdl(pDevice, pContextStateSDL, pDeviceStateSDL, pDeviceConfigSDL, ma_device_type_capture, pDescriptorCapture); if (result != MA_SUCCESS) { - ma_free(pDeviceDataSDL, &pDevice->pContext->allocationCallbacks); + ma_free(pDeviceStateSDL, ma_device_get_allocation_callbacks(pDevice)); return result; } } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_internal__sdl(pDevice, pConfig, pDescriptorPlayback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + result = ma_device_init_internal__sdl(pDevice, pContextStateSDL, pDeviceStateSDL, pDeviceConfigSDL, ma_device_type_playback, pDescriptorPlayback); if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ((MA_PFN_SDL_CloseAudioDevice)pContextDataSDL->SDL_CloseAudioDevice)(pDeviceDataSDL->deviceIDCapture); + if (deviceType == ma_device_type_duplex) { + pContextStateSDL->SDL_CloseAudioDevice(pDeviceStateSDL->capture.deviceID); } - ma_free(pDeviceDataSDL, &pDevice->pContext->allocationCallbacks); + ma_free(pDeviceStateSDL, ma_device_get_allocation_callbacks(pDevice)); return result; } } - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__sdl(void* pUserData, ma_device* pDevice) -{ - ma_context_data_sdl* pContextDataSDL; - ma_device_data_sdl* pDeviceDataSDL; - - (void)pUserData; - - pContextDataSDL = (ma_context_data_sdl*)pDevice->pContext->pBackendData; - pDeviceDataSDL = (ma_device_data_sdl*)pDevice->pBackendData; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_SDL_CloseAudioDevice)pContextDataSDL->SDL_CloseAudioDevice)(pDeviceDataSDL->deviceIDCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_SDL_CloseAudioDevice)pContextDataSDL->SDL_CloseAudioDevice)(pDeviceDataSDL->deviceIDCapture); - } - - ma_free(pDeviceDataSDL, &pDevice->pContext->allocationCallbacks); + *ppDeviceState = pDeviceStateSDL; return MA_SUCCESS; } -static ma_result ma_device_start__sdl(void* pUserData, ma_device* pDevice) +static void ma_device_uninit__sdl(ma_device* pDevice) { - ma_context_data_sdl* pContextDataSDL; - ma_device_data_sdl* pDeviceDataSDL; + ma_device_state_sdl* pDeviceStateSDL = ma_device_get_backend_state__sdl(pDevice); + ma_context_state_sdl* pContextStateSDL = ma_context_get_backend_state__sdl(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); - (void)pUserData; - - pContextDataSDL = (ma_context_data_sdl*)pDevice->pContext->pBackendData; - pDeviceDataSDL = (ma_device_data_sdl*)pDevice->pBackendData; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_SDL_PauseAudioDevice)pContextDataSDL->SDL_PauseAudioDevice)(pDeviceDataSDL->deviceIDCapture, 0); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStateSDL->SDL_CloseAudioDevice(pDeviceStateSDL->capture.deviceID); } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_SDL_PauseAudioDevice)pContextDataSDL->SDL_PauseAudioDevice)(pDeviceDataSDL->deviceIDPlayback, 0); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStateSDL->SDL_CloseAudioDevice(pDeviceStateSDL->playback.deviceID); + } + + ma_free(pDeviceStateSDL, ma_device_get_allocation_callbacks(pDevice)); +} + +static ma_result ma_device_start__sdl(ma_device* pDevice) +{ + ma_device_state_sdl* pDeviceStateSDL = ma_device_get_backend_state__sdl(pDevice); + ma_context_state_sdl* pContextStateSDL = ma_context_get_backend_state__sdl(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStateSDL->SDL_PauseAudioDevice(pDeviceStateSDL->capture.deviceID, 0); + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStateSDL->SDL_PauseAudioDevice(pDeviceStateSDL->playback.deviceID, 0); } return MA_SUCCESS; } -static ma_result ma_device_stop__sdl(void* pUserData, ma_device* pDevice) +static ma_result ma_device_stop__sdl(ma_device* pDevice) { - ma_context_data_sdl* pContextDataSDL; - ma_device_data_sdl* pDeviceDataSDL; + ma_device_state_sdl* pDeviceStateSDL = ma_device_get_backend_state__sdl(pDevice); + ma_context_state_sdl* pContextStateSDL = ma_context_get_backend_state__sdl(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); - (void)pUserData; - - pContextDataSDL = (ma_context_data_sdl*)pDevice->pContext->pBackendData; - pDeviceDataSDL = (ma_device_data_sdl*)pDevice->pBackendData; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_SDL_PauseAudioDevice)pContextDataSDL->SDL_PauseAudioDevice)(pDeviceDataSDL->deviceIDCapture, 1); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStateSDL->SDL_PauseAudioDevice(pDeviceStateSDL->capture.deviceID, 1); } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_SDL_PauseAudioDevice)pContextDataSDL->SDL_PauseAudioDevice)(pDeviceDataSDL->deviceIDPlayback, 1); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__sdl(void* pUserData, ma_context* pContext) -{ - ma_context_data_sdl* pContextDataSDL; - - (void)pUserData; - - pContextDataSDL = (ma_context_data_sdl*)pContext->pBackendData; - - ((MA_PFN_SDL_QuitSubSystem)pContextDataSDL->SDL_QuitSubSystem)(MA_SDL_INIT_AUDIO); - - /* Close the handle to the SDL shared object last. */ - ma_dlclose(ma_context_get_log(pContext), pContextDataSDL->hSDL); - pContextDataSDL->hSDL = NULL; - - ma_free(pContextDataSDL, &pContext->allocationCallbacks); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__sdl(void* pUserData, ma_context* pContext, const ma_context_config* pConfig) -{ - ma_context_data_sdl* pContextDataSDL; - const ma_context_config_sdl* pContextConfigSDL; - int resultSDL; - -#ifndef MA_NO_RUNTIME_LINKING - /* We'll use a list of possible shared object names for easier extensibility. */ - size_t iName; - const char* pSDLNames[] = { -#if defined(_WIN32) - "SDL2.dll" -#elif defined(__APPLE__) - "SDL2.framework/SDL2" -#else - "libSDL2-2.0.so.0" -#endif - }; - - (void)pUserData; - - /* Grab the config. This is not currently being used. */ - pContextConfigSDL = (const ma_context_config_sdl*)ma_context_config_find_custom_backend_config(pConfig, MA_DEVICE_BACKEND_VTABLE_SDL); - (void)pContextConfigSDL; - - - /* Allocate our SDL-specific context data. */ - pContextDataSDL = (ma_context_data_sdl*)ma_calloc(sizeof(*pContextDataSDL), &pContext->allocationCallbacks); - if (pContextDataSDL == NULL) { - return MA_OUT_OF_MEMORY; - } - - pContext->pBackendData = pContextDataSDL; - - - /* Check if we have SDL2 installed somewhere. If not it's not usable and we need to abort. */ - for (iName = 0; iName < ma_countof(pSDLNames); iName += 1) { - pContextDataSDL->hSDL = ma_dlopen(ma_context_get_log(pContext), pSDLNames[iName]); - if (pContextDataSDL->hSDL != NULL) { - break; - } - } - - if (pContextDataSDL->hSDL == NULL) { - ma_free(pContextDataSDL, &pContext->allocationCallbacks); - return MA_NO_BACKEND; /* SDL2 could not be loaded. */ - } - - /* Now that we have the handle to the shared object we can go ahead and load some function pointers. */ - pContextDataSDL->SDL_InitSubSystem = ma_dlsym(ma_context_get_log(pContext), pContextDataSDL->hSDL, "SDL_InitSubSystem"); - pContextDataSDL->SDL_QuitSubSystem = ma_dlsym(ma_context_get_log(pContext), pContextDataSDL->hSDL, "SDL_QuitSubSystem"); - pContextDataSDL->SDL_GetNumAudioDevices = ma_dlsym(ma_context_get_log(pContext), pContextDataSDL->hSDL, "SDL_GetNumAudioDevices"); - pContextDataSDL->SDL_GetAudioDeviceName = ma_dlsym(ma_context_get_log(pContext), pContextDataSDL->hSDL, "SDL_GetAudioDeviceName"); - pContextDataSDL->SDL_CloseAudioDevice = ma_dlsym(ma_context_get_log(pContext), pContextDataSDL->hSDL, "SDL_CloseAudioDevice"); - pContextDataSDL->SDL_OpenAudioDevice = ma_dlsym(ma_context_get_log(pContext), pContextDataSDL->hSDL, "SDL_OpenAudioDevice"); - pContextDataSDL->SDL_PauseAudioDevice = ma_dlsym(ma_context_get_log(pContext), pContextDataSDL->hSDL, "SDL_PauseAudioDevice"); -#else - pContextDataSDL->SDL_InitSubSystem = (ma_proc)SDL_InitSubSystem; - pContextDataSDL->SDL_QuitSubSystem = (ma_proc)SDL_QuitSubSystem; - pContextDataSDL->SDL_GetNumAudioDevices = (ma_proc)SDL_GetNumAudioDevices; - pContextDataSDL->SDL_GetAudioDeviceName = (ma_proc)SDL_GetAudioDeviceName; - pContextDataSDL->SDL_CloseAudioDevice = (ma_proc)SDL_CloseAudioDevice; - pContextDataSDL->SDL_OpenAudioDevice = (ma_proc)SDL_OpenAudioDevice; - pContextDataSDL->SDL_PauseAudioDevice = (ma_proc)SDL_PauseAudioDevice; -#endif /* MA_NO_RUNTIME_LINKING */ - - resultSDL = ((MA_PFN_SDL_InitSubSystem)pContextDataSDL->SDL_InitSubSystem)(MA_SDL_INIT_AUDIO); - if (resultSDL != 0) { - ma_dlclose(ma_context_get_log(pContext), pContextDataSDL->hSDL); - ma_free(pContextDataSDL, &pContext->allocationCallbacks); - return MA_ERROR; + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStateSDL->SDL_PauseAudioDevice(pDeviceStateSDL->playback.deviceID, 1); } return MA_SUCCESS; @@ -604,6 +610,7 @@ static ma_result ma_context_init__sdl(void* pUserData, ma_context* pContext, con static ma_device_backend_vtable ma_gDeviceBackendVTable_SDL = { + ma_backend_info__sdl, ma_context_init__sdl, ma_context_uninit__sdl, ma_context_enumerate_devices__sdl, @@ -614,23 +621,13 @@ static ma_device_backend_vtable ma_gDeviceBackendVTable_SDL = ma_device_stop__sdl, NULL, /* onDeviceRead */ NULL, /* onDeviceWrite */ - NULL, /* onDeviceDataLoop */ - NULL, /* onDeviceDataLoopWakeup */ - NULL, /* onDeviceGetInfo */ - - /* Temp. */ - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ }; -const ma_device_backend_vtable* MA_DEVICE_BACKEND_VTABLE_SDL = &ma_gDeviceBackendVTable_SDL; +ma_device_backend_vtable* ma_device_backend_sdl = &ma_gDeviceBackendVTable_SDL; #else -const ma_device_backend_vtable* MA_DEVICE_BACKEND_VTABLE_SDL = NULL; +ma_device_backend_vtable* ma_device_backend_sdl = NULL; #endif /* MA_HAS_SDL */ diff --git a/extras/backends/sdl/backend_sdl.h b/extras/backends/sdl/backend_sdl.h index da553bcc..36055f5e 100644 --- a/extras/backends/sdl/backend_sdl.h +++ b/extras/backends/sdl/backend_sdl.h @@ -10,7 +10,7 @@ and device configs. extern "C" { #endif -extern const ma_device_backend_vtable* MA_DEVICE_BACKEND_VTABLE_SDL; +extern ma_device_backend_vtable* ma_device_backend_sdl; typedef struct diff --git a/miniaudio.h b/miniaudio.h index aa5137a5..3102ec4e 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -6661,6 +6661,14 @@ This section contains the APIs for device playback and capture. Here is where yo ************************************************************************************************************************************************************* ************************************************************************************************************************************************************/ #ifndef MA_NO_DEVICE_IO + +typedef struct ma_device_backend_vtable ma_device_backend_vtable; + +/************************************************************************************************************************************************************ + +BACKENDS + +************************************************************************************************************************************************************/ /* Some backends are only supported on certain platforms. */ #if defined(MA_WIN32) #define MA_SUPPORT_WASAPI @@ -6724,15 +6732,6 @@ This section contains the APIs for device playback and capture. Here is where yo #if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) #define MA_HAS_WINMM #endif -#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) - #define MA_HAS_ALSA -#endif -#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) - #define MA_HAS_PULSEAUDIO -#endif -#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) - #define MA_HAS_JACK -#endif #if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) #define MA_HAS_COREAUDIO #endif @@ -6745,6 +6744,15 @@ This section contains the APIs for device playback and capture. Here is where yo #if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) #define MA_HAS_OSS #endif +#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) + #define MA_HAS_PULSEAUDIO +#endif +#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) + #define MA_HAS_ALSA +#endif +#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) + #define MA_HAS_JACK +#endif #if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) #define MA_HAS_AAUDIO #endif @@ -6754,13 +6762,465 @@ This section contains the APIs for device playback and capture. Here is where yo #if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) #define MA_HAS_WEBAUDIO #endif -#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) - #define MA_HAS_CUSTOM -#endif #if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) #define MA_HAS_NULL #endif +/* BEG WASAPI */ +/* WASAPI audio thread priority characteristics. */ +typedef enum +{ + ma_wasapi_usage_default = 0, + ma_wasapi_usage_games, + ma_wasapi_usage_pro_audio, +} ma_wasapi_usage; + + +typedef struct ma_context_config_wasapi +{ + int _unused; +} ma_context_config_wasapi; + +MA_API ma_context_config_wasapi ma_context_config_wasapi_init(void); + + +typedef struct ma_device_config_wasapi +{ + ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ + ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ + ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ + ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ +} ma_device_config_wasapi; + +MA_API ma_device_config_wasapi ma_device_config_wasapi_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_wasapi; +/* END WASAPI */ + + +/* BEG DSOUND */ +typedef struct ma_context_config_dsound +{ + ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */ +} ma_context_config_dsound; + +MA_API ma_context_config_dsound ma_context_config_dsound_init(void); + + +typedef struct ma_device_config_dsound +{ + int _unused; +} ma_device_config_dsound; + +MA_API ma_device_config_dsound ma_device_config_dsound_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_dsound; +/* END DSOUND */ + + +/* BEG WINMM */ +typedef struct ma_context_config_winmm +{ + int _unused; +} ma_context_config_winmm; + +MA_API ma_context_config_winmm ma_context_config_winmm_init(void); + + +typedef struct ma_device_config_winmm +{ + int _unused; +} ma_device_config_winmm; + +MA_API ma_device_config_winmm ma_device_config_winmm_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_winmm; +/* END WINMM */ + + +/* BEG COREAUDIO */ +/* iOS/tvOS/watchOS session categories. */ +typedef enum +{ + ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ + ma_ios_session_category_none, /* Leave the session category unchanged. */ + ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ + ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ + ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ + ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ + ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ + ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ +} ma_ios_session_category; + +/* iOS/tvOS/watchOS session category options */ +typedef enum +{ + ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ + ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ + ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ + ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ + ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ + ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ + ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ +} ma_ios_session_category_option; + + +typedef struct ma_context_config_coreaudio +{ + ma_ios_session_category sessionCategory; + ma_uint32 sessionCategoryOptions; + ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ + ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ +} ma_context_config_coreaudio; + +MA_API ma_context_config_coreaudio ma_context_config_coreaudio_init(void); + + +typedef struct ma_device_config_coreaudio +{ + ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ +} ma_device_config_coreaudio; + +MA_API ma_device_config_coreaudio ma_device_config_coreaudio_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_coreaudio; +/* END COREAUDIO */ + + +/* BEG SNDIO */ +typedef struct ma_context_config_sndio +{ + int _unused; +} ma_context_config_sndio; + +MA_API ma_context_config_sndio ma_context_config_sndio_init(void); + + +typedef struct ma_device_config_sndio +{ + int _unused; +} ma_device_config_sndio; + +MA_API ma_device_config_sndio ma_device_config_sndio_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_sndio; +/* END SNDIO */ + + +/* BEG AUDIO4 */ +typedef struct ma_context_config_audio4 +{ + int _unused; +} ma_context_config_audio4; + +MA_API ma_context_config_audio4 ma_context_config_audio4_init(void); + + +typedef struct ma_device_config_audio4 +{ + int _unused; +} ma_device_config_audio4; + +MA_API ma_device_config_audio4 ma_device_config_audio4_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_audio4; +/* END AUDIO4 */ + + +/* BEG OSS */ +typedef struct ma_context_config_oss +{ + int _unused; +} ma_context_config_oss; + +MA_API ma_context_config_oss ma_context_config_oss_init(void); + + +typedef struct ma_device_config_oss +{ + int _unused; +} ma_device_config_oss; + +MA_API ma_device_config_oss ma_device_config_oss_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_oss; +/* END OSS */ + + +/* BEG PULSEAUDIO */ +typedef struct ma_context_config_pulseaudio +{ + const char* pApplicationName; + const char* pServerName; + ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ +} ma_context_config_pulseaudio; + +MA_API ma_context_config_pulseaudio ma_context_config_pulseaudio_init(void); + + +typedef struct ma_device_config_pulseaudio +{ + const char* pStreamNamePlayback; + const char* pStreamNameCapture; + int channelMap; +} ma_device_config_pulseaudio; + +MA_API ma_device_config_pulseaudio ma_device_config_pulseaudio_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_pulseaudio; +/* END PULSEAUDIO */ + + +/* BEG ALSA */ +typedef struct ma_context_config_alsa +{ + ma_bool32 useVerboseDeviceEnumeration; +} ma_context_config_alsa; + +MA_API ma_context_config_alsa ma_context_config_alsa_init(void); + + +typedef struct ma_device_config_alsa +{ + ma_bool32 noMMap; /* Disables MMap mode. */ + ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ + ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ + ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ +} ma_device_config_alsa; + +MA_API ma_device_config_alsa ma_device_config_alsa_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_alsa; +/* END ALSA */ + + +/* BEG JACK */ +typedef struct ma_context_config_jack +{ + const char* pClientName; + ma_bool32 tryStartServer; +} ma_context_config_jack; + +MA_API ma_context_config_jack ma_context_config_jack_init(void); + + +typedef struct ma_device_config_jack +{ + int _unused; +} ma_device_config_jack; + +MA_API ma_device_config_jack ma_device_config_jack_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_jack; +/* END JACK */ + + +/* BEG AAUDIO */ +/* AAudio usage types. */ +typedef enum +{ + ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ + ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ + ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ + ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ + ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ + ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ + ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ + ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ + ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ + ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ + ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ + ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ + ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ + ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ + ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ + ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ + ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ +} ma_aaudio_usage; + +/* AAudio content types. */ +typedef enum +{ + ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ + ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ + ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ + ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ + ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ +} ma_aaudio_content_type; + +/* AAudio input presets. */ +typedef enum +{ + ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ + ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ + ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ + ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ + ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ + ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ + ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ +} ma_aaudio_input_preset; + +typedef enum +{ + ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ + ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ + ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ + ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ +} ma_aaudio_allowed_capture_policy; + +typedef enum +{ + ma_aaudio_performance_mode_default = 0, /* Leaves the performance mode unset. */ + ma_aaudio_performance_mode_low_latency, /* MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY */ + ma_aaudio_performance_mode_none, /* MA_AAUDIO_PERFORMANCE_MODE_NONE */ + ma_aaudio_performance_mode_power_saving /* MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING */ +} ma_aaudio_performance_mode; + + +typedef struct ma_context_config_aaudio +{ + int _unused; +} ma_context_config_aaudio; + +MA_API ma_context_config_aaudio ma_context_config_aaudio_init(void); + + +typedef struct ma_device_config_aaudio +{ + ma_aaudio_usage usage; + ma_aaudio_content_type contentType; + ma_aaudio_input_preset inputPreset; + ma_aaudio_allowed_capture_policy allowedCapturePolicy; + ma_aaudio_performance_mode performanceMode; + ma_bool32 noAutoStartAfterReroute; + ma_bool32 enableCompatibilityWorkarounds; + ma_bool32 allowSetBufferCapacity; +} ma_device_config_aaudio; + +MA_API ma_device_config_aaudio ma_device_config_aaudio_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_aaudio; +/* END AAUDIO */ + + +/* BEG OPENSL */ +/* OpenSL stream types. */ +typedef enum +{ + ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ + ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ + ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ + ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ + ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ + ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ + ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ +} ma_opensl_stream_type; + +/* OpenSL recording presets. */ +typedef enum +{ + ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ + ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ + ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ + ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ + ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ + ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ +} ma_opensl_recording_preset; + + +typedef struct ma_context_config_opensl +{ + int _unused; +} ma_context_config_opensl; + +MA_API ma_context_config_opensl ma_context_config_opensl_init(void); + + +typedef struct ma_device_config_opensl +{ + ma_opensl_stream_type streamType; + ma_opensl_recording_preset recordingPreset; + ma_bool32 enableCompatibilityWorkarounds; +} ma_device_config_opensl; + +MA_API ma_device_config_opensl ma_device_config_opensl_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_opensl; +/* END OPENSL */ + + +/* BEG WEBAUDIO */ +typedef enum ma_webaudio_latency_hint +{ + ma_webaudio_latency_hint_interactive = 0, + ma_webaudio_latency_hint_playback, + ma_webaudio_latency_hint_balanced +} ma_webaudio_latency_hint; + + +typedef struct ma_context_config_webaudio +{ + int _unused; +} ma_context_config_webaudio; + +MA_API ma_context_config_webaudio ma_context_config_webaudio_init(void); + + +typedef struct ma_device_config_webaudio +{ + ma_webaudio_latency_hint latencyHint; +} ma_device_config_webaudio; + +MA_API ma_device_config_webaudio ma_device_config_webaudio_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_webaudio; +/* END WEBAUDIO */ + + +/* BEG NULL */ +typedef struct ma_context_config_null +{ + int _unused; +} ma_context_config_null; + +MA_API ma_context_config_null ma_context_config_null_init(void); + + +typedef struct ma_device_config_null +{ + int _unused; +} ma_device_config_null; + +MA_API ma_device_config_null ma_device_config_null_init(void); + + +extern ma_device_backend_vtable* ma_device_backend_null; +/* END NULL */ + + +/************************************************************************************************************************************************************ + +END BACKENDS + +************************************************************************************************************************************************************/ + + typedef enum { ma_device_state_uninitialized = 0, @@ -6869,6 +7329,10 @@ typedef struct { int _unused; } interruption; + struct + { + int _unused; + } unlocked; } data; } ma_device_notification; @@ -6973,113 +7437,6 @@ typedef enum ma_share_mode_exclusive } ma_share_mode; -/* iOS/tvOS/watchOS session categories. */ -typedef enum -{ - ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ - ma_ios_session_category_none, /* Leave the session category unchanged. */ - ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ - ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ - ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ - ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ - ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ - ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ -} ma_ios_session_category; - -/* iOS/tvOS/watchOS session category options */ -typedef enum -{ - ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ - ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ - ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ - ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ - ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ - ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ - ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ -} ma_ios_session_category_option; - -/* OpenSL stream types. */ -typedef enum -{ - ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ - ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ - ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ - ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ - ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ - ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ - ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ -} ma_opensl_stream_type; - -/* OpenSL recording presets. */ -typedef enum -{ - ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ - ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ - ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ - ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ - ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ - ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ -} ma_opensl_recording_preset; - -/* WASAPI audio thread priority characteristics. */ -typedef enum -{ - ma_wasapi_usage_default = 0, - ma_wasapi_usage_games, - ma_wasapi_usage_pro_audio, -} ma_wasapi_usage; - -/* AAudio usage types. */ -typedef enum -{ - ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ - ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ - ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ - ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ - ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ - ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ - ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ - ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ - ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ - ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ - ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ - ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ - ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ - ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ - ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ - ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ - ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ -} ma_aaudio_usage; - -/* AAudio content types. */ -typedef enum -{ - ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ - ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ - ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ - ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ - ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ -} ma_aaudio_content_type; - -/* AAudio input presets. */ -typedef enum -{ - ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ - ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ - ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ - ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ - ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ - ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ - ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ -} ma_aaudio_input_preset; - -typedef enum -{ - ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ - ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ - ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ - ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ -} ma_aaudio_allowed_capture_policy; typedef union { @@ -7102,58 +7459,82 @@ typedef union ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ + int nullbackend; /* The null backend uses an integer for device IDs. */ union { int i; char s[256]; void* p; - } custom; /* The custom backend could be anything. Give them a few options. */ - int nullbackend; /* The null backend uses an integer for device IDs. */ + } custom; /* External backends could be anything. Give them a few options. */ } ma_device_id; MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB); /* -This data structure is used to bridge the backend with the device's data callback. When a backend needs to -invoke the data callback, it should call `ma_device_data_callback_invoke()`. When the backend needs data for -for playback, the backend should pass in a value for `pFramesOutInBackendFormat` which will be in the -backend's native format. Likewise for capture, when the backend is ready to push data to the application from -the microphone, it should pass in a value for `pFramesInInBackendFormat`, which again is in the backend's -native format. - -The `ma_device_data_callback` structure should be considered opaque. It is declared here in the header section -so it can be stored directly in the `ma_device` object without needing a heap allocation. +Describes some basic details about a playback or capture device. */ -typedef struct ma_device_data_callback -{ - void* pInternal; -} ma_device_data_callback; - -MA_API void ma_device_data_callback_invoke(ma_device_data_callback* pDeviceDataCallback, void* pFramesOutInBackendFormat, const void* pFramesInInBackendFormat, ma_uint32 frameCount); - - -typedef struct ma_context_config ma_context_config; -typedef struct ma_device_config ma_device_config; -typedef struct ma_device_backend_vtable ma_device_backend_vtable; -typedef struct ma_device_backend_vtable2 ma_device_backend_vtable2; -typedef struct ma_backend_callbacks ma_backend_callbacks; - -/* For defining vtables, configs and user data pointers for custom backends. */ typedef struct { - const ma_device_backend_vtable* pVTable; /* The vtable of the backend. */ - const void* pConfig; /* Contextual based on whether or not this is being used for a context or device. The object this points to depends on the backend. */ - void* pUserData; /* User data pointer passed into each callback in the vtable. */ -} ma_device_backend_spec; + const ma_device_id* pDeviceID; + ma_share_mode shareMode; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_channel channelMap[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFrames; + ma_uint32 periodSizeInMilliseconds; + ma_uint32 periodCount; +} ma_device_descriptor; + +typedef struct ma_device_backend_info +{ + const char* pName; +} ma_device_backend_info; + + +/* +This data structure is used to bridge backends to miniaudio. There are two main uses for it: + + 1) To handle the data callback from inside the backend + 2) To notify miniaudio of a reroute so it can do any necessary data conversion reinitialization + +Not all backends need to use this, but many will. If the backend uses a blocking read/write mechanism +to deliver audio data, they need not deal with the data callback aspect. If the backend handles +rerouting automatically under the hood it need not worry about the rerouting part. + +The `ma_device_backend_connection` structure should be considered opaque. It is declared here in the header +section so it can be stored directly in the `ma_device` object without needing a heap allocation. +*/ +typedef struct ma_device_backend_connection +{ + void* pInternal; +} ma_device_backend_connection; + +MA_API void ma_device_backend_connection_data_callback(ma_device_backend_connection* pDeviceBackendConnection, void* pFramesOutInBackendFormat, const void* pFramesInInBackendFormat, ma_uint32 frameCount); +MA_API void ma_device_backend_connection_reroute(ma_device_backend_connection* pDeviceBackendConnection, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void* pNewDeviceState); +MA_API void ma_device_backend_connection_rerouted_notification(ma_device_backend_connection* pDeviceBackendConnection); +MA_API void ma_device_backend_connection_stopped_notification(ma_device_backend_connection* pDeviceBackendConnection); +MA_API void ma_device_backend_connection_started_notification(ma_device_backend_connection* pDeviceBackendConnection); +MA_API void ma_device_backend_connection_interruption_began_notification(ma_device_backend_connection* pDeviceBackendConnection); +MA_API void ma_device_backend_connection_interruption_ended_notification(ma_device_backend_connection* pDeviceBackendConnection); +MA_API ma_device_state ma_device_backend_connection_status(ma_device_backend_connection* pDeviceBackendConnection); +MA_API void ma_device_backend_connection_start_device(ma_device_backend_connection* pDeviceBackendConnection); +MA_API void ma_device_backend_connection_stop_device(ma_device_backend_connection* pDeviceBackendConnection); + + +typedef struct ma_context_config ma_context_config; +typedef struct ma_device_config ma_device_config; /* For mapping a config object to a backend. Used for both context configs or device configs. */ typedef struct ma_device_backend_config { - const ma_device_backend_vtable2* pBackend; - void* pConfig; /* Can be a pointer to either a backend-specific context config, or a backend-specific device config, depending on context. */ + ma_device_backend_vtable* pVTable; + const void* pConfig; /* Can be a pointer to either a backend-specific context config, or a backend-specific device config, depending on context. */ } ma_device_backend_config; +MA_API ma_device_backend_config ma_device_backend_config_init(ma_device_backend_vtable* pVTable, const void* pConfig); + #define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ @@ -7193,6 +7574,8 @@ struct ma_device_config ma_device_data_proc dataCallback; ma_device_notification_proc notificationCallback; void* pUserData; + ma_device_backend_config* pBackendConfigs; + size_t backendConfigCount; ma_resampler_config resampling; struct { @@ -7215,54 +7598,21 @@ struct ma_device_config ma_share_mode shareMode; } capture; - struct - { - ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ - ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ - ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ - ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ - } wasapi; - struct - { - ma_bool32 noMMap; /* Disables MMap mode. */ - ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ - ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ - ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ - } alsa; - struct - { - const char* pStreamNamePlayback; - const char* pStreamNameCapture; - int channelMap; - } pulse; - struct - { - ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ - } coreaudio; - struct - { - ma_opensl_stream_type streamType; - ma_opensl_recording_preset recordingPreset; - ma_bool32 enableCompatibilityWorkarounds; - } opensl; - struct - { - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - ma_bool32 enableCompatibilityWorkarounds; - ma_bool32 allowSetBufferCapacity; - } aaudio; - struct - { - ma_device_backend_spec* pBackends; - size_t count; - } custom; + /* Stock backend configs. */ + ma_device_config_wasapi wasapi; + ma_device_config_dsound dsound; + ma_device_config_winmm winmm; + ma_device_config_coreaudio coreaudio; + ma_device_config_sndio sndio; + ma_device_config_audio4 audio4; + ma_device_config_oss oss; + ma_device_config_pulseaudio pulseaudio; + ma_device_config_alsa alsa; + ma_device_config_jack jack; + ma_device_config_aaudio aaudio; + ma_device_config_opensl opensl; + ma_device_config_webaudio webaudio; + ma_device_config_null null_backend; }; @@ -7286,22 +7636,7 @@ pUserData (in) typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); -/* -Describes some basic details about a playback or capture device. -*/ -typedef struct -{ - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periodCount; -} ma_device_descriptor; - +/* TODO: THIS SECTION NEEDS TO BE UPDATED FOR THE NEW BACKEND SYSTEM. */ /* These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context to many devices. A device is created from a context. @@ -7361,78 +7696,27 @@ which will allow it to implement the logic that will run on the audio thread. Th The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. -The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this +The invocation of the `onDeviceLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated -which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, -look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to +which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceLoop()` callback, +look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceWakeup()` callback if you need a mechanism to wake up the audio thread. - -If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the -`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. */ -struct ma_backend_callbacks -{ - ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); - ma_result (* onContextUninit)(ma_context* pContext); - ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); - ma_result (* onDeviceUninit)(ma_device* pDevice); - ma_result (* onDeviceStart)(ma_device* pDevice); - ma_result (* onDeviceStop)(ma_device* pDevice); - ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); - ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); - ma_result (* onDeviceDataLoop)(ma_device* pDevice); - ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); - ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); -}; - struct ma_device_backend_vtable { - ma_result (* onContextInit )(void* pUserData, ma_context* pContext, const ma_context_config* pConfig); - ma_result (* onContextUninit )(void* pUserData, ma_context* pContext); - ma_result (* onContextEnumerateDevices)(void* pUserData, ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData); - ma_result (* onContextGetDeviceInfo )(void* pUserData, ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - ma_result (* onDeviceInit )(void* pUserData, ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); - ma_result (* onDeviceUninit )(void* pUserData, ma_device* pDevice); - ma_result (* onDeviceStart )(void* pUserData, ma_device* pDevice); - ma_result (* onDeviceStop )(void* pUserData, ma_device* pDevice); - ma_result (* onDeviceRead )(void* pUserData, ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); - ma_result (* onDeviceWrite )(void* pUserData, ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); - ma_result (* onDeviceDataLoop )(void* pUserData, ma_device* pDevice); - ma_result (* onDeviceDataLoopWakeup )(void* pUserData, ma_device* pDevice); - ma_result (* onDeviceGetInfo )(void* pUserData, ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); - - /* This is temporary while we migrate backends over to the new callback system. */ - ma_bool32 (* onHasStart )(void* pUserData, ma_context* pContext); - ma_bool32 (* onHasStop )(void* pUserData, ma_context* pContext); - ma_bool32 (* onHasRead )(void* pUserData, ma_context* pContext); - ma_bool32 (* onHasWrite )(void* pUserData, ma_context* pContext); - ma_bool32 (* onHasDataLoop )(void* pUserData, ma_context* pContext); - ma_bool32 (* onHasDataLoopWakeup)(void* pUserData, ma_context* pContext); - ma_bool32 (* onHasDeviceGetInfo )(void* pUserData, ma_context* pContext); -}; - - -typedef struct ma_backend_info -{ - const char* pName; -} ma_backend_info; - -struct ma_device_backend_vtable2 -{ - void (* onBackendInfo )(ma_backend_info* pBackendInfo); - ma_result (* onContextInit )(const ma_context_config* pConfig, ma_log* pLog, const ma_allocation_callbacks* pAllocationCallbacks, void** ppContextState); - void (* onContextUninit )(void* pContextState, const ma_allocation_callbacks* pAllocationCallbacks); - ma_result (* onContextEnumerateDevices)(void* pContextState, ma_enum_devices_callback_proc callback, void* pCallbackUserData); - ma_result (* onContextGetDeviceInfo )(void* pContextState, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - ma_result (* onDeviceInit )(void* pContextState, const ma_device_config* pConfig, ma_device_data_callback* pDeviceDataCallback, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState); - void (* onDeviceUninit )(void* pDeviceState, const ma_allocation_callbacks* pAllocationCallbacks); - ma_result (* onDeviceStart )(void* pDeviceState); - ma_result (* onDeviceStop )(void* pDeviceState); - ma_result (* onDeviceRead )(void* pDeviceState, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); - ma_result (* onDeviceWrite )(void* pDeviceState, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); - ma_result (* onDeviceGetInfo )(void* pDeviceState, ma_device_type type, ma_device_info* pDeviceInfo); + void (* onBackendInfo )(ma_device_backend_info* pBackendInfo); + ma_result (* onContextInit )(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState); + void (* onContextUninit )(ma_context* pContext); + ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData); + ma_result (* onContextGetDeviceInfo )(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); + ma_result (* onDeviceInit )(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState); + void (* onDeviceUninit )(ma_device* pDevice); + ma_result (* onDeviceStart )(ma_device* pDevice); + ma_result (* onDeviceStop )(ma_device* pDevice); + ma_result (* onDeviceRead )(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); + ma_result (* onDeviceWrite )(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); + void (* onDeviceLoop )(ma_device* pDevice); + void (* onDeviceWakeup )(ma_device* pDevice); }; struct ma_context_config @@ -7442,72 +7726,27 @@ struct ma_context_config size_t threadStackSize; void* pUserData; ma_allocation_callbacks allocationCallbacks; - struct - { - ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */ - } dsound; - struct - { - ma_bool32 useVerboseDeviceEnumeration; - } alsa; - struct - { - const char* pApplicationName; - const char* pServerName; - ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ - } pulse; - struct - { - ma_ios_session_category sessionCategory; - ma_uint32 sessionCategoryOptions; - ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ - ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ - } coreaudio; - struct - { - const char* pClientName; - ma_bool32 tryStartServer; - } jack; - struct - { - ma_device_backend_spec* pBackends; - size_t count; - } custom; + + /* Stock backend configs. */ + ma_context_config_wasapi wasapi; + ma_context_config_dsound dsound; + ma_context_config_winmm winmm; + ma_context_config_coreaudio coreaudio; + ma_context_config_sndio sndio; + ma_context_config_audio4 audio4; + ma_context_config_oss oss; + ma_context_config_pulseaudio pulseaudio; + ma_context_config_alsa alsa; + ma_context_config_jack jack; + ma_context_config_aaudio aaudio; + ma_context_config_opensl opensl; + ma_context_config_webaudio webaudio; + ma_context_config_null null_backend; }; -/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ -typedef struct -{ - int code; - ma_event* pEvent; /* This will be signalled when the event is complete. */ - union - { - struct - { - int _unused; - } quit; - struct - { - ma_device_type deviceType; - void* pAudioClient; - void** ppAudioClientService; - ma_result* pResult; /* The result from creating the audio client service. */ - } createAudioClient; - struct - { - ma_device* pDevice; - ma_device_type deviceType; - } releaseAudioClient; - } data; -} ma_context_command__wasapi; - struct ma_context { - ma_backend_callbacks callbacks; /* Old system. Will be removed when all stock backends have been converted over to the new system. */ - const ma_device_backend_vtable* pVTable; /* New system. TODO: Delete this. */ - void* pVTableUserData; /* TODO: Delete this. */ - const ma_device_backend_vtable2* pVTable2; /* New new system. */ - void* pBackendData; /* TODO: Delete this. */ + ma_device_backend_vtable* pVTable; /* New new system. */ void* pBackendState; /* Backend state created by the backend. This will be passed to the relevant backend functions. */ ma_backend backend; /* DirectSound, ALSA, etc. */ ma_log* pLog; @@ -7523,408 +7762,41 @@ struct ma_context ma_uint32 captureDeviceInfoCount; ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - ma_thread commandThread; - ma_mutex commandLock; - ma_semaphore commandSem; - ma_uint32 commandIndex; - ma_uint32 commandCount; - ma_context_command__wasapi commands[4]; - ma_handle hAvrt; - ma_proc AvSetMmThreadCharacteristicsA; - ma_proc AvRevertMmThreadcharacteristics; - ma_handle hMMDevapi; - ma_proc ActivateAudioInterfaceAsync; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - ma_handle hWnd; /* Can be null. */ - ma_handle hDSoundDLL; - ma_proc DirectSoundCreate; - ma_proc DirectSoundEnumerateA; - ma_proc DirectSoundCaptureCreate; - ma_proc DirectSoundCaptureEnumerateA; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - ma_handle hWinMM; - ma_proc waveOutGetNumDevs; - ma_proc waveOutGetDevCapsA; - ma_proc waveOutOpen; - ma_proc waveOutClose; - ma_proc waveOutPrepareHeader; - ma_proc waveOutUnprepareHeader; - ma_proc waveOutWrite; - ma_proc waveOutReset; - ma_proc waveInGetNumDevs; - ma_proc waveInGetDevCapsA; - ma_proc waveInOpen; - ma_proc waveInClose; - ma_proc waveInPrepareHeader; - ma_proc waveInUnprepareHeader; - ma_proc waveInAddBuffer; - ma_proc waveInStart; - ma_proc waveInReset; - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - ma_handle asoundSO; - ma_proc snd_pcm_open; - ma_proc snd_pcm_close; - ma_proc snd_pcm_hw_params_sizeof; - ma_proc snd_pcm_hw_params_any; - ma_proc snd_pcm_hw_params_set_format; - ma_proc snd_pcm_hw_params_set_format_first; - ma_proc snd_pcm_hw_params_get_format_mask; - ma_proc snd_pcm_hw_params_set_channels; - ma_proc snd_pcm_hw_params_set_channels_near; - ma_proc snd_pcm_hw_params_set_channels_minmax; - ma_proc snd_pcm_hw_params_set_rate_resample; - ma_proc snd_pcm_hw_params_set_rate; - ma_proc snd_pcm_hw_params_set_rate_near; - ma_proc snd_pcm_hw_params_set_buffer_size_near; - ma_proc snd_pcm_hw_params_set_periods_near; - ma_proc snd_pcm_hw_params_set_access; - ma_proc snd_pcm_hw_params_get_format; - ma_proc snd_pcm_hw_params_get_channels; - ma_proc snd_pcm_hw_params_get_channels_min; - ma_proc snd_pcm_hw_params_get_channels_max; - ma_proc snd_pcm_hw_params_get_rate; - ma_proc snd_pcm_hw_params_get_rate_min; - ma_proc snd_pcm_hw_params_get_rate_max; - ma_proc snd_pcm_hw_params_get_buffer_size; - ma_proc snd_pcm_hw_params_get_periods; - ma_proc snd_pcm_hw_params_get_access; - ma_proc snd_pcm_hw_params_test_format; - ma_proc snd_pcm_hw_params_test_channels; - ma_proc snd_pcm_hw_params_test_rate; - ma_proc snd_pcm_hw_params; - ma_proc snd_pcm_sw_params_sizeof; - ma_proc snd_pcm_sw_params_current; - ma_proc snd_pcm_sw_params_get_boundary; - ma_proc snd_pcm_sw_params_set_avail_min; - ma_proc snd_pcm_sw_params_set_start_threshold; - ma_proc snd_pcm_sw_params_set_stop_threshold; - ma_proc snd_pcm_sw_params; - ma_proc snd_pcm_format_mask_sizeof; - ma_proc snd_pcm_format_mask_test; - ma_proc snd_pcm_get_chmap; - ma_proc snd_pcm_state; - ma_proc snd_pcm_prepare; - ma_proc snd_pcm_start; - ma_proc snd_pcm_drop; - ma_proc snd_pcm_drain; - ma_proc snd_pcm_reset; - ma_proc snd_device_name_hint; - ma_proc snd_device_name_get_hint; - ma_proc snd_card_get_index; - ma_proc snd_device_name_free_hint; - ma_proc snd_pcm_mmap_begin; - ma_proc snd_pcm_mmap_commit; - ma_proc snd_pcm_recover; - ma_proc snd_pcm_readi; - ma_proc snd_pcm_writei; - ma_proc snd_pcm_avail; - ma_proc snd_pcm_avail_update; - ma_proc snd_pcm_wait; - ma_proc snd_pcm_nonblock; - ma_proc snd_pcm_info; - ma_proc snd_pcm_info_sizeof; - ma_proc snd_pcm_info_get_name; - ma_proc snd_pcm_poll_descriptors; - ma_proc snd_pcm_poll_descriptors_count; - ma_proc snd_pcm_poll_descriptors_revents; - ma_proc snd_config_update_free_global; - - ma_mutex internalDeviceEnumLock; - ma_bool32 useVerboseDeviceEnumeration; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - ma_handle pulseSO; - ma_proc pa_mainloop_new; - ma_proc pa_mainloop_free; - ma_proc pa_mainloop_quit; - ma_proc pa_mainloop_get_api; - ma_proc pa_mainloop_iterate; - ma_proc pa_mainloop_wakeup; - ma_proc pa_threaded_mainloop_new; - ma_proc pa_threaded_mainloop_free; - ma_proc pa_threaded_mainloop_start; - ma_proc pa_threaded_mainloop_stop; - ma_proc pa_threaded_mainloop_lock; - ma_proc pa_threaded_mainloop_unlock; - ma_proc pa_threaded_mainloop_wait; - ma_proc pa_threaded_mainloop_signal; - ma_proc pa_threaded_mainloop_accept; - ma_proc pa_threaded_mainloop_get_retval; - ma_proc pa_threaded_mainloop_get_api; - ma_proc pa_threaded_mainloop_in_thread; - ma_proc pa_threaded_mainloop_set_name; - ma_proc pa_context_new; - ma_proc pa_context_unref; - ma_proc pa_context_connect; - ma_proc pa_context_disconnect; - ma_proc pa_context_set_state_callback; - ma_proc pa_context_get_state; - ma_proc pa_context_get_sink_info_list; - ma_proc pa_context_get_source_info_list; - ma_proc pa_context_get_sink_info_by_name; - ma_proc pa_context_get_source_info_by_name; - ma_proc pa_operation_unref; - ma_proc pa_operation_get_state; - ma_proc pa_channel_map_init_extend; - ma_proc pa_channel_map_valid; - ma_proc pa_channel_map_compatible; - ma_proc pa_stream_new; - ma_proc pa_stream_unref; - ma_proc pa_stream_connect_playback; - ma_proc pa_stream_connect_record; - ma_proc pa_stream_disconnect; - ma_proc pa_stream_get_state; - ma_proc pa_stream_get_sample_spec; - ma_proc pa_stream_get_channel_map; - ma_proc pa_stream_get_buffer_attr; - ma_proc pa_stream_set_buffer_attr; - ma_proc pa_stream_get_device_name; - ma_proc pa_stream_set_write_callback; - ma_proc pa_stream_set_read_callback; - ma_proc pa_stream_set_suspended_callback; - ma_proc pa_stream_set_moved_callback; - ma_proc pa_stream_is_suspended; - ma_proc pa_stream_flush; - ma_proc pa_stream_drain; - ma_proc pa_stream_is_corked; - ma_proc pa_stream_cork; - ma_proc pa_stream_trigger; - ma_proc pa_stream_begin_write; - ma_proc pa_stream_write; - ma_proc pa_stream_peek; - ma_proc pa_stream_drop; - ma_proc pa_stream_writable_size; - ma_proc pa_stream_readable_size; - - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - ma_handle jackSO; - ma_proc jack_client_open; - ma_proc jack_client_close; - ma_proc jack_client_name_size; - ma_proc jack_set_process_callback; - ma_proc jack_set_buffer_size_callback; - ma_proc jack_on_shutdown; - ma_proc jack_get_sample_rate; - ma_proc jack_get_buffer_size; - ma_proc jack_get_ports; - ma_proc jack_activate; - ma_proc jack_deactivate; - ma_proc jack_connect; - ma_proc jack_port_register; - ma_proc jack_port_name; - ma_proc jack_port_get_buffer; - ma_proc jack_free; - - char* pClientName; - ma_bool32 tryStartServer; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_handle hCoreFoundation; - ma_proc CFStringGetCString; - ma_proc CFRelease; - - ma_handle hCoreAudio; - ma_proc AudioObjectGetPropertyData; - ma_proc AudioObjectGetPropertyDataSize; - ma_proc AudioObjectSetPropertyData; - ma_proc AudioObjectAddPropertyListener; - ma_proc AudioObjectRemovePropertyListener; - - ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ - ma_proc AudioComponentFindNext; - ma_proc AudioComponentInstanceDispose; - ma_proc AudioComponentInstanceNew; - ma_proc AudioOutputUnitStart; - ma_proc AudioOutputUnitStop; - ma_proc AudioUnitAddPropertyListener; - ma_proc AudioUnitGetPropertyInfo; - ma_proc AudioUnitGetProperty; - ma_proc AudioUnitSetProperty; - ma_proc AudioUnitInitialize; - ma_proc AudioUnitRender; - - /*AudioComponent*/ ma_ptr component; - ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_handle sndioSO; - ma_proc sio_open; - ma_proc sio_close; - ma_proc sio_setpar; - ma_proc sio_getpar; - ma_proc sio_getcap; - ma_proc sio_start; - ma_proc sio_stop; - ma_proc sio_read; - ma_proc sio_write; - ma_proc sio_onmove; - ma_proc sio_nfds; - ma_proc sio_pollfd; - ma_proc sio_revents; - ma_proc sio_eof; - ma_proc sio_setvol; - ma_proc sio_onvol; - ma_proc sio_initpar; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int _unused; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int versionMajor; - int versionMinor; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - ma_handle hAAudio; /* libaaudio.so */ - ma_proc AAudio_createStreamBuilder; - ma_proc AAudioStreamBuilder_delete; - ma_proc AAudioStreamBuilder_setDeviceId; - ma_proc AAudioStreamBuilder_setDirection; - ma_proc AAudioStreamBuilder_setSharingMode; - ma_proc AAudioStreamBuilder_setFormat; - ma_proc AAudioStreamBuilder_setChannelCount; - ma_proc AAudioStreamBuilder_setSampleRate; - ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; - ma_proc AAudioStreamBuilder_setFramesPerDataCallback; - ma_proc AAudioStreamBuilder_setDataCallback; - ma_proc AAudioStreamBuilder_setErrorCallback; - ma_proc AAudioStreamBuilder_setPerformanceMode; - ma_proc AAudioStreamBuilder_setUsage; - ma_proc AAudioStreamBuilder_setContentType; - ma_proc AAudioStreamBuilder_setInputPreset; - ma_proc AAudioStreamBuilder_setAllowedCapturePolicy; - ma_proc AAudioStreamBuilder_openStream; - ma_proc AAudioStream_close; - ma_proc AAudioStream_getState; - ma_proc AAudioStream_waitForStateChange; - ma_proc AAudioStream_getFormat; - ma_proc AAudioStream_getChannelCount; - ma_proc AAudioStream_getSampleRate; - ma_proc AAudioStream_getBufferCapacityInFrames; - ma_proc AAudioStream_getFramesPerDataCallback; - ma_proc AAudioStream_getFramesPerBurst; - ma_proc AAudioStream_requestStart; - ma_proc AAudioStream_requestStop; - ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - ma_handle libOpenSLES; - ma_handle SL_IID_ENGINE; - ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; - ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - ma_handle SL_IID_RECORD; - ma_handle SL_IID_PLAY; - ma_handle SL_IID_OUTPUTMIX; - ma_handle SL_IID_ANDROIDCONFIGURATION; - ma_proc slCreateEngine; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - int _unused; - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - int _unused; - } null_backend; -#endif - }; - - union - { #if defined(MA_WIN32) - struct - { - /*HMODULE*/ ma_handle hOle32DLL; - ma_proc CoInitialize; - ma_proc CoInitializeEx; - ma_proc CoUninitialize; - ma_proc CoCreateInstance; - ma_proc CoTaskMemFree; - ma_proc PropVariantClear; - ma_proc StringFromGUID2; + struct + { + /*HMODULE*/ ma_handle hOle32DLL; + ma_proc CoInitialize; + ma_proc CoInitializeEx; + ma_proc CoUninitialize; + ma_proc CoCreateInstance; + ma_proc CoTaskMemFree; + ma_proc PropVariantClear; + ma_proc StringFromGUID2; - /*HMODULE*/ ma_handle hUser32DLL; - ma_proc GetForegroundWindow; - ma_proc GetDesktopWindow; + /*HMODULE*/ ma_handle hUser32DLL; + ma_proc GetForegroundWindow; + ma_proc GetDesktopWindow; - /*HMODULE*/ ma_handle hAdvapi32DLL; - ma_proc RegOpenKeyExA; - ma_proc RegCloseKey; - ma_proc RegQueryValueExA; + /*HMODULE*/ ma_handle hAdvapi32DLL; + ma_proc RegOpenKeyExA; + ma_proc RegCloseKey; + ma_proc RegQueryValueExA; - /*HRESULT*/ long CoInitializeResult; - } win32; + /*HRESULT*/ long CoInitializeResult; + } win32; #endif -#ifdef MA_POSIX - struct - { - int _unused; - } posix; -#endif - int _unused; - }; }; struct ma_device { ma_context* pContext; ma_device_type type; - ma_device_data_callback dataCallback; /* The data callback object for bridging the backend to the device. */ ma_uint32 sampleRate; ma_atomic_device_state state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ void* pUserData; /* Application defined data. */ - void* pBackendData; /* This is not used by miniaudio, but is a way for custom backends to associate some backend-specific data with the device. Custom backends are free to use this pointer however they like. */ void* pBackendState; /* Backend state created by the backend. This will be passed to the relevant backend functions. */ ma_mutex startStopLock; ma_event wakeupEvent; @@ -7938,7 +7810,7 @@ struct ma_device ma_bool8 noDisableDenormals; ma_bool8 noFixedSizedCallback; ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ - ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ + ma_duplex_rb duplexRB; /* Intermediary buffer for duplex devices on asynchronous backends. */ struct { ma_resample_algorithm algorithm; @@ -7995,221 +7867,6 @@ struct ma_device ma_uint32 intermediaryBufferCap; ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ } capture; - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - /*IAudioClient**/ ma_ptr pAudioClientPlayback; - /*IAudioClient**/ ma_ptr pAudioClientCapture; - /*IAudioRenderClient**/ ma_ptr pRenderClient; - /*IAudioCaptureClient**/ ma_ptr pCaptureClient; - /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ - ma_IMMNotificationClient notificationClient; - /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ - /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ - ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ - ma_uint32 actualBufferSizeInFramesCapture; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_uint32 periodSizeInFramesPlayback; - ma_uint32 periodSizeInFramesCapture; - void* pMappedBufferCapture; - ma_uint32 mappedBufferCaptureCap; - ma_uint32 mappedBufferCaptureLen; - void* pMappedBufferPlayback; - ma_uint32 mappedBufferPlaybackCap; - ma_uint32 mappedBufferPlaybackLen; - ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_uint32 loopbackProcessID; - ma_bool8 loopbackProcessExclude; - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noHardwareOffloading; - ma_bool8 allowCaptureAutoStreamRouting; - ma_bool8 allowPlaybackAutoStreamRouting; - ma_bool8 isDetachedPlayback; - ma_bool8 isDetachedCapture; - ma_wasapi_usage usage; - void* hAvrtHandle; - ma_mutex rerouteLock; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - /*LPDIRECTSOUND*/ ma_ptr pPlayback; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; - /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; - /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - /*HWAVEOUT*/ ma_handle hDevicePlayback; - /*HWAVEIN*/ ma_handle hDeviceCapture; - /*HANDLE*/ ma_handle hEventPlayback; - /*HANDLE*/ ma_handle hEventCapture; - ma_uint32 fragmentSizeInFrames; - ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ - ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ - ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ - ma_uint32 headerFramesConsumedCapture; /* ^^^ */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ - ma_uint8* pIntermediaryBufferPlayback; - ma_uint8* pIntermediaryBufferCapture; - ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - /*snd_pcm_t**/ ma_ptr pPCMPlayback; - /*snd_pcm_t**/ ma_ptr pPCMCapture; - /*struct pollfd**/ void* pPollDescriptorsPlayback; - /*struct pollfd**/ void* pPollDescriptorsCapture; - int pollDescriptorCountPlayback; - int pollDescriptorCountCapture; - int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ - int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ - ma_bool8 isUsingMMapPlayback; - ma_bool8 isUsingMMapCapture; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - /*pa_stream**/ ma_ptr pStreamPlayback; - /*pa_stream**/ ma_ptr pStreamCapture; - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - /*jack_client_t**/ ma_ptr pClient; - /*jack_port_t**/ ma_ptr* ppPortsPlayback; - /*jack_port_t**/ ma_ptr* ppPortsCapture; - float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ - float* pIntermediaryBufferCapture; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_uint32 deviceObjectIDPlayback; - ma_uint32 deviceObjectIDCapture; - /*AudioUnit*/ ma_ptr audioUnitPlayback; - /*AudioUnit*/ ma_ptr audioUnitCapture; - /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ - ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ - ma_event stopEvent; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_bool32 isDefaultPlaybackDevice; - ma_bool32 isDefaultCaptureDevice; - ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_ptr handlePlayback; - ma_ptr handleCapture; - ma_bool32 isStartedPlayback; - ma_bool32 isStartedCapture; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int fdPlayback; - int fdCapture; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int fdPlayback; - int fdCapture; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - /*AAudioStream**/ ma_ptr pStreamPlayback; - /*AAudioStream**/ ma_ptr pStreamCapture; - ma_mutex rerouteLock; - ma_atomic_bool32 isTearingDown; - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - /*SLObjectItf*/ ma_ptr pOutputMixObj; - /*SLOutputMixItf*/ ma_ptr pOutputMix; - /*SLObjectItf*/ ma_ptr pAudioPlayerObj; - /*SLPlayItf*/ ma_ptr pAudioPlayer; - /*SLObjectItf*/ ma_ptr pAudioRecorderObj; - /*SLRecordItf*/ ma_ptr pAudioRecorder; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; - ma_bool32 isDrainingCapture; - ma_bool32 isDrainingPlayback; - ma_uint32 currentBufferIndexPlayback; - ma_uint32 currentBufferIndexCapture; - ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ - ma_uint8* pBufferCapture; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - /* AudioWorklets path. */ - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; - /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; - float* pIntermediaryBuffer; - void* pStackBuffer; - ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ - int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - ma_thread deviceThread; - ma_event operationEvent; - ma_event operationCompletionEvent; - ma_semaphore operationSemaphore; - ma_uint32 operation; - ma_result operationResult; - ma_timer timer; - double priorRunTime; - ma_uint32 currentPeriodFramesRemainingPlayback; - ma_uint32 currentPeriodFramesRemainingCapture; - ma_uint64 lastProcessedFramePlayback; - ma_uint64 lastProcessedFrameCapture; - ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ - } null_device; -#endif - }; }; #if defined(_MSC_VER) && !defined(__clang__) #pragma warning(pop) @@ -8242,7 +7899,7 @@ MA_API ma_context_config ma_context_config_init(void); /* -Helper function for retrieving a pointer to a custom backend's context config. +Helper function for retrieving a pointer to a backend's context config. Remarks @@ -8250,7 +7907,7 @@ Remarks This should only ever be used by custom backend implementations. It's used for retrieving the pConfig pointer that has been associated with the specified backend vtable. */ -MA_API const void* ma_context_config_find_custom_backend_config(const ma_context_config* pConfig, const ma_device_backend_vtable* pVTable); +MA_API const void* ma_context_config_find_backend_config(const ma_context_config* pConfig, const ma_device_backend_config* pBackendConfigs, size_t backendConfigCount, ma_device_backend_vtable* pVTable); /* @@ -8262,8 +7919,8 @@ device. There is one context to many devices, and a device is created from a con Parameters ---------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. +pBackends (in, optional) + A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. See examples on how to use this. backendCount (in, optional) The number of items in `backend`. Ignored if `backend` is NULL. @@ -8287,26 +7944,26 @@ Unsafe. Do not call this function across multiple threads as some backends read Remarks ------- -When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: +When `pBackends` is NULL, the default priority order will be used. Below is a list of backends in priority order: - |-------------|-----------------------|--------------------------------------------------------| - | Name | Enum Name | Supported Operating Systems | - |-------------|-----------------------|--------------------------------------------------------| - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Null | ma_backend_null | Cross Platform (not used on Web) | - |-------------|-----------------------|--------------------------------------------------------| + |-------------|------------------------------|--------------------------------------------------------| + | Name | Enum Name | Supported Operating Systems | + |-------------|------------------------------|--------------------------------------------------------| + | WASAPI | ma_device_backend_wasapi | Windows Vista+ | + | DirectSound | ma_device_backend_dsound | Windows XP+ | + | WinMM | ma_device_backend_winmm | Windows 95+ | + | Core Audio | ma_device_backend_coreaudio | macOS, iOS | + | sndio | ma_device_backend_sndio | OpenBSD | + | audio(4) | ma_device_backend_audio4 | NetBSD, OpenBSD | + | OSS | ma_device_backend_oss | FreeBSD | + | PulseAudio | ma_device_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | + | ALSA | ma_device_backend_alsa | Linux | + | JACK | ma_device_backend_jack | Cross Platform (disabled on BSD and Android) | + | AAudio | ma_device_backend_aaudio | Android 8+ | + | OpenSL|ES | ma_device_backend_opensl | Android (API level 16+) | + | Web Audio | ma_device_backend_webaudio | Web (via Emscripten) | + | Null | ma_device_backend_null | Cross Platform (not used on Web) | + |-------------|------------------------------|--------------------------------------------------------| The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings can then be set directly on the structure. Below are the members of the `ma_context_config` object. @@ -8430,11 +8087,12 @@ want an error to be returned if no valid backend is available which they achieve For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. ```c -ma_backend backends[] = { - ma_backend_alsa, - ma_backend_pulseaudio, - ma_backend_wasapi, - ma_backend_dsound +ma_device_backend_config backends[] = +{ + { ma_device_backend_alsa, NULL }, + { ma_device_backend_pulseaudio, NULL }, + { ma_device_backend_wasapi, NULL }, + { ma_device_backend_dsound, NULL } }; ma_log log; @@ -8457,13 +8115,17 @@ if (result != MA_SUCCESS) { ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); ``` +When listing the backends you want to support, it's done in pairs. The first item is the backend identifier. The second is a pointer to a backend-specific +configuration object. Because we are only using stock backends in this example, we can set this to null, and any backend-specific configurations can be +set directly in the `ma_context_config` object. + See Also -------- ma_context_config_init() ma_context_uninit() */ -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); +MA_API ma_result ma_context_init(const ma_device_backend_config* pBackends, ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); /* Uninitializes a context. @@ -8490,6 +8152,7 @@ ma_context_init() */ MA_API ma_result ma_context_uninit(ma_context* pContext); + /* Retrieves the size of the ma_context object. @@ -8497,6 +8160,7 @@ This is mainly for the purpose of bindings to know how much memory to allocate. */ MA_API size_t ma_context_sizeof(void); + /* Retrieves a pointer to the log object associated with this context. @@ -8516,6 +8180,62 @@ NULL will be returned. */ MA_API ma_log* ma_context_get_log(ma_context* pContext); + +/* +Retrieves the allocation callbacks associated with this context. + + +Remarks +------- +You can pass this into `ma_malloc()`, etc. to allocate memory using the allocation callbacks. This +is mainly intended for use by backends. + + +Return Value +------------ +A pointer to the `ma_allocation_callbacks` object. +*/ +MA_API const ma_allocation_callbacks* ma_context_get_allocation_callbacks(ma_context* pContext); + + +/* +Retrieves the thread priority associated with this context. + + +Remarks +------- +This is intended for use by backends that want to run their own thread while respecting the thread +priority requested by the application. + + +Return Value +----------- +The thread priority to use for audio threads by backends. +*/ +MA_API ma_thread_priority ma_context_get_thread_priority(ma_context* pContext); + + +/* +Retrieves the backend-specific state object. + + +Remarks +------- +This should only ever by called by backends. A normal application should never call this function. + +When a backend is initialized, it allocates a backend-specific data structure which is then linked +to the `ma_context` object. This function retrieves a pointer to that backend-specific data so it +can be used by the backend. The meaning of this pointer is only known by the backend that created +it, and is therefore the only thing that need call this function. + + +Return Value +------------ +A pointer to the backend-specific state object. +*/ +MA_API void* ma_context_get_backend_state(ma_context* pContext); + + /* Enumerates over every device (both playback and capture). @@ -8774,7 +8494,7 @@ Remarks This should only ever be used by custom backend implementations. It's used for retrieving the pConfig pointer that has been associated with the specified backend vtable. */ -MA_API const void* ma_device_config_find_custom_backend_config(const ma_device_config* pConfig, const ma_device_backend_vtable* pVTable); +MA_API const void* ma_device_config_find_backend_config(const ma_device_config* pConfig, ma_device_backend_vtable* pVTable); /* @@ -9118,7 +8838,7 @@ allows you to configure the internally created context. Parameters ---------- -backends (in, optional) +pBackends (in, optional) A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. backendCount (in, optional) @@ -9165,7 +8885,7 @@ ma_device_uninit() ma_device_config_init() ma_context_init() */ -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); +MA_API ma_result ma_device_init_ex(const ma_device_backend_config* pBackends, ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); /* Uninitializes a device. @@ -9212,6 +8932,50 @@ Helper function for retrieving the log object associated with the context that o */ MA_API ma_log* ma_device_get_log(ma_device* pDevice); +/* +Helper function for retrieving the device type (playback, capture, etc.) +*/ +MA_API ma_device_type ma_device_get_type(ma_device* pDevice); + + + +/* +Retrieves the allocation callbacks associated with this context. + + +Remarks +------- +You can pass this into `ma_malloc()`, etc. to allocate memory using the allocation callbacks. This +is mainly intended for use by backends. + + +Return Value +------------ +A pointer to the `ma_allocation_callbacks` object. +*/ +MA_API const ma_allocation_callbacks* ma_device_get_allocation_callbacks(ma_device* pDevice); + + +/* +Retrieves the backend-specific state object. + + +Remarks +------- +This should only ever by called by backends. A normal application should never call this function. + +When a backend is initialized, it allocates a backend-specific data structure which is then linked +to the `ma_device` object. This function retrieves a pointer to that backend-specific data so it +can be used by the backend. The meaning of this pointer is only known by the backend that created +it, and is therefore the only thing that need call this function. + + +Return Value +------------ +A pointer to the backend-specific state object. +*/ +MA_API void* ma_device_get_backend_state(ma_device* pDevice); + /* Retrieves information about the device. @@ -9759,6 +9523,19 @@ callback. MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); +/* +Notification helpers. Should only be called from backends. +*/ +MA_API void ma_device_post_notification(ma_device_notification notification); +MA_API void ma_device_post_notification_started(ma_device* pDevice); +MA_API void ma_device_post_notification_stopped(ma_device* pDevice); +MA_API void ma_device_post_notification_rerouted(ma_device* pDevice); +MA_API void ma_device_post_notification_interruption_began(ma_device* pDevice); +MA_API void ma_device_post_notification_interruption_ended(ma_device* pDevice); +MA_API void ma_device_post_notification_unlocked(ma_device* pDevice); + + + /* Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. @@ -9813,6 +9590,7 @@ MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_dev + /* Retrieves a friendly name for a backend. */ @@ -19504,213 +19282,310 @@ DEVICE I/O #endif +/************************************************************************************************************************************************************ -static ma_result ma_context_init__compat(void* pUserData, ma_context* pContext, const ma_context_config* pConfig) +BACKENDS + +************************************************************************************************************************************************************/ +/* BEG WASAPI */ +MA_API ma_context_config_wasapi ma_context_config_wasapi_init(void) { - (void)pUserData; - return pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); + ma_context_config_wasapi config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_context_uninit__compat(void* pUserData, ma_context* pContext) +MA_API ma_device_config_wasapi ma_device_config_wasapi_init(void) { - (void)pUserData; + ma_device_config_wasapi config; - if (pContext->callbacks.onContextUninit == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pContext->callbacks.onContextUninit(pContext); + return config; +} +/* END WASAPI */ + + +/* BEG DSOUND */ +MA_API ma_context_config_dsound ma_context_config_dsound_init(void) +{ + ma_context_config_dsound config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_context_enumerate_devices__compat(void* pUserData, ma_context* pContext, ma_enum_devices_callback_proc callback, void* pCallbackUserData) +MA_API ma_device_config_dsound ma_device_config_dsound_init(void) { - (void)pUserData; + ma_device_config_dsound config; - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pContext->callbacks.onContextEnumerateDevices(pContext, callback, pCallbackUserData); + return config; +} +/* END DSOUND */ + + +/* BEG WINMM */ +MA_API ma_context_config_winmm ma_context_config_winmm_init(void) +{ + ma_context_config_winmm config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_context_get_device_info__compat(void* pUserData, ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +MA_API ma_device_config_winmm ma_device_config_winmm_init(void) { - (void)pUserData; + ma_device_config_winmm config; - if (pContext->callbacks.onContextGetDeviceInfo == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, pDeviceInfo); + return config; +} +/* END WINMM */ + + +/* BEG COREAUDIO */ +MA_API ma_context_config_coreaudio ma_context_config_coreaudio_init(void) +{ + ma_context_config_coreaudio config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_device_init__compat(void* pUserData, ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +MA_API ma_device_config_coreaudio ma_device_config_coreaudio_init(void) { - (void)pUserData; - - if (pDevice->pContext->callbacks.onDeviceInit == NULL) { - return MA_NOT_IMPLEMENTED; - } + ma_device_config_coreaudio config; - return pDevice->pContext->callbacks.onDeviceInit(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture); + MA_ZERO_OBJECT(&config); + + return config; +} +/* END COREAUDIO */ + + +/* BEG SNDIO */ +MA_API ma_context_config_sndio ma_context_config_sndio_init(void) +{ + ma_context_config_sndio config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_device_uninit__compat(void* pUserData, ma_device* pDevice) +MA_API ma_device_config_sndio ma_device_config_sndio_init(void) { - (void)pUserData; + ma_device_config_sndio config; - if (pDevice->pContext->callbacks.onDeviceUninit == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pDevice->pContext->callbacks.onDeviceUninit(pDevice); + return config; +} +/* END SNDIO */ + + +/* BEG AUDIO4 */ +MA_API ma_context_config_audio4 ma_context_config_audio4_init(void) +{ + ma_context_config_audio4 config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_device_start__compat(void* pUserData, ma_device* pDevice) +MA_API ma_device_config_audio4 ma_device_config_audio4_init(void) { - (void)pUserData; + ma_device_config_audio4 config; - if (pDevice->pContext->callbacks.onDeviceStart == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pDevice->pContext->callbacks.onDeviceStart(pDevice); + return config; +} +/* END AUDIO4 */ + + +/* BEG OSS */ +MA_API ma_context_config_oss ma_context_config_oss_init(void) +{ + ma_context_config_oss config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_device_stop__compat(void* pUserData, ma_device* pDevice) +MA_API ma_device_config_oss ma_device_config_oss_init(void) { - (void)pUserData; + ma_device_config_oss config; - if (pDevice->pContext->callbacks.onDeviceStop == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pDevice->pContext->callbacks.onDeviceStop(pDevice); + return config; +} +/* END OSS */ + + +/* BEG PULSEAUDIO */ +MA_API ma_context_config_pulseaudio ma_context_config_pulseaudio_init(void) +{ + ma_context_config_pulseaudio config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_device_read__compat(void* pUserData, ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +MA_API ma_device_config_pulseaudio ma_device_config_pulseaudio_init(void) { - (void)pUserData; + ma_device_config_pulseaudio config; - if (pDevice->pContext->callbacks.onDeviceRead == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pDevice->pContext->callbacks.onDeviceRead(pDevice, pFrames, frameCount, pFramesRead); + return config; +} +/* END PULSEAUDIO */ + + +/* BEG ALSA */ +MA_API ma_context_config_alsa ma_context_config_alsa_init(void) +{ + ma_context_config_alsa config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_device_write__compat(void* pUserData, ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +MA_API ma_device_config_alsa ma_device_config_alsa_init(void) { - (void)pUserData; + ma_device_config_alsa config; - if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pDevice->pContext->callbacks.onDeviceWrite(pDevice, pFrames, frameCount, pFramesWritten); + return config; +} +/* END ALSA */ + + +/* BEG JACK */ +MA_API ma_context_config_jack ma_context_config_jack_init(void) +{ + ma_context_config_jack config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_device_data_loop__compat(void* pUserData, ma_device* pDevice) +MA_API ma_device_config_jack ma_device_config_jack_init(void) { - (void)pUserData; + ma_device_config_jack config; - if (pDevice->pContext->callbacks.onDeviceDataLoop == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); + return config; +} +/* END JACK */ + + +/* BEG AAUDIO */ +MA_API ma_context_config_aaudio ma_context_config_aaudio_init(void) +{ + ma_context_config_aaudio config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_device_data_loop_wakeup__compat(void* pUserData, ma_device* pDevice) +MA_API ma_device_config_aaudio ma_device_config_aaudio_init(void) { - (void)pUserData; + ma_device_config_aaudio config; - if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); + return config; +} +/* END AAUDIO */ + + +/* BEG OPENSL */ +MA_API ma_context_config_opensl ma_context_config_opensl_init(void) +{ + ma_context_config_opensl config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_result ma_device_get_info__compat(void* pUserData, ma_device* pDevice, ma_device_type deviceType, ma_device_info* pDeviceInfo) +MA_API ma_device_config_opensl ma_device_config_opensl_init(void) { - (void)pUserData; + ma_device_config_opensl config; - if (pDevice->pContext->callbacks.onDeviceGetInfo == NULL) { - return MA_NOT_IMPLEMENTED; - } + MA_ZERO_OBJECT(&config); - return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, deviceType, pDeviceInfo); + return config; +} +/* END OPENSL */ + + +/* BEG WEBAUDIO */ +MA_API ma_context_config_webaudio ma_context_config_webaudio_init(void) +{ + ma_context_config_webaudio config; + + MA_ZERO_OBJECT(&config); + + return config; } - -static ma_bool32 ma_device_has_start__compat(void* pUserData, ma_context* pContext) +MA_API ma_device_config_webaudio ma_device_config_webaudio_init(void) { - (void)pUserData; - return pContext->callbacks.onDeviceStart != NULL; + ma_device_config_webaudio config; + + MA_ZERO_OBJECT(&config); + + return config; +} +/* END WEBAUDIO */ + + +/* BEG NULL */ +MA_API ma_context_config_null ma_context_config_null_init(void) +{ + ma_context_config_null config; + + MA_ZERO_OBJECT(&config); + + return config; } -static ma_bool32 ma_device_has_stop__compat(void* pUserData, ma_context* pContext) +MA_API ma_device_config_null ma_device_config_null_init(void) { - (void)pUserData; - return pContext->callbacks.onDeviceStop != NULL; + ma_device_config_null config; + + MA_ZERO_OBJECT(&config); + + return config; } +/* END NULL */ -static ma_bool32 ma_device_has_read__compat(void* pUserData, ma_context* pContext) -{ - (void)pUserData; - return pContext->callbacks.onDeviceRead != NULL; -} +/************************************************************************************************************************************************************ -static ma_bool32 ma_device_has_write__compat(void* pUserData, ma_context* pContext) -{ - (void)pUserData; - return pContext->callbacks.onDeviceWrite != NULL; -} +END BACKENDS -static ma_bool32 ma_device_has_data_loop__compat(void* pUserData, ma_context* pContext) -{ - (void)pUserData; - return pContext->callbacks.onDeviceDataLoop != NULL; -} +************************************************************************************************************************************************************/ -static ma_bool32 ma_device_has_data_loop_wakeup__compat(void* pUserData, ma_context* pContext) -{ - (void)pUserData; - return pContext->callbacks.onDeviceDataLoopWakeup != NULL; -} - -static ma_bool32 ma_device_has_get_info__compat(void* pUserData, ma_context* pContext) -{ - (void)pUserData; - return pContext->callbacks.onDeviceGetInfo != NULL; -} - -static ma_device_backend_vtable ma_gDeviceVTable_Compat = -{ - ma_context_init__compat, - ma_context_uninit__compat, - ma_context_enumerate_devices__compat, - ma_context_get_device_info__compat, - ma_device_init__compat, - ma_device_uninit__compat, - ma_device_start__compat, - ma_device_stop__compat, - ma_device_read__compat, - ma_device_write__compat, - ma_device_data_loop__compat, - ma_device_data_loop_wakeup__compat, - ma_device_get_info__compat, - - /* Temporary compatibility functions. Will be removed when all backends implement the new vtable system. */ - ma_device_has_start__compat, - ma_device_has_stop__compat, - ma_device_has_read__compat, - ma_device_has_write__compat, - ma_device_has_data_loop__compat, - ma_device_has_data_loop_wakeup__compat, - ma_device_has_get_info__compat -}; @@ -20398,56 +20273,6 @@ static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned i } } -static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) -{ - ma_device_notification notification; - - MA_ZERO_OBJECT(¬ification); - notification.pDevice = pDevice; - notification.type = type; - - return notification; -} - -static void ma_device__on_notification(ma_device_notification notification) -{ - MA_ASSERT(notification.pDevice != NULL); - - if (notification.pDevice->onNotification != NULL) { - notification.pDevice->onNotification(¬ification); - } -} - -static void ma_device__on_notification_started(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); -} - -static void ma_device__on_notification_stopped(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); -} - -/* Not all platforms support reroute notifications. */ -#if !defined(MA_EMSCRIPTEN) -static void ma_device__on_notification_rerouted(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); -} -#endif - -#if defined(MA_EMSCRIPTEN) -#ifdef __cplusplus -extern "C" { -#endif -void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); -} -#ifdef __cplusplus -} -#endif -#endif static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) @@ -20963,7 +20788,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) /* Just some quick validation on the device type and the available callbacks. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - if (pDevice->pContext->pVTable->onDeviceRead == NULL || (pDevice->pContext->pVTable->onHasRead && pDevice->pContext->pVTable->onHasRead(pDevice->pContext->pVTableUserData, pDevice->pContext) == MA_FALSE)) { + if (pDevice->pContext->pVTable->onDeviceRead == NULL) { return MA_NOT_IMPLEMENTED; } @@ -20971,7 +20796,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->pContext->pVTable->onDeviceWrite == NULL || (pDevice->pContext->pVTable->onHasWrite && pDevice->pContext->pVTable->onHasWrite(pDevice->pContext->pVTableUserData, pDevice->pContext) == MA_FALSE)) { + if (pDevice->pContext->pVTable->onDeviceWrite == NULL) { return MA_NOT_IMPLEMENTED; } @@ -20997,7 +20822,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; } - result = pDevice->pContext->pVTable->onDeviceRead(pDevice->pContext->pVTableUserData, pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); + result = pDevice->pContext->pVTable->onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); if (result != MA_SUCCESS) { exitLoop = MA_TRUE; break; @@ -21044,7 +20869,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) break; } - result = pDevice->pContext->pVTable->onDeviceWrite(pDevice->pContext->pVTableUserData, pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ + result = pDevice->pContext->pVTable->onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ if (result != MA_SUCCESS) { exitLoop = MA_TRUE; break; @@ -21085,7 +20910,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) framesToReadThisIteration = capturedDeviceDataCapInFrames; } - result = pDevice->pContext->pVTable->onDeviceRead(pDevice->pContext->pVTableUserData, pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); + result = pDevice->pContext->pVTable->onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); if (result != MA_SUCCESS) { exitLoop = MA_TRUE; break; @@ -21117,7 +20942,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); - result = pDevice->pContext->pVTable->onDeviceWrite(pDevice->pContext->pVTableUserData, pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); + result = pDevice->pContext->pVTable->onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); if (result != MA_SUCCESS) { exitLoop = MA_TRUE; break; @@ -21154,59 +20979,89 @@ Null Backend #define MA_DEVICE_OP_SUSPEND__NULL 2 #define MA_DEVICE_OP_KILL__NULL 3 +typedef struct ma_context_state_null +{ + int _unused; +} ma_context_state_null; + +typedef struct ma_device_state_null +{ + ma_thread deviceThread; + ma_event operationEvent; + ma_event operationCompletionEvent; + ma_semaphore operationSemaphore; + ma_uint32 operation; + ma_result operationResult; + ma_timer timer; + double priorRunTime; + struct + { + ma_uint64 lastProcessedFrame; + ma_uint32 currentPeriodFramesRemaining; + ma_device_descriptor descriptor; + } capture; + struct + { + ma_uint64 lastProcessedFrame; + ma_uint32 currentPeriodFramesRemaining; + ma_device_descriptor descriptor; + } playback; + ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ +} ma_device_state_null; + static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) { - ma_device* pDevice = (ma_device*)pData; - MA_ASSERT(pDevice != NULL); + ma_device_state_null* pDeviceStateNull = (ma_device_state_null*)pData; + MA_ASSERT(pDeviceStateNull != NULL); for (;;) { /* Keep the thread alive until the device is uninitialized. */ ma_uint32 operation; /* Wait for an operation to be requested. */ - ma_event_wait(&pDevice->null_device.operationEvent); + ma_event_wait(&pDeviceStateNull->operationEvent); /* At this point an event should have been triggered. */ - operation = pDevice->null_device.operation; + operation = pDeviceStateNull->operation; /* Starting the device needs to put the thread into a loop. */ if (operation == MA_DEVICE_OP_START__NULL) { /* Reset the timer just in case. */ - ma_timer_init(&pDevice->null_device.timer); + ma_timer_init(&pDeviceStateNull->timer); /* Getting here means a suspend or kill operation has been requested. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); + pDeviceStateNull->operationResult = MA_SUCCESS; + ma_event_signal(&pDeviceStateNull->operationCompletionEvent); + ma_semaphore_release(&pDeviceStateNull->operationSemaphore); continue; } /* Suspending the device means we need to stop the timer and just continue the loop. */ if (operation == MA_DEVICE_OP_SUSPEND__NULL) { /* We need to add the current run time to the prior run time, then reset the timer. */ - pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); - ma_timer_init(&pDevice->null_device.timer); + pDeviceStateNull->priorRunTime += ma_timer_get_time_in_seconds(&pDeviceStateNull->timer); + ma_timer_init(&pDeviceStateNull->timer); /* We're done. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); + pDeviceStateNull->operationResult = MA_SUCCESS; + ma_event_signal(&pDeviceStateNull->operationCompletionEvent); + ma_semaphore_release(&pDeviceStateNull->operationSemaphore); continue; } /* Killing the device means we need to get out of this loop so that this thread can terminate. */ if (operation == MA_DEVICE_OP_KILL__NULL) { - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); + pDeviceStateNull->operationResult = MA_SUCCESS; + ma_event_signal(&pDeviceStateNull->operationCompletionEvent); + ma_semaphore_release(&pDeviceStateNull->operationSemaphore); break; } /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ if (operation == MA_DEVICE_OP_NONE__NULL) { MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ - pDevice->null_device.operationResult = MA_INVALID_OPERATION; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); + pDeviceStateNull->operationResult = MA_INVALID_OPERATION; + ma_event_signal(&pDeviceStateNull->operationCompletionEvent); + ma_semaphore_release(&pDeviceStateNull->operationSemaphore); continue; /* Continue the loop. Don't terminate. */ } } @@ -21214,7 +21069,7 @@ static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) return (ma_thread_result)0; } -static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) +static ma_result ma_device_do_operation__null(ma_device_state_null* pDeviceStateNull, ma_uint32 operation) { ma_result result; @@ -21228,7 +21083,7 @@ static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 oper The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later to support queuing of operations. */ - result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); + result = ma_semaphore_wait(&pDeviceStateNull->operationSemaphore); if (result != MA_SUCCESS) { return result; /* Failed to wait for the event. */ } @@ -21237,40 +21092,93 @@ static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 oper When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to signal an event to the worker thread to let it know that it can start work. */ - pDevice->null_device.operation = operation; + pDeviceStateNull->operation = operation; /* Once the operation code has been set, the worker thread can start work. */ - if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { + if (ma_event_signal(&pDeviceStateNull->operationEvent) != MA_SUCCESS) { return MA_ERROR; } /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ - if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { + if (ma_event_wait(&pDeviceStateNull->operationCompletionEvent) != MA_SUCCESS) { return MA_ERROR; } - return pDevice->null_device.operationResult; + return pDeviceStateNull->operationResult; } -static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) +static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice, ma_device_state_null* pDeviceStateNull) { ma_uint32 internalSampleRate; - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - internalSampleRate = pDevice->capture.internalSampleRate; + ma_device_type deviceType = ma_device_get_type(pDevice); + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + internalSampleRate = pDeviceStateNull->capture.descriptor.sampleRate; } else { - internalSampleRate = pDevice->playback.internalSampleRate; + internalSampleRate = pDeviceStateNull->playback.descriptor.sampleRate; } - return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); + return (ma_uint64)((pDeviceStateNull->priorRunTime + ma_timer_get_time_in_seconds(&pDeviceStateNull->timer)) * internalSampleRate); +} + + +static ma_context_state_null* ma_context_get_backend_state__null(ma_context* pContext) +{ + return (ma_context_state_null*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_null* ma_device_get_backend_state__null(ma_device* pDevice) +{ + return (ma_device_state_null*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__null(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "Null (Silence)"; +} + +static ma_result ma_context_init__null(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_null* pContextStateNull; + const ma_context_config_null* pContextConfigNull = (const ma_context_config_null*)pContextBackendConfig; + ma_context_config_null defaultConfigNull; + + if (pContextConfigNull == NULL) { + defaultConfigNull = ma_context_config_null_init(); + pContextConfigNull = &defaultConfigNull; + } + + (void)pContextConfigNull; + + pContextStateNull = (ma_context_state_null*)ma_calloc(sizeof(*pContextStateNull), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateNull == NULL) { + return MA_OUT_OF_MEMORY; + } + + *ppContextState = pContextStateNull; + + return MA_SUCCESS; +} + +static void ma_context_uninit__null(ma_context* pContext) +{ + ma_context_state_null* pContextStateNull = ma_context_get_backend_state__null(pContext); + + ma_free(pContextStateNull, ma_context_get_allocation_callbacks(pContext)); } static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { + ma_context_state_null* pContextStateNull = ma_context_get_backend_state__null(pContext); ma_bool32 cbResult = MA_TRUE; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pContextStateNull != NULL); MA_ASSERT(callback != NULL); + (void)pContextStateNull; + /* Playback. */ if (cbResult) { ma_device_info deviceInfo; @@ -21296,7 +21204,10 @@ static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enu static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { - MA_ASSERT(pContext != NULL); + ma_context_state_null* pContextStateNull = ma_context_get_backend_state__null(pContext); + + MA_ASSERT(pContextStateNull != NULL); + (void)pContextStateNull; if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { return MA_NO_DEVICE; /* Don't know the device. */ @@ -21318,43 +21229,39 @@ static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_devic pDeviceInfo->nativeDataFormats[0].flags = 0; pDeviceInfo->nativeDataFormatCount = 1; - (void)pContext; return MA_SUCCESS; } -static ma_result ma_device_uninit__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Keep it clean and wait for the device thread to finish before returning. */ - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); - - /* Wait for the thread to finish before continuing. */ - ma_thread_wait(&pDevice->null_device.deviceThread); - - /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ - ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); - ma_event_uninit(&pDevice->null_device.operationCompletionEvent); - ma_event_uninit(&pDevice->null_device.operationEvent); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) + + +static ma_result ma_device_init__null(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { + ma_device_state_null* pDeviceStateNull; + const ma_device_config_null* pDeviceConfigNull = (const ma_device_config_null*)pDeviceBackendConfig; + ma_device_config_null defaultConfigNull; + ma_context_state_null* pContextStateNull = ma_context_get_backend_state__null(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result; - MA_ASSERT(pDevice != NULL); + (void)pContextStateNull; - MA_ZERO_OBJECT(&pDevice->null_device); + if (pDeviceConfigNull == NULL) { + defaultConfigNull = ma_device_config_null_init(); + pDeviceConfigNull = &defaultConfigNull; + } - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } + pDeviceStateNull = (ma_device_state_null*)ma_calloc(sizeof(*pDeviceStateNull), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateNull == NULL) { + return MA_OUT_OF_MEMORY; + } + /* The null backend supports everything exactly as we specify it. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; @@ -21363,10 +21270,12 @@ static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); } - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); + pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, ma_performance_profile_low_latency); + + pDeviceStateNull->capture.descriptor = *pDescriptorCapture; } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; @@ -21375,65 +21284,104 @@ static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); } - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, ma_performance_profile_low_latency); + + pDeviceStateNull->playback.descriptor = *pDescriptorPlayback; } /* In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the first period is "written" to it, and then stopped in ma_device_stop__null(). */ - result = ma_event_init(&pDevice->null_device.operationEvent); + result = ma_event_init(&pDeviceStateNull->operationEvent); if (result != MA_SUCCESS) { + ma_device_get_allocation_callbacks(pDevice); return result; } - result = ma_event_init(&pDevice->null_device.operationCompletionEvent); + result = ma_event_init(&pDeviceStateNull->operationCompletionEvent); if (result != MA_SUCCESS) { + ma_event_uninit(&pDeviceStateNull->operationEvent); + ma_device_get_allocation_callbacks(pDevice); return result; } - result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ + result = ma_semaphore_init(1, &pDeviceStateNull->operationSemaphore); /* <-- It's important that the initial value is set to 1. */ if (result != MA_SUCCESS) { + ma_event_uninit(&pDeviceStateNull->operationCompletionEvent); + ma_event_uninit(&pDeviceStateNull->operationEvent); + ma_device_get_allocation_callbacks(pDevice); return result; } - result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); + result = ma_thread_create(&pDeviceStateNull->deviceThread, ma_context_get_thread_priority(ma_device_get_context(pDevice)), 0, ma_device_thread__null, pDeviceStateNull, ma_device_get_allocation_callbacks(pDevice)); if (result != MA_SUCCESS) { + ma_semaphore_uninit(&pDeviceStateNull->operationSemaphore); + ma_event_uninit(&pDeviceStateNull->operationCompletionEvent); + ma_event_uninit(&pDeviceStateNull->operationEvent); + ma_device_get_allocation_callbacks(pDevice); return result; } + *ppDeviceState = pDeviceStateNull; + return MA_SUCCESS; } +static void ma_device_uninit__null(ma_device* pDevice) +{ + ma_device_state_null* pDeviceStateNull = ma_device_get_backend_state__null(pDevice); + + MA_ASSERT(pDeviceStateNull != NULL); + + /* Keep it clean and wait for the device thread to finish before returning. */ + ma_device_do_operation__null(pDeviceStateNull, MA_DEVICE_OP_KILL__NULL); + + /* Wait for the thread to finish before continuing. */ + ma_thread_wait(&pDeviceStateNull->deviceThread); + + /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ + ma_semaphore_uninit(&pDeviceStateNull->operationSemaphore); + ma_event_uninit(&pDeviceStateNull->operationCompletionEvent); + ma_event_uninit(&pDeviceStateNull->operationEvent); + + ma_free(pDeviceStateNull, ma_device_get_allocation_callbacks(pDevice)); +} + static ma_result ma_device_start__null(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_null* pDeviceStateNull = ma_device_get_backend_state__null(pDevice); - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); + MA_ASSERT(pDeviceStateNull != NULL); - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE); + ma_device_do_operation__null(pDeviceStateNull, MA_DEVICE_OP_START__NULL); + + ma_atomic_bool32_set(&pDeviceStateNull->isStarted, MA_TRUE); return MA_SUCCESS; } static ma_result ma_device_stop__null(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_null* pDeviceStateNull = ma_device_get_backend_state__null(pDevice); - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); + MA_ASSERT(pDeviceStateNull != NULL); - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE); + ma_device_do_operation__null(pDeviceStateNull, MA_DEVICE_OP_SUSPEND__NULL); + + ma_atomic_bool32_set(&pDeviceStateNull->isStarted, MA_FALSE); return MA_SUCCESS; } -static ma_bool32 ma_device_is_started__null(ma_device* pDevice) +static ma_bool32 ma_device_is_started__null(ma_device_state_null* pDeviceStateNull) { - MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDeviceStateNull != NULL); - return ma_atomic_bool32_get(&pDevice->null_device.isStarted); + return ma_atomic_bool32_get(&pDeviceStateNull->isStarted); } static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { + ma_device_state_null* pDeviceStateNull = ma_device_get_backend_state__null(pDevice); ma_result result = MA_SUCCESS; ma_uint32 totalPCMFramesProcessed; ma_bool32 wasStartedOnEntry; @@ -21442,7 +21390,7 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame *pFramesWritten = 0; } - wasStartedOnEntry = ma_device_is_started__null(pDevice); + wasStartedOnEntry = ma_device_is_started__null(pDeviceStateNull); /* Keep going until everything has been read. */ totalPCMFramesProcessed = 0; @@ -21450,9 +21398,9 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame ma_uint64 targetFrame; /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { + if (pDeviceStateNull->playback.currentPeriodFramesRemaining > 0) { ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; + ma_uint32 framesToProcess = pDeviceStateNull->playback.currentPeriodFramesRemaining; if (framesToProcess > framesRemaining) { framesToProcess = framesRemaining; } @@ -21460,15 +21408,15 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ (void)pPCMFrames; - pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; + pDeviceStateNull->playback.currentPeriodFramesRemaining -= framesToProcess; totalPCMFramesProcessed += framesToProcess; } /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { - pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; + if (pDeviceStateNull->playback.currentPeriodFramesRemaining == 0) { + pDeviceStateNull->playback.currentPeriodFramesRemaining = 0; - if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) { + if (!ma_device_is_started__null(pDeviceStateNull) && !wasStartedOnEntry) { result = ma_device_start__null(pDevice); if (result != MA_SUCCESS) { break; @@ -21483,16 +21431,16 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame } /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFramePlayback; + targetFrame = pDeviceStateNull->playback.lastProcessedFrame; for (;;) { ma_uint64 currentFrame; /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { + if (!ma_device_is_started__null(pDeviceStateNull)) { break; } - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); + currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice, pDeviceStateNull); if (currentFrame >= targetFrame) { break; } @@ -21501,8 +21449,8 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame ma_sleep(10); } - pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; + pDeviceStateNull->playback.lastProcessedFrame += pDeviceStateNull->playback.descriptor.periodSizeInFrames; + pDeviceStateNull->playback.currentPeriodFramesRemaining = pDeviceStateNull->playback.descriptor.periodSizeInFrames; } if (pFramesWritten != NULL) { @@ -21514,6 +21462,7 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { + ma_device_state_null* pDeviceStateNull = ma_device_get_backend_state__null(pDevice); ma_result result = MA_SUCCESS; ma_uint32 totalPCMFramesProcessed; @@ -21527,10 +21476,10 @@ static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_u ma_uint64 targetFrame; /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + if (pDeviceStateNull->capture.currentPeriodFramesRemaining > 0) { + ma_uint32 bpf = ma_get_bytes_per_frame(pDeviceStateNull->capture.descriptor.format, pDeviceStateNull->capture.descriptor.channels); ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; + ma_uint32 framesToProcess = pDeviceStateNull->capture.currentPeriodFramesRemaining; if (framesToProcess > framesRemaining) { framesToProcess = framesRemaining; } @@ -21538,13 +21487,13 @@ static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_u /* We need to ensure the output buffer is zeroed. */ MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); - pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; + pDeviceStateNull->capture.currentPeriodFramesRemaining -= framesToProcess; totalPCMFramesProcessed += framesToProcess; } /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { - pDevice->null_device.currentPeriodFramesRemainingCapture = 0; + if (pDeviceStateNull->capture.currentPeriodFramesRemaining == 0) { + pDeviceStateNull->capture.currentPeriodFramesRemaining = 0; } /* If we've consumed the whole buffer we can return now. */ @@ -21554,16 +21503,16 @@ static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_u } /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; + targetFrame = pDeviceStateNull->capture.lastProcessedFrame + pDeviceStateNull->capture.descriptor.periodSizeInFrames; for (;;) { ma_uint64 currentFrame; /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { + if (!ma_device_is_started__null(pDeviceStateNull)) { break; } - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); + currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice, pDeviceStateNull); if (currentFrame >= targetFrame) { break; } @@ -21572,8 +21521,8 @@ static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_u ma_sleep(10); } - pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; + pDeviceStateNull->capture.lastProcessedFrame += pDeviceStateNull->capture.descriptor.periodSizeInFrames; + pDeviceStateNull->capture.currentPeriodFramesRemaining = pDeviceStateNull->capture.descriptor.periodSizeInFrames; } if (pFramesRead != NULL) { @@ -21583,37 +21532,26 @@ static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_u return result; } -static ma_result ma_context_uninit__null(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_Null = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_null); + ma_backend_info__null, + ma_context_init__null, + ma_context_uninit__null, + ma_context_enumerate_devices__null, + ma_context_get_device_info__null, + ma_device_init__null, + ma_device_uninit__null, + ma_device_start__null, + ma_device_stop__null, + ma_device_read__null, + ma_device_write__null, + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ +}; - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - (void)pContext; - - pCallbacks->onContextInit = ma_context_init__null; - pCallbacks->onContextUninit = ma_context_uninit__null; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; - pCallbacks->onDeviceInit = ma_device_init__null; - pCallbacks->onDeviceUninit = ma_device_uninit__null; - pCallbacks->onDeviceStart = ma_device_start__null; - pCallbacks->onDeviceStop = ma_device_stop__null; - pCallbacks->onDeviceRead = ma_device_read__null; - pCallbacks->onDeviceWrite = ma_device_write__null; - pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ - - /* The null backend always works. */ - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_null = &ma_gDeviceBackendVTable_Null; +#else +ma_device_backend_vtable* ma_device_backend_null = NULL; #endif @@ -22441,6 +22379,102 @@ typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* dev typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); + + +/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ +typedef struct +{ + int code; + ma_event* pEvent; /* This will be signalled when the event is complete. */ + union + { + struct + { + int _unused; + } quit; + struct + { + ma_device_type deviceType; + ma_IAudioClient* pAudioClient; + void** ppAudioClientService; + ma_result* pResult; /* The result from creating the audio client service. */ + } createAudioClient; + struct + { + ma_device* pDevice; + ma_device_type deviceType; + } releaseAudioClient; + } data; +} ma_context_command__wasapi; + + +typedef struct ma_context_state_wasapi +{ + ma_thread commandThread; + ma_mutex commandLock; + ma_semaphore commandSem; + ma_uint32 commandIndex; + ma_uint32 commandCount; + ma_context_command__wasapi commands[4]; + ma_handle hAvrt; + MA_PFN_AvSetMmThreadCharacteristicsA AvSetMmThreadCharacteristicsA; + MA_PFN_AvRevertMmThreadCharacteristics AvRevertMmThreadcharacteristics; + ma_handle hMMDevapi; + ma_proc ActivateAudioInterfaceAsync; +} ma_context_state_wasapi; + +typedef struct ma_device_state_wasapi +{ + ma_IAudioClient* pAudioClientPlayback; + ma_IAudioClient* pAudioClientCapture; + ma_IAudioRenderClient* pRenderClient; + ma_IAudioCaptureClient* pCaptureClient; + ma_IMMDeviceEnumerator* pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ + ma_IMMNotificationClient notificationClient; + HANDLE hEventPlayback; /* Auto reset. Initialized to signaled. */ + HANDLE hEventCapture; /* Auto reset. Initialized to unsignaled. */ + ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ + ma_uint32 actualBufferSizeInFramesCapture; + ma_uint32 originalPeriodSizeInFrames; + ma_uint32 originalPeriodSizeInMilliseconds; + ma_uint32 originalPeriods; + ma_performance_profile originalPerformanceProfile; + ma_uint32 periodSizeInFramesPlayback; + ma_uint32 periodSizeInFramesCapture; + void* pMappedBufferCapture; + ma_uint32 mappedBufferCaptureCap; + ma_uint32 mappedBufferCaptureLen; + void* pMappedBufferPlayback; + ma_uint32 mappedBufferPlaybackCap; + ma_uint32 mappedBufferPlaybackLen; + ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_uint32 loopbackProcessID; + ma_bool8 loopbackProcessExclude; + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noHardwareOffloading; + ma_bool8 allowCaptureAutoStreamRouting; + ma_bool8 allowPlaybackAutoStreamRouting; + ma_bool8 isDetachedPlayback; + ma_bool8 isDetachedCapture; + ma_wasapi_usage usage; + void* hAvrtHandle; + ma_mutex rerouteLock; +} ma_device_state_wasapi; + + +static ma_context_state_wasapi* ma_context_get_backend_state__wasapi(ma_context* pContext) +{ + return (ma_context_state_wasapi*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_wasapi* ma_device_get_backend_state__wasapi(ma_device* pDevice) +{ + return (ma_device_state_wasapi*)ma_device_get_backend_state(pDevice); +} + + #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; @@ -22575,6 +22609,8 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m ma_bool32 isThisDevice = MA_FALSE; ma_bool32 isCapture = MA_FALSE; ma_bool32 isPlayback = MA_FALSE; + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pThis->pDevice); + ma_device_type deviceType = ma_device_get_type(pThis->pDevice); #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ @@ -22584,14 +22620,14 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect that the device is disabled or has been unplugged. */ - if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { + if (pDeviceStateWASAPI->allowCaptureAutoStreamRouting && (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex ||deviceType == ma_device_type_loopback)) { isCapture = MA_TRUE; if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { isThisDevice = MA_TRUE; } } - if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { + if (pDeviceStateWASAPI->allowPlaybackAutoStreamRouting && (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex)) { isPlayback = MA_TRUE; if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { isThisDevice = MA_TRUE; @@ -22613,10 +22649,10 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m */ if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { if (isPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; + pDeviceStateWASAPI->isDetachedPlayback = MA_TRUE; } if (isCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; + pDeviceStateWASAPI->isDetachedCapture = MA_TRUE; } ma_device_stop(pThis->pDevice); @@ -22628,23 +22664,23 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m ma_bool8 tryRestartingDevice = MA_FALSE; if (isPlayback) { - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; + if (pDeviceStateWASAPI->isDetachedPlayback) { + pDeviceStateWASAPI->isDetachedPlayback = MA_FALSE; ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); tryRestartingDevice = MA_TRUE; } } if (isCapture) { - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); + if (pDeviceStateWASAPI->isDetachedCapture) { + pDeviceStateWASAPI->isDetachedCapture = MA_FALSE; + ma_device_reroute__wasapi(pThis->pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); tryRestartingDevice = MA_TRUE; } } if (tryRestartingDevice) { - if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { + if (pDeviceStateWASAPI->isDetachedPlayback == MA_FALSE && pDeviceStateWASAPI->isDetachedCapture == MA_FALSE) { ma_device_start(pThis->pDevice); } } @@ -22680,6 +22716,9 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMM static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pThis->pDevice); + ma_device_type deviceType = ma_device_get_type(pThis->pDevice); + #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ #endif @@ -22687,21 +22726,21 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged (void)role; /* We only care about devices with the same data flow as the current device. */ - if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || - (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || - (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { + if ((deviceType == ma_device_type_playback && dataFlow != ma_eRender) || + (deviceType == ma_device_type_capture && dataFlow != ma_eCapture) || + (deviceType == ma_device_type_loopback && dataFlow != ma_eRender)) { ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); return S_OK; } /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */ - if (pThis->pDevice->type == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { dataFlow = ma_eCapture; } /* Don't do automatic stream routing if we're not allowed. */ - if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || - (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { + if ((dataFlow == ma_eRender && pDeviceStateWASAPI->allowPlaybackAutoStreamRouting == MA_FALSE) || + (dataFlow == ma_eCapture && pDeviceStateWASAPI->allowCaptureAutoStreamRouting == MA_FALSE)) { ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); return S_OK; } @@ -22739,15 +22778,15 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged } if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ - ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock); + ma_mutex_lock(&pDeviceStateWASAPI->rerouteLock); { if (dataFlow == ma_eRender) { ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; + if (pDeviceStateWASAPI->isDetachedPlayback) { + pDeviceStateWASAPI->isDetachedPlayback = MA_FALSE; - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { + if (deviceType == ma_device_type_duplex && pDeviceStateWASAPI->isDetachedCapture) { restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ } else { @@ -22756,12 +22795,12 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged } } else { - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); + ma_device_reroute__wasapi(pThis->pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; + if (pDeviceStateWASAPI->isDetachedCapture) { + pDeviceStateWASAPI->isDetachedCapture = MA_FALSE; - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { + if (deviceType == ma_device_type_duplex && pDeviceStateWASAPI->isDetachedPlayback) { restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ } else { @@ -22770,7 +22809,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged } } } - ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock); + ma_mutex_unlock(&pDeviceStateWASAPI->rerouteLock); if (restartDevice) { ma_device_start(pThis->pDevice); @@ -22842,6 +22881,7 @@ static ma_context_command__wasapi ma_context_init_command__wasapi(int code) static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) { /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ + ma_context_state_wasapi* pContextStateWASAPI = ma_context_get_backend_state__wasapi(pContext); ma_result result; ma_bool32 isUsingLocalEvent = MA_FALSE; ma_event localEvent; @@ -22859,25 +22899,25 @@ static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_ } /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ - ma_mutex_lock(&pContext->wasapi.commandLock); + ma_mutex_lock(&pContextStateWASAPI->commandLock); { ma_uint32 index; /* Spin until we've got some space available. */ - while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { + while (pContextStateWASAPI->commandCount == ma_countof(pContextStateWASAPI->commands)) { ma_yield(); } /* Space is now available. Can safely add to the list. */ - index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commands[index] = *pCmd; - pContext->wasapi.commands[index].pEvent = &localEvent; - pContext->wasapi.commandCount += 1; + index = (pContextStateWASAPI->commandIndex + pContextStateWASAPI->commandCount) % ma_countof(pContextStateWASAPI->commands); + pContextStateWASAPI->commands[index] = *pCmd; + pContextStateWASAPI->commands[index].pEvent = &localEvent; + pContextStateWASAPI->commandCount += 1; /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ - ma_semaphore_release(&pContext->wasapi.commandSem); + ma_semaphore_release(&pContextStateWASAPI->commandSem); } - ma_mutex_unlock(&pContext->wasapi.commandLock); + ma_mutex_unlock(&pContextStateWASAPI->commandLock); if (isUsingLocalEvent) { ma_event_wait(&localEvent); @@ -22889,20 +22929,21 @@ static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_ static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) { + ma_context_state_wasapi* pContextStateWASAPI = ma_context_get_backend_state__wasapi(pContext); ma_result result = MA_SUCCESS; MA_ASSERT(pContext != NULL); MA_ASSERT(pCmd != NULL); - result = ma_semaphore_wait(&pContext->wasapi.commandSem); + result = ma_semaphore_wait(&pContextStateWASAPI->commandSem); if (result == MA_SUCCESS) { - ma_mutex_lock(&pContext->wasapi.commandLock); + ma_mutex_lock(&pContextStateWASAPI->commandLock); { - *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; - pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commandCount -= 1; + *pCmd = pContextStateWASAPI->commands[pContextStateWASAPI->commandIndex]; + pContextStateWASAPI->commandIndex = (pContextStateWASAPI->commandIndex + 1) % ma_countof(pContextStateWASAPI->commands); + pContextStateWASAPI->commandCount -= 1; } - ma_mutex_unlock(&pContext->wasapi.commandLock); + ma_mutex_unlock(&pContextStateWASAPI->commandLock); } return result; @@ -22912,6 +22953,7 @@ static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pU { ma_result result; ma_context* pContext = (ma_context*)pUserData; + ma_context_state_wasapi* pContextStateWASAPI = ma_context_get_backend_state__wasapi(pContext); MA_ASSERT(pContext != NULL); for (;;) { @@ -22931,25 +22973,28 @@ static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pU case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: { if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); + *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService(cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); } else { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); + *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService(cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); } } break; case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: { + ma_device* pDevice = cmd.data.releaseAudioClient.pDevice; + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); + if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; + if (pDeviceStateWASAPI->pAudioClientPlayback != NULL) { + ma_IAudioClient_Release(pDeviceStateWASAPI->pAudioClientPlayback); + pDeviceStateWASAPI->pAudioClientPlayback = NULL; } } if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; + if (pDeviceStateWASAPI->pAudioClientCapture != NULL) { + ma_IAudioClient_Release(pDeviceStateWASAPI->pAudioClientCapture); + pDeviceStateWASAPI->pAudioClientCapture = NULL; } } } break; @@ -22979,7 +23024,7 @@ static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pCont ma_result cmdResult; ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); cmd.data.createAudioClient.deviceType = deviceType; - cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; + cmd.data.createAudioClient.pAudioClient = pAudioClient; cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ @@ -23495,7 +23540,7 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m return result; } - hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); + hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContextStateWASAPI->ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); if (FAILED(hr)) { ma_completion_handler_uwp_uninit(&completionHandler); ma_CoTaskMemFree(pContext, iidStr); @@ -23639,6 +23684,196 @@ static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_de } +static void ma_backend_info__wasapi(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "WASAPI"; +} + +static ma_result ma_context_init__wasapi(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_wasapi* pContextStateWASAPI = NULL; + const ma_context_config_wasapi* pContextConfigWASAPI = (const ma_context_config_wasapi*)pContextBackendConfig; + ma_context_config_wasapi defaultConfigWASAPI; + ma_result result = MA_SUCCESS; + + if (pContextBackendConfig == NULL) { + defaultConfigWASAPI = ma_context_config_wasapi_init(); + pContextBackendConfig = &defaultConfigWASAPI; + } + + (void)pContextBackendConfig; + +#ifdef MA_WIN32_DESKTOP + /* + WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven + exclusive mode does not work until SP1. + + Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. + */ + { + ma_OSVERSIONINFOEXW osvi; + ma_handle kernel32DLL; + ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; + ma_PFNVerSetConditionMask _VerSetConditionMask; + + kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); + if (kernel32DLL == NULL) { + return MA_NO_BACKEND; + } + + _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); + _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); + if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); + return MA_NO_BACKEND; + } + + MA_ZERO_OBJECT(&osvi); + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); + osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); + osvi.wServicePackMajor = 1; + if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { + result = MA_SUCCESS; + } else { + result = MA_NO_BACKEND; + } + + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); + } +#endif + + if (result != MA_SUCCESS) { + return result; + } + + pContextStateWASAPI = (ma_context_state_wasapi*)ma_calloc(sizeof(*pContextStateWASAPI), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateWASAPI == NULL) { + return MA_OUT_OF_MEMORY; + } + + #if defined(MA_WIN32_UWP) + { + /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ + pContextStateWASAPI->hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); + if (pContextStateWASAPI->hMMDevapi) { + pContextStateWASAPI->ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContextStateWASAPI->hMMDevapi, "ActivateAudioInterfaceAsync"); + if (pContextStateWASAPI->ActivateAudioInterfaceAsync == NULL) { + /* ActivateAudioInterfaceAsync() could not be loaded. */ + ma_free(pContextStateWASAPI, ma_context_get_allocation_callbacks(pContext)); + ma_dlclose(ma_context_get_log(pContext), pContextStateWASAPI->hMMDevapi); + return MA_NO_BACKEND; + } + } else { + /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ + ma_free(pContextStateWASAPI, ma_context_get_allocation_callbacks(pContext)); + return MA_NO_BACKEND; + } + } + #endif + + /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ + pContextStateWASAPI->hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); + if (pContextStateWASAPI->hAvrt) { + pContextStateWASAPI->AvSetMmThreadCharacteristicsA = (MA_PFN_AvSetMmThreadCharacteristicsA )ma_dlsym(ma_context_get_log(pContext), pContextStateWASAPI->hAvrt, "AvSetMmThreadCharacteristicsA"); + pContextStateWASAPI->AvRevertMmThreadcharacteristics = (MA_PFN_AvRevertMmThreadCharacteristics)ma_dlsym(ma_context_get_log(pContext), pContextStateWASAPI->hAvrt, "AvRevertMmThreadCharacteristics"); + + /* If either function could not be found, disable use of avrt entirely. */ + if (!pContextStateWASAPI->AvSetMmThreadCharacteristicsA || !pContextStateWASAPI->AvRevertMmThreadcharacteristics) { + pContextStateWASAPI->AvSetMmThreadCharacteristicsA = NULL; + pContextStateWASAPI->AvRevertMmThreadcharacteristics = NULL; + ma_dlclose(ma_context_get_log(pContext), pContextStateWASAPI->hAvrt); + pContextStateWASAPI->hAvrt = NULL; + } + } + + + /* + Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread + than the one that retrieved it with GetService(). This can result in a deadlock in two + situations: + + 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and + 2) When uninitializing and reinitializing the internal IAudioClient object in response to + automatic stream routing. + + We could define ma_device_uninit() such that it must be called on the same thread as + ma_device_init(). We could also just not release the IAudioClient when performing automatic + stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so + we're going to have to work around this with a worker thread. This is not ideal, but I can't + think of a better way to do this. + + More information about this can be found here: + + https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient + + Note this section: + + When releasing an IAudioRenderClient interface instance, the client must call the interface's + Release method from the same thread as the call to IAudioClient::GetService that created the + object. + */ + { + result = ma_mutex_init(&pContextStateWASAPI->commandLock); + if (result != MA_SUCCESS) { + ma_free(pContextStateWASAPI, ma_context_get_allocation_callbacks(pContext)); + return result; + } + + result = ma_semaphore_init(0, &pContextStateWASAPI->commandSem); + if (result != MA_SUCCESS) { + ma_mutex_uninit(&pContextStateWASAPI->commandLock); + ma_free(pContextStateWASAPI, ma_context_get_allocation_callbacks(pContext)); + return result; + } + + result = ma_thread_create(&pContextStateWASAPI->commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); + if (result != MA_SUCCESS) { + ma_semaphore_uninit(&pContextStateWASAPI->commandSem); + ma_mutex_uninit(&pContextStateWASAPI->commandLock); + ma_free(pContextStateWASAPI, ma_context_get_allocation_callbacks(pContext)); + return result; + } + } + + *ppContextState = pContextStateWASAPI; + + return MA_SUCCESS; +} + +static void ma_context_uninit__wasapi(ma_context* pContext) +{ + ma_context_state_wasapi* pContextStateWASAPI = ma_context_get_backend_state__wasapi(pContext); + ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_wasapi); + + ma_context_post_command__wasapi(pContext, &cmd); + ma_thread_wait(&pContextStateWASAPI->commandThread); + + if (pContextStateWASAPI->hAvrt) { + ma_dlclose(ma_context_get_log(pContext), pContextStateWASAPI->hAvrt); + pContextStateWASAPI->hAvrt = NULL; + } + + #if defined(MA_WIN32_UWP) + { + if (pContextStateWASAPI->hMMDevapi) { + ma_dlclose(ma_context_get_log(pContext), pContextStateWASAPI->hMMDevapi); + pContextStateWASAPI->hMMDevapi = NULL; + } + } + #endif + + /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ + ma_semaphore_uninit(&pContextStateWASAPI->commandSem); + ma_mutex_uninit(&pContextStateWASAPI->commandLock); + + ma_free(pContextStateWASAPI, ma_context_get_allocation_callbacks(pContext)); +} + static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { /* Different enumeration for desktop and UWP. */ @@ -23742,58 +23977,7 @@ static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_dev #endif } -static ma_result ma_device_uninit__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - if (pDevice->wasapi.pDeviceEnumerator) { - ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); - ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); - } - - ma_mutex_uninit(&pDevice->wasapi.rerouteLock); - } - #endif - - if (pDevice->wasapi.pRenderClient) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - } - if (pDevice->wasapi.pCaptureClient) { - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - } - - if (pDevice->wasapi.pAudioClientPlayback) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - } - if (pDevice->wasapi.pAudioClientCapture) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - } - - if (pDevice->wasapi.hEventPlayback) { - CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); - } - if (pDevice->wasapi.hEventCapture) { - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - } - - return MA_SUCCESS; -} typedef struct @@ -24313,6 +24497,7 @@ done: static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); ma_device_init_internal_data__wasapi data; ma_result result; @@ -24335,26 +24520,26 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev a command thread which might be useful for this. */ if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - if (pDevice->wasapi.pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; + if (pDeviceStateWASAPI->pCaptureClient) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDeviceStateWASAPI->pCaptureClient); + pDeviceStateWASAPI->pCaptureClient = NULL; } - if (pDevice->wasapi.pAudioClientCapture) { + if (pDeviceStateWASAPI->pAudioClientCapture) { /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ - pDevice->wasapi.pAudioClientCapture = NULL; + pDeviceStateWASAPI->pAudioClientCapture = NULL; } } if (deviceType == ma_device_type_playback) { - if (pDevice->wasapi.pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; + if (pDeviceStateWASAPI->pRenderClient) { + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDeviceStateWASAPI->pRenderClient); + pDeviceStateWASAPI->pRenderClient = NULL; } - if (pDevice->wasapi.pAudioClientPlayback) { + if (pDeviceStateWASAPI->pAudioClientPlayback) { /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ - pDevice->wasapi.pAudioClientPlayback = NULL; + pDeviceStateWASAPI->pAudioClientPlayback = NULL; } } @@ -24372,15 +24557,15 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev } data.sampleRateIn = pDevice->sampleRate; - data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->wasapi.originalPeriods; - data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; - data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; - data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; + data.periodSizeInFramesIn = pDeviceStateWASAPI->originalPeriodSizeInFrames; + data.periodSizeInMillisecondsIn = pDeviceStateWASAPI->originalPeriodSizeInMilliseconds; + data.periodsIn = pDeviceStateWASAPI->originalPeriods; + data.performanceProfile = pDeviceStateWASAPI->originalPerformanceProfile; + data.noAutoConvertSRC = pDeviceStateWASAPI->noAutoConvertSRC; + data.noDefaultQualitySRC = pDeviceStateWASAPI->noDefaultQualitySRC; + data.noHardwareOffloading = pDeviceStateWASAPI->noHardwareOffloading; + data.loopbackProcessID = pDeviceStateWASAPI->loopbackProcessID; + data.loopbackProcessExclude = pDeviceStateWASAPI->loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); if (result != MA_SUCCESS) { return result; @@ -24388,8 +24573,8 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */ if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; + pDeviceStateWASAPI->pAudioClientCapture = data.pAudioClient; + pDeviceStateWASAPI->pCaptureClient = data.pCaptureClient; pDevice->capture.internalFormat = data.formatOut; pDevice->capture.internalChannels = data.channelsOut; @@ -24398,18 +24583,18 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; pDevice->capture.internalPeriods = data.periodsOut; - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDeviceStateWASAPI->pAudioClientCapture, (HANDLE)pDeviceStateWASAPI->hEventCapture); - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); + pDeviceStateWASAPI->periodSizeInFramesCapture = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDeviceStateWASAPI->pAudioClientCapture, &pDeviceStateWASAPI->actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); } if (deviceType == ma_device_type_playback) { - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; + pDeviceStateWASAPI->pAudioClientPlayback = data.pAudioClient; + pDeviceStateWASAPI->pRenderClient = data.pRenderClient; pDevice->playback.internalFormat = data.formatOut; pDevice->playback.internalChannels = data.channelsOut; @@ -24418,10 +24603,10 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; pDevice->playback.internalPeriods = data.periodsOut; - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDeviceStateWASAPI->pAudioClientPlayback, (HANDLE)pDeviceStateWASAPI->hEventPlayback); - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); + pDeviceStateWASAPI->periodSizeInFramesPlayback = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDeviceStateWASAPI->pAudioClientPlayback, &pDeviceStateWASAPI->actualBufferSizeInFramesPlayback); /* We must always have a valid ID because rerouting will look at it. */ ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); @@ -24430,8 +24615,14 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev return MA_SUCCESS; } -static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static void ma_device_uninit__wasapi(ma_device* pDevice); + +static ma_result ma_device_init__wasapi(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { + ma_device_state_wasapi* pDeviceStateWASAPI = NULL; + const ma_device_config_wasapi* pDeviceConfigWASAPI = (const ma_device_config_wasapi*)pDeviceBackendConfig; + ma_device_config_wasapi defaultConfigWASAPI; + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result = MA_SUCCESS; #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) @@ -24441,20 +24632,31 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->wasapi); - pDevice->wasapi.usage = pConfig->wasapi.usage; - pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - /* Exclusive mode is not allowed with loopback. */ - if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { + if (deviceType == ma_device_type_loopback && pDescriptorCapture->shareMode == ma_share_mode_exclusive) { return MA_INVALID_DEVICE_CONFIG; } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + if (pDeviceConfigWASAPI == NULL) { + defaultConfigWASAPI = ma_device_config_wasapi_init(); + pDeviceConfigWASAPI = &defaultConfigWASAPI; + } + + pDeviceStateWASAPI = (ma_device_state_wasapi*)ma_calloc(sizeof(*pDeviceStateWASAPI), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateWASAPI == NULL) { + return MA_OUT_OF_MEMORY; + } + + pDeviceStateWASAPI->usage = pDeviceConfigWASAPI->usage; + pDeviceStateWASAPI->noAutoConvertSRC = pDeviceConfigWASAPI->noAutoConvertSRC; + pDeviceStateWASAPI->noDefaultQualitySRC = pDeviceConfigWASAPI->noDefaultQualitySRC; + pDeviceStateWASAPI->noHardwareOffloading = pDeviceConfigWASAPI->noHardwareOffloading; + pDeviceStateWASAPI->loopbackProcessID = pDeviceConfigWASAPI->loopbackProcessID; + pDeviceStateWASAPI->loopbackProcessExclude = pDeviceConfigWASAPI->loopbackProcessExclude; + + + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { ma_device_init_internal_data__wasapi data; data.formatIn = pDescriptorCapture->format; data.channelsIn = pDescriptorCapture->channels; @@ -24464,49 +24666,51 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; data.periodsIn = pDescriptorCapture->periodCount; data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; + data.performanceProfile = ma_performance_profile_low_latency; + data.noAutoConvertSRC = pDeviceConfigWASAPI->noAutoConvertSRC; + data.noDefaultQualitySRC = pDeviceConfigWASAPI->noDefaultQualitySRC; + data.noHardwareOffloading = pDeviceConfigWASAPI->noHardwareOffloading; + data.loopbackProcessID = pDeviceConfigWASAPI->loopbackProcessID; + data.loopbackProcessExclude = pDeviceConfigWASAPI->loopbackProcessExclude; - result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); + result = ma_device_init_internal__wasapi(pDevice->pContext, (deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); if (result != MA_SUCCESS) { + ma_free(pDeviceStateWASAPI, ma_device_get_allocation_callbacks(pDevice)); return result; } - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; + pDeviceStateWASAPI->pAudioClientCapture = data.pAudioClient; + pDeviceStateWASAPI->pCaptureClient = data.pCaptureClient; + pDeviceStateWASAPI->originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; + pDeviceStateWASAPI->originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; + pDeviceStateWASAPI->originalPeriods = pDescriptorCapture->periodCount; + pDeviceStateWASAPI->originalPerformanceProfile = ma_performance_profile_low_latency; /* The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, however, because we want to block until we actually have something for the first call to ma_device_read(). */ - pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ - if (pDevice->wasapi.hEventCapture == NULL) { + pDeviceStateWASAPI->hEventCapture = CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ + if (pDeviceStateWASAPI->hEventCapture == NULL) { result = ma_result_from_GetLastError(GetLastError()); - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; + if (pDeviceStateWASAPI->pCaptureClient != NULL) { + ma_IAudioCaptureClient_Release(pDeviceStateWASAPI->pCaptureClient); + pDeviceStateWASAPI->pCaptureClient = NULL; } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; + if (pDeviceStateWASAPI->pAudioClientCapture != NULL) { + ma_IAudioClient_Release(pDeviceStateWASAPI->pAudioClientCapture); + pDeviceStateWASAPI->pAudioClientCapture = NULL; } + ma_free(pDeviceStateWASAPI, ma_device_get_allocation_callbacks(pDevice)); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); return result; } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); + ma_IAudioClient_SetEventHandle(pDeviceStateWASAPI->pAudioClientCapture, pDeviceStateWASAPI->hEventCapture); - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); + pDeviceStateWASAPI->periodSizeInFramesCapture = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize(pDeviceStateWASAPI->pAudioClientCapture, &pDeviceStateWASAPI->actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); @@ -24520,7 +24724,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf pDescriptorCapture->periodCount = data.periodsOut; } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_device_init_internal_data__wasapi data; data.formatIn = pDescriptorPlayback->format; data.channelsIn = pDescriptorPlayback->channels; @@ -24530,37 +24734,39 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; data.periodsIn = pDescriptorPlayback->periodCount; data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; + data.performanceProfile = ma_performance_profile_low_latency; + data.noAutoConvertSRC = pDeviceConfigWASAPI->noAutoConvertSRC; + data.noDefaultQualitySRC = pDeviceConfigWASAPI->noDefaultQualitySRC; + data.noHardwareOffloading = pDeviceConfigWASAPI->noHardwareOffloading; + data.loopbackProcessID = pDeviceConfigWASAPI->loopbackProcessID; + data.loopbackProcessExclude = pDeviceConfigWASAPI->loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; + if (deviceType == ma_device_type_duplex) { + if (pDeviceStateWASAPI->pCaptureClient != NULL) { + ma_IAudioCaptureClient_Release(pDeviceStateWASAPI->pCaptureClient); + pDeviceStateWASAPI->pCaptureClient = NULL; } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; + if (pDeviceStateWASAPI->pAudioClientCapture != NULL) { + ma_IAudioClient_Release(pDeviceStateWASAPI->pAudioClientCapture); + pDeviceStateWASAPI->pAudioClientCapture = NULL; } - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; + CloseHandle((HANDLE)pDeviceStateWASAPI->hEventCapture); + pDeviceStateWASAPI->hEventCapture = NULL; } + + ma_free(pDeviceStateWASAPI, ma_device_get_allocation_callbacks(pDevice)); return result; } - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; + pDeviceStateWASAPI->pAudioClientPlayback = data.pAudioClient; + pDeviceStateWASAPI->pRenderClient = data.pRenderClient; + pDeviceStateWASAPI->originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; + pDeviceStateWASAPI->originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; + pDeviceStateWASAPI->originalPeriods = pDescriptorPlayback->periodCount; + pDeviceStateWASAPI->originalPerformanceProfile = ma_performance_profile_low_latency; /* The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled @@ -24569,40 +24775,41 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able to get passed WaitForMultipleObjects(). */ - pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ - if (pDevice->wasapi.hEventPlayback == NULL) { + pDeviceStateWASAPI->hEventPlayback = CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ + if (pDeviceStateWASAPI->hEventPlayback == NULL) { result = ma_result_from_GetLastError(GetLastError()); - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; + if (deviceType == ma_device_type_duplex) { + if (pDeviceStateWASAPI->pCaptureClient != NULL) { + ma_IAudioCaptureClient_Release(pDeviceStateWASAPI->pCaptureClient); + pDeviceStateWASAPI->pCaptureClient = NULL; } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; + if (pDeviceStateWASAPI->pAudioClientCapture != NULL) { + ma_IAudioClient_Release(pDeviceStateWASAPI->pAudioClientCapture); + pDeviceStateWASAPI->pAudioClientCapture = NULL; } - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; + CloseHandle(pDeviceStateWASAPI->hEventCapture); + pDeviceStateWASAPI->hEventCapture = NULL; } - if (pDevice->wasapi.pRenderClient != NULL) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; + if (pDeviceStateWASAPI->pRenderClient != NULL) { + ma_IAudioRenderClient_Release(pDeviceStateWASAPI->pRenderClient); + pDeviceStateWASAPI->pRenderClient = NULL; } - if (pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - pDevice->wasapi.pAudioClientPlayback = NULL; + if (pDeviceStateWASAPI->pAudioClientPlayback != NULL) { + ma_IAudioClient_Release(pDeviceStateWASAPI->pAudioClientPlayback); + pDeviceStateWASAPI->pAudioClientPlayback = NULL; } + ma_free(pDeviceStateWASAPI, ma_device_get_allocation_callbacks(pDevice)); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); return result; } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); + ma_IAudioClient_SetEventHandle(pDeviceStateWASAPI->pAudioClientPlayback, pDeviceStateWASAPI->hEventPlayback); - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); + pDeviceStateWASAPI->periodSizeInFramesPlayback = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize(pDeviceStateWASAPI->pAudioClientPlayback, &pDeviceStateWASAPI->actualBufferSizeInFramesPlayback); /* We must always have a valid ID because rerouting will look at it. */ ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); @@ -24622,16 +24829,16 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf stop the device outright and let the application handle it. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { - if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) { - pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; + if (pDeviceConfigWASAPI->noAutoStreamRouting == MA_FALSE) { + if ((deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) && pDescriptorCapture->pDeviceID == NULL) { + pDeviceStateWASAPI->allowCaptureAutoStreamRouting = MA_TRUE; } - if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { - pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; + if ((deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID == NULL) { + pDeviceStateWASAPI->allowPlaybackAutoStreamRouting = MA_TRUE; } } - ma_mutex_init(&pDevice->wasapi.rerouteLock); + ma_mutex_init(&pDeviceStateWASAPI->rerouteLock); hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { @@ -24640,27 +24847,85 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf return ma_result_from_HRESULT(hr); } - pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; - pDevice->wasapi.notificationClient.counter = 1; - pDevice->wasapi.notificationClient.pDevice = pDevice; + pDeviceStateWASAPI->notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; + pDeviceStateWASAPI->notificationClient.counter = 1; + pDeviceStateWASAPI->notificationClient.pDevice = pDevice; - hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); + hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDeviceStateWASAPI->notificationClient); if (SUCCEEDED(hr)) { - pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; + pDeviceStateWASAPI->pDeviceEnumerator = pDeviceEnumerator; } else { /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); } #endif - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); + ma_atomic_bool32_set(&pDeviceStateWASAPI->isStartedCapture, MA_FALSE); + ma_atomic_bool32_set(&pDeviceStateWASAPI->isStartedPlayback, MA_FALSE); + + *ppDeviceState = pDeviceStateWASAPI; return MA_SUCCESS; } +static void ma_device_uninit__wasapi(ma_device* pDevice) +{ + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); + + MA_ASSERT(pDevice != NULL); + + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + { + if (pDeviceStateWASAPI->pDeviceEnumerator) { + pDeviceStateWASAPI->pDeviceEnumerator->lpVtbl->UnregisterEndpointNotificationCallback(pDeviceStateWASAPI->pDeviceEnumerator, &pDeviceStateWASAPI->notificationClient); + ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDeviceStateWASAPI->pDeviceEnumerator); + } + + ma_mutex_uninit(&pDeviceStateWASAPI->rerouteLock); + } + #endif + + if (pDeviceStateWASAPI->pRenderClient) { + if (pDeviceStateWASAPI->pMappedBufferPlayback != NULL) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDeviceStateWASAPI->pRenderClient, pDeviceStateWASAPI->mappedBufferPlaybackCap, 0); + pDeviceStateWASAPI->pMappedBufferPlayback = NULL; + pDeviceStateWASAPI->mappedBufferPlaybackCap = 0; + pDeviceStateWASAPI->mappedBufferPlaybackLen = 0; + } + + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDeviceStateWASAPI->pRenderClient); + } + if (pDeviceStateWASAPI->pCaptureClient) { + if (pDeviceStateWASAPI->pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDeviceStateWASAPI->pCaptureClient, pDeviceStateWASAPI->mappedBufferCaptureCap); + pDeviceStateWASAPI->pMappedBufferCapture = NULL; + pDeviceStateWASAPI->mappedBufferCaptureCap = 0; + pDeviceStateWASAPI->mappedBufferCaptureLen = 0; + } + + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDeviceStateWASAPI->pCaptureClient); + } + + if (pDeviceStateWASAPI->pAudioClientPlayback) { + ma_IAudioClient_Release((ma_IAudioClient*)pDeviceStateWASAPI->pAudioClientPlayback); + } + if (pDeviceStateWASAPI->pAudioClientCapture) { + ma_IAudioClient_Release((ma_IAudioClient*)pDeviceStateWASAPI->pAudioClientCapture); + } + + if (pDeviceStateWASAPI->hEventPlayback) { + CloseHandle((HANDLE)pDeviceStateWASAPI->hEventPlayback); + } + if (pDeviceStateWASAPI->hEventCapture) { + CloseHandle((HANDLE)pDeviceStateWASAPI->hEventCapture); + } + + ma_free(pDeviceStateWASAPI, ma_device_get_allocation_callbacks(pDevice)); +} + static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); ma_uint32 paddingFramesCount; HRESULT hr; ma_share_mode shareMode; @@ -24670,7 +24935,7 @@ static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_ *pFrameCount = 0; - if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { + if (pAudioClient != pDeviceStateWASAPI->pAudioClientPlayback && pAudioClient != pDeviceStateWASAPI->pAudioClientCapture) { return MA_INVALID_OPERATION; } @@ -24689,7 +24954,7 @@ static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_ Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the entire buffer. This depends on the caller making sure they wait on the event handler. */ - shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; + shareMode = (pAudioClient == pDeviceStateWASAPI->pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; if (shareMode == ma_share_mode_shared) { /* Shared mode. */ hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); @@ -24697,17 +24962,17 @@ static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_ return ma_result_from_HRESULT(hr); } - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; + if (pAudioClient == pDeviceStateWASAPI->pAudioClientPlayback) { + *pFrameCount = pDeviceStateWASAPI->actualBufferSizeInFramesPlayback - paddingFramesCount; } else { *pFrameCount = paddingFramesCount; } } else { /* Exclusive mode. */ - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; + if (pAudioClient == pDeviceStateWASAPI->pAudioClientPlayback) { + *pFrameCount = pDeviceStateWASAPI->actualBufferSizeInFramesPlayback; } else { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; + *pFrameCount = pDeviceStateWASAPI->actualBufferSizeInFramesCapture; } } @@ -24732,7 +24997,7 @@ static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type de } ma_device__post_init_setup(pDevice, deviceType); - ma_device__on_notification_rerouted(pDevice); + ma_device_post_notification_rerouted(pDevice); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n"); @@ -24741,34 +25006,36 @@ static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type de static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); + ma_context_state_wasapi* pContextStateWASAPI = ma_context_get_backend_state__wasapi(ma_device_get_context(pDevice)); HRESULT hr; - if (pDevice->pContext->wasapi.hAvrt) { - const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); + if (pContextStateWASAPI->hAvrt) { + const char* pTaskName = ma_to_usage_string__wasapi(pDeviceStateWASAPI->usage); if (pTaskName) { DWORD idx = 0; - pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); + pDeviceStateWASAPI->hAvrtHandle = pContextStateWASAPI->AvSetMmThreadCharacteristicsA(pTaskName, &idx); } } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + hr = ma_IAudioClient_Start(pDeviceStateWASAPI->pAudioClientCapture); if (FAILED(hr)) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); return ma_result_from_HRESULT(hr); } - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE); + ma_atomic_bool32_set(&pDeviceStateWASAPI->isStartedCapture, MA_TRUE); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + hr = ma_IAudioClient_Start(pDeviceStateWASAPI->pAudioClientPlayback); if (FAILED(hr)) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); return ma_result_from_HRESULT(hr); } - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE); + ma_atomic_bool32_set(&pDeviceStateWASAPI->isStartedPlayback, MA_TRUE); } return MA_SUCCESS; @@ -24776,90 +25043,93 @@ static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) static ma_result ma_device_start__wasapi(ma_device* pDevice) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); ma_result result; MA_ASSERT(pDevice != NULL); /* Wait for any rerouting to finish before attempting to start the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); + ma_mutex_lock(&pDeviceStateWASAPI->rerouteLock); { result = ma_device_start__wasapi_nolock(pDevice); } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); + ma_mutex_unlock(&pDeviceStateWASAPI->rerouteLock); return result; } static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); + ma_context_state_wasapi* pContextStateWASAPI = ma_context_get_backend_state__wasapi(ma_device_get_context(pDevice)); ma_result result; HRESULT hr; MA_ASSERT(pDevice != NULL); - if (pDevice->wasapi.hAvrtHandle) { - ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); - pDevice->wasapi.hAvrtHandle = NULL; + if (pDeviceStateWASAPI->hAvrtHandle) { + pContextStateWASAPI->AvRevertMmThreadcharacteristics(pDeviceStateWASAPI->hAvrtHandle); + pDeviceStateWASAPI->hAvrtHandle = NULL; } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { /* If we have a mapped buffer we need to release it. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; + if (pDeviceStateWASAPI->pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer(pDeviceStateWASAPI->pCaptureClient, pDeviceStateWASAPI->mappedBufferCaptureCap); + pDeviceStateWASAPI->pMappedBufferCapture = NULL; + pDeviceStateWASAPI->mappedBufferCaptureCap = 0; + pDeviceStateWASAPI->mappedBufferCaptureLen = 0; } - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + hr = ma_IAudioClient_Stop(pDeviceStateWASAPI->pAudioClientCapture); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); return ma_result_from_HRESULT(hr); } /* The audio client needs to be reset otherwise restarting will fail. */ - hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + hr = ma_IAudioClient_Reset(pDeviceStateWASAPI->pAudioClientCapture); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); return ma_result_from_HRESULT(hr); } - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); + ma_atomic_bool32_set(&pDeviceStateWASAPI->isStartedCapture, MA_FALSE); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + if (pDeviceStateWASAPI->pMappedBufferPlayback != NULL) { ma_silence_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen, + ma_offset_pcm_frames_ptr(pDeviceStateWASAPI->pMappedBufferPlayback, pDeviceStateWASAPI->mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), + pDeviceStateWASAPI->mappedBufferPlaybackCap - pDeviceStateWASAPI->mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels ); - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; + ma_IAudioRenderClient_ReleaseBuffer(pDeviceStateWASAPI->pRenderClient, pDeviceStateWASAPI->mappedBufferPlaybackCap, 0); + pDeviceStateWASAPI->pMappedBufferPlayback = NULL; + pDeviceStateWASAPI->mappedBufferPlaybackCap = 0; + pDeviceStateWASAPI->mappedBufferPlaybackLen = 0; } /* The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. */ - if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { + if (ma_atomic_bool32_get(&pDeviceStateWASAPI->isStartedPlayback)) { /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ - DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate; + DWORD waitTime = (pDeviceStateWASAPI->actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate; if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); + WaitForSingleObject(pDeviceStateWASAPI->hEventPlayback, waitTime); } else { ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1; ma_uint32 framesAvailablePlayback; for (;;) { - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); + result = ma_device__get_available_frames__wasapi(pDevice, pDeviceStateWASAPI->pAudioClientPlayback, &framesAvailablePlayback); if (result != MA_SUCCESS) { break; } - if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { + if (framesAvailablePlayback >= pDeviceStateWASAPI->actualBufferSizeInFramesPlayback) { break; } @@ -24872,13 +25142,13 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) } prevFramesAvailablePlayback = framesAvailablePlayback; - ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); + ResetEvent(pDeviceStateWASAPI->hEventPlayback); /* Manual reset. */ + WaitForSingleObject(pDeviceStateWASAPI->hEventPlayback, waitTime); } } } - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + hr = ma_IAudioClient_Stop(pDeviceStateWASAPI->pAudioClientPlayback); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); return ma_result_from_HRESULT(hr); @@ -24888,7 +25158,7 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) { ma_int32 retries = 5; - while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) { + while ((hr = ma_IAudioClient_Reset(pDeviceStateWASAPI->pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) { ma_sleep(10); retries -= 1; } @@ -24899,7 +25169,7 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) return ma_result_from_HRESULT(hr); } - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); + ma_atomic_bool32_set(&pDeviceStateWASAPI->isStartedPlayback, MA_FALSE); } return MA_SUCCESS; @@ -24907,16 +25177,17 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) static ma_result ma_device_stop__wasapi(ma_device* pDevice) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); ma_result result; MA_ASSERT(pDevice != NULL); /* Wait for any rerouting to finish before attempting to stop the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); + ma_mutex_lock(&pDeviceStateWASAPI->rerouteLock); { result = ma_device_stop__wasapi_nolock(pDevice); } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); + ma_mutex_unlock(&pDeviceStateWASAPI->rerouteLock); return result; } @@ -24928,6 +25199,7 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); ma_result result = MA_SUCCESS; ma_uint32 totalFramesProcessed = 0; @@ -24942,29 +25214,29 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui ma_uint32 framesRemaining = frameCount - totalFramesProcessed; /* If we have a mapped data buffer, consume that first. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { + if (pDeviceStateWASAPI->pMappedBufferCapture != NULL) { /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { - framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; + if (framesToProcessNow > pDeviceStateWASAPI->mappedBufferCaptureLen) { + framesToProcessNow = pDeviceStateWASAPI->mappedBufferCaptureLen; } /* Now just copy the data over to the output buffer. */ ma_copy_pcm_frames( ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), + ma_offset_pcm_frames_const_ptr(pDeviceStateWASAPI->pMappedBufferCapture, pDeviceStateWASAPI->mappedBufferCaptureCap - pDeviceStateWASAPI->mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), framesToProcessNow, pDevice->capture.internalFormat, pDevice->capture.internalChannels ); - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; + totalFramesProcessed += framesToProcessNow; + pDeviceStateWASAPI->mappedBufferCaptureLen -= framesToProcessNow; /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferCaptureLen == 0) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; + if (pDeviceStateWASAPI->mappedBufferCaptureLen == 0) { + ma_IAudioCaptureClient_ReleaseBuffer(pDeviceStateWASAPI->pCaptureClient, pDeviceStateWASAPI->mappedBufferCaptureCap); + pDeviceStateWASAPI->pMappedBufferCapture = NULL; + pDeviceStateWASAPI->mappedBufferCaptureCap = 0; } } else { /* We don't have any cached data pointer, so grab another one. */ @@ -24972,10 +25244,10 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui DWORD flags = 0; /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); + hr = ma_IAudioCaptureClient_GetBuffer(pDeviceStateWASAPI->pCaptureClient, (BYTE**)&pDeviceStateWASAPI->pMappedBufferCapture, &pDeviceStateWASAPI->mappedBufferCaptureCap, &flags, NULL, NULL); if (hr == S_OK) { /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + pDeviceStateWASAPI->mappedBufferCaptureLen = pDeviceStateWASAPI->mappedBufferCaptureCap; /* There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every @@ -24989,7 +25261,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDeviceStateWASAPI->mappedBufferCaptureCap); } } } @@ -25009,7 +25281,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers to prevent a never-ending sequence of glitches due to straddling the end of the buffer. */ - if (pDevice->type == ma_device_type_duplex) { + if (ma_device_get_type(pDevice) == ma_device_type_duplex) { /* Experiment: @@ -25018,30 +25290,30 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui empty out about half. */ ma_uint32 i; - ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); + ma_uint32 periodCount = (pDeviceStateWASAPI->actualBufferSizeInFramesCapture / pDeviceStateWASAPI->periodSizeInFramesCapture); ma_uint32 iterationCount = periodCount / 2; if ((periodCount % 2) > 0) { iterationCount += 1; } for (i = 0; i < iterationCount; i += 1) { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + hr = ma_IAudioCaptureClient_ReleaseBuffer(pDeviceStateWASAPI->pCaptureClient, pDeviceStateWASAPI->mappedBufferCaptureCap); if (FAILED(hr)) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr); break; } flags = 0; - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); + hr = ma_IAudioCaptureClient_GetBuffer(pDeviceStateWASAPI->pCaptureClient, (BYTE**)&pDeviceStateWASAPI->pMappedBufferCapture, &pDeviceStateWASAPI->mappedBufferCaptureCap, &flags, NULL, NULL); if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { /* The buffer has been completely emptied or an error occurred. In this case we'll need to reset the state of the mapped buffer which will trigger the next iteration to get a fresh buffer from WASAPI. */ - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; + pDeviceStateWASAPI->pMappedBufferCapture = NULL; + pDeviceStateWASAPI->mappedBufferCaptureCap = 0; + pDeviceStateWASAPI->mappedBufferCaptureLen = 0; if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { @@ -25060,8 +25332,8 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui } /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + if (pDeviceStateWASAPI->pMappedBufferCapture != NULL) { + pDeviceStateWASAPI->mappedBufferCaptureLen = pDeviceStateWASAPI->mappedBufferCaptureCap; } } } @@ -25084,7 +25356,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui timeoutInMilliseconds = 10; } - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { + if (WaitForSingleObject(pDeviceStateWASAPI->hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { if (pDevice->type == ma_device_type_loopback) { continue; /* Keep waiting in loopback mode. */ } else { @@ -25109,11 +25381,11 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui there's a good chance either an error occurred or the device was stopped mid-read. In this case we'll need to make sure the buffer is released. */ - if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; + if (totalFramesProcessed < frameCount && pDeviceStateWASAPI->pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer(pDeviceStateWASAPI->pCaptureClient, pDeviceStateWASAPI->mappedBufferCaptureCap); + pDeviceStateWASAPI->pMappedBufferCapture = NULL; + pDeviceStateWASAPI->mappedBufferCaptureCap = 0; + pDeviceStateWASAPI->mappedBufferCaptureLen = 0; } if (pFramesRead != NULL) { @@ -25125,6 +25397,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); ma_result result = MA_SUCCESS; ma_uint32 totalFramesProcessed = 0; @@ -25138,30 +25411,30 @@ static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE it means we need to wait for some data to become available. */ - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + if (pDeviceStateWASAPI->pMappedBufferPlayback != NULL) { /* We still have some space available in the mapped data buffer. Write to it. */ ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { - framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); + if (framesToProcessNow > (pDeviceStateWASAPI->mappedBufferPlaybackCap - pDeviceStateWASAPI->mappedBufferPlaybackLen)) { + framesToProcessNow = (pDeviceStateWASAPI->mappedBufferPlaybackCap - pDeviceStateWASAPI->mappedBufferPlaybackLen); } /* Now just copy the data over to the output buffer. */ ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), + ma_offset_pcm_frames_ptr(pDeviceStateWASAPI->pMappedBufferPlayback, pDeviceStateWASAPI->mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), framesToProcessNow, pDevice->playback.internalFormat, pDevice->playback.internalChannels ); - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; + totalFramesProcessed += framesToProcessNow; + pDeviceStateWASAPI->mappedBufferPlaybackLen += framesToProcessNow; /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; + if (pDeviceStateWASAPI->mappedBufferPlaybackLen == pDeviceStateWASAPI->mappedBufferPlaybackCap) { + ma_IAudioRenderClient_ReleaseBuffer(pDeviceStateWASAPI->pRenderClient, pDeviceStateWASAPI->mappedBufferPlaybackCap, 0); + pDeviceStateWASAPI->pMappedBufferPlayback = NULL; + pDeviceStateWASAPI->mappedBufferPlaybackCap = 0; + pDeviceStateWASAPI->mappedBufferPlaybackLen = 0; /* In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never @@ -25169,7 +25442,7 @@ static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames whether or not we need to wait for more data. */ if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + if (WaitForSingleObject(pDeviceStateWASAPI->hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { result = MA_ERROR; break; /* Wait failed. Probably timed out. */ } @@ -25182,20 +25455,20 @@ static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames /* Special rules for exclusive mode. */ if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; + bufferSizeInFrames = pDeviceStateWASAPI->actualBufferSizeInFramesPlayback; } else { - bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; + bufferSizeInFrames = pDeviceStateWASAPI->periodSizeInFramesPlayback; } - hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); + hr = ma_IAudioRenderClient_GetBuffer(pDeviceStateWASAPI->pRenderClient, bufferSizeInFrames, (BYTE**)&pDeviceStateWASAPI->pMappedBufferPlayback); if (hr == S_OK) { /* We have data available. */ - pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; - pDevice->wasapi.mappedBufferPlaybackLen = 0; + pDeviceStateWASAPI->mappedBufferPlaybackCap = bufferSizeInFrames; + pDeviceStateWASAPI->mappedBufferPlaybackLen = 0; } else { if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { /* Not enough data available. We need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + if (WaitForSingleObject((HANDLE)pDeviceStateWASAPI->hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { result = MA_ERROR; break; /* Wait failed. Probably timed out. */ } @@ -25216,201 +25489,44 @@ static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames return result; } -static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) +static void ma_device_wakeup__wasapi(ma_device* pDevice) { + ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); + MA_ASSERT(pDevice != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - SetEvent((HANDLE)pDevice->wasapi.hEventCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + SetEvent(pDeviceStateWASAPI->hEventCapture); } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + SetEvent(pDeviceStateWASAPI->hEventPlayback); } - - return MA_SUCCESS; } -static ma_result ma_context_uninit__wasapi(ma_context* pContext) + +static ma_device_backend_vtable ma_gDeviceBackendVTable_WASAPI = { - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); + ma_backend_info__wasapi, + ma_context_init__wasapi, + ma_context_uninit__wasapi, + ma_context_enumerate_devices__wasapi, + ma_context_get_device_info__wasapi, + ma_device_init__wasapi, + ma_device_uninit__wasapi, + ma_device_start__wasapi, + ma_device_stop__wasapi, + ma_device_read__wasapi, + ma_device_write__wasapi, + NULL, /* onDeviceLoop */ + ma_device_wakeup__wasapi +}; - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_wasapi); - - ma_context_post_command__wasapi(pContext, &cmd); - ma_thread_wait(&pContext->wasapi.commandThread); - - if (pContext->wasapi.hAvrt) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - - #if defined(MA_WIN32_UWP) - { - if (pContext->wasapi.hMMDevapi) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - pContext->wasapi.hMMDevapi = NULL; - } - } - #endif - - /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#ifdef MA_WIN32_DESKTOP - /* - WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven - exclusive mode does not work until SP1. - - Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. - */ - { - ma_OSVERSIONINFOEXW osvi; - ma_handle kernel32DLL; - ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; - ma_PFNVerSetConditionMask _VerSetConditionMask; - - kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); - if (kernel32DLL == NULL) { - return MA_NO_BACKEND; - } - - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); - if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - return MA_NO_BACKEND; - } - - MA_ZERO_OBJECT(&osvi); - osvi.dwOSVersionInfoSize = sizeof(osvi); - osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); - osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); - osvi.wServicePackMajor = 1; - if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { - result = MA_SUCCESS; - } else { - result = MA_NO_BACKEND; - } - - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - } -#endif - - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&pContext->wasapi); - - - #if defined(MA_WIN32_UWP) - { - /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ - pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); - if (pContext->wasapi.hMMDevapi) { - pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); - if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ - } - } else { - return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ - } - } - #endif - - /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ - pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); - if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); - pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); - - /* If either function could not be found, disable use of avrt entirely. */ - if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; - pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - } - - - /* - Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread - than the one that retrieved it with GetService(). This can result in a deadlock in two - situations: - - 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and - 2) When uninitializing and reinitializing the internal IAudioClient object in response to - automatic stream routing. - - We could define ma_device_uninit() such that it must be called on the same thread as - ma_device_init(). We could also just not release the IAudioClient when performing automatic - stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so - we're going to have to work around this with a worker thread. This is not ideal, but I can't - think of a better way to do this. - - More information about this can be found here: - - https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient - - Note this section: - - When releasing an IAudioRenderClient interface instance, the client must call the interface's - Release method from the same thread as the call to IAudioClient::GetService that created the - object. - */ - { - result = ma_mutex_init(&pContext->wasapi.commandLock); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(0, &pContext->wasapi.commandSem); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - - result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - } - - - pCallbacks->onContextInit = ma_context_init__wasapi; - pCallbacks->onContextUninit = ma_context_uninit__wasapi; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; - pCallbacks->onDeviceInit = ma_device_init__wasapi; - pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; - pCallbacks->onDeviceStart = ma_device_start__wasapi; - pCallbacks->onDeviceStop = ma_device_stop__wasapi; - pCallbacks->onDeviceRead = ma_device_read__wasapi; - pCallbacks->onDeviceWrite = ma_device_write__wasapi; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_wasapi = &ma_gDeviceBackendVTable_WASAPI; +#else +ma_device_backend_vtable* ma_device_backend_wasapi = NULL; #endif /****************************************************************************** @@ -25741,12 +25857,109 @@ static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pT static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } -typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); +typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc)(GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); + +typedef HRESULT (WINAPI * ma_DirectSoundCreateProc )(const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); +typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc )(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); +typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc )(const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); + +typedef struct ma_context_state_dsound +{ + HWND hWnd; /* Can be null. */ + ma_handle hDSoundDLL; + ma_DirectSoundCreateProc DirectSoundCreate; + ma_DirectSoundEnumerateAProc DirectSoundEnumerateA; + ma_DirectSoundCaptureCreateProc DirectSoundCaptureCreate; + ma_DirectSoundCaptureEnumerateAProc DirectSoundCaptureEnumerateA; +} ma_context_state_dsound; + +typedef struct ma_device_state_dsound +{ + ma_IDirectSound* pPlayback; + ma_IDirectSoundBuffer* pPlaybackPrimaryBuffer; + ma_IDirectSoundBuffer* pPlaybackBuffer; + ma_IDirectSoundCapture* pCapture; + ma_IDirectSoundCaptureBuffer* pCaptureBuffer; +} ma_device_state_dsound; + + +static ma_context_state_dsound* ma_context_get_backend_state__dsound(ma_context* pContext) +{ + return (ma_context_state_dsound*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_dsound* ma_device_get_backend_state__dsound(ma_device* pDevice) +{ + return (ma_device_state_dsound*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__dsound(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "DirectSound"; +} + +static ma_result ma_context_init__dsound(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_dsound* pContextStateDSound; + const ma_context_config_dsound* pContextConfigDSound = (const ma_context_config_dsound*)pContextBackendConfig; + ma_context_config_dsound defaultConfigDSound; + + if (pContextConfigDSound == NULL) { + defaultConfigDSound = ma_context_config_dsound_init(); + pContextConfigDSound = &defaultConfigDSound; + } + + pContextStateDSound = (ma_context_state_dsound*)ma_calloc(sizeof(*pContextStateDSound), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateDSound == NULL) { + return MA_OUT_OF_MEMORY; + } + + pContextStateDSound->hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); + if (pContextStateDSound->hDSoundDLL == NULL) { + ma_free(pContextStateDSound, ma_context_get_allocation_callbacks(pContext)); + return MA_API_NOT_FOUND; + } + + pContextStateDSound->DirectSoundCreate = (ma_DirectSoundCreateProc )ma_dlsym(ma_context_get_log(pContext), pContextStateDSound->hDSoundDLL, "DirectSoundCreate"); + pContextStateDSound->DirectSoundEnumerateA = (ma_DirectSoundEnumerateAProc )ma_dlsym(ma_context_get_log(pContext), pContextStateDSound->hDSoundDLL, "DirectSoundEnumerateA"); + pContextStateDSound->DirectSoundCaptureCreate = (ma_DirectSoundCaptureCreateProc )ma_dlsym(ma_context_get_log(pContext), pContextStateDSound->hDSoundDLL, "DirectSoundCaptureCreate"); + pContextStateDSound->DirectSoundCaptureEnumerateA = (ma_DirectSoundCaptureEnumerateAProc)ma_dlsym(ma_context_get_log(pContext), pContextStateDSound->hDSoundDLL, "DirectSoundCaptureEnumerateA"); + + /* + We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too + well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient + place to just disable the DirectSound backend for Windows 95. + */ + if (pContextStateDSound->DirectSoundCreate == NULL || + pContextStateDSound->DirectSoundEnumerateA == NULL || + pContextStateDSound->DirectSoundCaptureCreate == NULL || + pContextStateDSound->DirectSoundCaptureEnumerateA == NULL) { + ma_free(pContextStateDSound, ma_context_get_allocation_callbacks(pContext)); + return MA_API_NOT_FOUND; + } + + pContextStateDSound->hWnd = (HWND)pContextConfigDSound->hWnd; + + *ppContextState = pContextStateDSound; + + return MA_SUCCESS; +} + +static void ma_context_uninit__dsound(ma_context* pContext) +{ + ma_context_state_dsound* pContextStateDSound = ma_context_get_backend_state__dsound(pContext); + + ma_dlclose(ma_context_get_log(pContext), pContextStateDSound->hDSoundDLL); + + ma_free(pContextStateDSound, ma_context_get_allocation_callbacks(pContext)); +} + + + static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) { /* Normalize the range in case we were given something stupid. */ @@ -25825,6 +26038,7 @@ static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WOR static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) { + ma_context_state_dsound* pContextStateDSound = ma_context_get_backend_state__dsound(pContext); ma_IDirectSound* pDirectSound; HWND hWnd; HRESULT hr; @@ -25835,13 +26049,13 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma *ppDirectSound = NULL; pDirectSound = NULL; - if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { + if (FAILED(pContextStateDSound->DirectSoundCreate((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } /* The cooperative level must be set before doing anything else. */ - hWnd = (HWND)pContext->dsound.hWnd; + hWnd = pContextStateDSound->hWnd; if (hWnd == 0) { hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); if (hWnd == 0) { @@ -25861,6 +26075,7 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) { + ma_context_state_dsound* pContextStateDSound = ma_context_get_backend_state__dsound(pContext); ma_IDirectSoundCapture* pDirectSoundCapture; HRESULT hr; @@ -25875,7 +26090,7 @@ static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pCont *ppDirectSoundCapture = NULL; pDirectSoundCapture = NULL; - hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); + hr = pContextStateDSound->DirectSoundCaptureCreate((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); return ma_result_from_HRESULT(hr); @@ -26030,6 +26245,7 @@ static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { + ma_context_state_dsound* pContextStateDSound = ma_context_get_backend_state__dsound(pContext); ma_context_enumerate_devices_callback_data__dsound data; MA_ASSERT(pContext != NULL); @@ -26043,13 +26259,13 @@ static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_e /* Playback. */ if (!data.terminated) { data.deviceType = ma_device_type_playback; - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); + pContextStateDSound->DirectSoundEnumerateA(ma_context_enumerate_devices_callback__dsound, &data); } /* Capture. */ if (!data.terminated) { data.deviceType = ma_device_type_capture; - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); + pContextStateDSound->DirectSoundCaptureEnumerateA(ma_context_enumerate_devices_callback__dsound, &data); } return MA_SUCCESS; @@ -26091,6 +26307,7 @@ static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, c static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { + ma_context_state_dsound* pContextStateDSound = ma_context_get_backend_state__dsound(pContext); ma_result result; HRESULT hr; @@ -26105,9 +26322,9 @@ static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_dev data.pDeviceInfo = pDeviceInfo; data.found = MA_FALSE; if (deviceType == ma_device_type_playback) { - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); + pContextStateDSound->DirectSoundEnumerateA(ma_context_get_device_info_callback__dsound, &data); } else { - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); + pContextStateDSound->DirectSoundCaptureEnumerateA(ma_context_get_device_info_callback__dsound, &data); } if (!data.found) { @@ -26244,30 +26461,6 @@ static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_dev -static ma_result ma_device_uninit__dsound(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->dsound.pCaptureBuffer != NULL) { - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - } - if (pDevice->dsound.pCapture != NULL) { - ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); - } - - if (pDevice->dsound.pPlaybackBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - } - if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); - } - if (pDevice->dsound.pPlayback != NULL) { - ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); - } - - return MA_SUCCESS; -} - static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) { GUID subformat; @@ -26319,7 +26512,7 @@ static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 c return MA_SUCCESS; } -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate) { /* DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for @@ -26328,7 +26521,7 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(cons ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); ma_uint32 periodSizeInFrames; - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, ma_performance_profile_low_latency); if (periodSizeInFrames < minPeriodSizeInFrames) { periodSizeInFrames = minPeriodSizeInFrames; } @@ -26336,25 +26529,37 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(cons return periodSizeInFrames; } -static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static void ma_device_uninit__dsound(ma_device* pDevice); + +static ma_result ma_device_init__dsound(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { + ma_device_state_dsound* pDeviceStateDSound; + const ma_device_config_dsound* pDeviceConfigDSound = (const ma_device_config_dsound*)pDeviceBackendConfig; + ma_device_config_dsound defaultConfigDSound; + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result; HRESULT hr; - MA_ASSERT(pDevice != NULL); + if (pDeviceConfigDSound == NULL) { + defaultConfigDSound = ma_device_config_dsound_init(); + pDeviceConfigDSound = &defaultConfigDSound; + } - MA_ZERO_OBJECT(&pDevice->dsound); - - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } + pDeviceStateDSound = (ma_device_state_dsound*)ma_calloc(sizeof(*pDeviceStateDSound), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateDSound == NULL) { + return MA_OUT_OF_MEMORY; + } + /* Unfortunately DirectSound uses different APIs and data structures for playback and capture devices. We need to initialize the capture device first because we'll want to match its buffer size and period count on the playback side if we're using full-duplex mode. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { MA_WAVEFORMATEXTENSIBLE wf; MA_DSCBUFFERDESC descDS; ma_uint32 periodSizeInFrames; @@ -26367,13 +26572,13 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf return result; } - result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); + result = ma_context_create_IDirectSoundCapture__dsound(ma_device_get_context(pDevice), pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, &pDeviceStateDSound->pCapture); if (result != MA_SUCCESS) { ma_device_uninit__dsound(pDevice); return result; } - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); + result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_device_get_context(pDevice), pDeviceStateDSound->pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); if (result != MA_SUCCESS) { ma_device_uninit__dsound(pDevice); return result; @@ -26385,7 +26590,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec); periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; MA_ZERO_OBJECT(&descDS); @@ -26393,7 +26598,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf descDS.dwFlags = 0; descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); + hr = ma_IDirectSoundCapture_CreateCaptureBuffer(pDeviceStateDSound->pCapture, &descDS, &pDeviceStateDSound->pCaptureBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); @@ -26402,7 +26607,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf /* Get the _actual_ properties of the buffer. */ pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + hr = ma_IDirectSoundCaptureBuffer_GetFormat(pDeviceStateDSound->pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); @@ -26427,9 +26632,9 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf */ if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + ma_IDirectSoundCaptureBuffer_Release(pDeviceStateDSound->pCaptureBuffer); - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); + hr = ma_IDirectSoundCapture_CreateCaptureBuffer(pDeviceStateDSound->pCapture, &descDS, &pDeviceStateDSound->pCaptureBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); @@ -26442,7 +26647,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf pDescriptorCapture->periodCount = periodCount; } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { MA_WAVEFORMATEXTENSIBLE wf; MA_DSBUFFERDESC descDSPrimary; MA_DSCAPS caps; @@ -26459,7 +26664,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf return result; } - result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); + result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, &pDeviceStateDSound->pPlayback); if (result != MA_SUCCESS) { ma_device_uninit__dsound(pDevice); return result; @@ -26468,7 +26673,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf MA_ZERO_OBJECT(&descDSPrimary); descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); + hr = ma_IDirectSound_CreateSoundBuffer(pDeviceStateDSound->pPlayback, &descDSPrimary, &pDeviceStateDSound->pPlaybackPrimaryBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); @@ -26479,7 +26684,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf /* We may want to make some adjustments to the format if we are using defaults. */ MA_ZERO_OBJECT(&caps); caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); + hr = ma_IDirectSound_GetCaps(pDeviceStateDSound->pPlayback, &caps); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); @@ -26493,7 +26698,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf nativeChannelCount = 2; /* Look at the speaker configuration to get a better idea on the channel count. */ - if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { + if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig(pDeviceStateDSound->pPlayback, &speakerConfig))) { ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask); } } else { @@ -26526,7 +26731,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer and compare the result with the format that was requested with the SetFormat method. */ - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); + hr = ma_IDirectSoundBuffer_SetFormat(pDeviceStateDSound->pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); if (FAILED(hr)) { /* If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have @@ -26542,7 +26747,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); + hr = ma_IDirectSoundBuffer_SetFormat(pDeviceStateDSound->pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); @@ -26552,7 +26757,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf /* Get the _actual_ properties of the buffer. */ pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + hr = ma_IDirectSoundBuffer_GetFormat(pDeviceStateDSound->pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); @@ -26572,7 +26777,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate); periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; /* @@ -26595,7 +26800,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); + hr = ma_IDirectSound_CreateSoundBuffer(pDeviceStateDSound->pPlayback, &descDS, &pDeviceStateDSound->pPlaybackBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); @@ -26607,12 +26812,39 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf pDescriptorPlayback->periodCount = periodCount; } + *ppDeviceState = pDeviceStateDSound; + return MA_SUCCESS; } - -static ma_result ma_device_data_loop__dsound(ma_device* pDevice) +static void ma_device_uninit__dsound(ma_device* pDevice) { + ma_device_state_dsound* pDeviceStateDSound = ma_device_get_backend_state__dsound(pDevice); + + if (pDeviceStateDSound->pCaptureBuffer != NULL) { + ma_IDirectSoundCaptureBuffer_Release(pDeviceStateDSound->pCaptureBuffer); + } + if (pDeviceStateDSound->pCapture != NULL) { + ma_IDirectSoundCapture_Release(pDeviceStateDSound->pCapture); + } + + if (pDeviceStateDSound->pPlaybackBuffer != NULL) { + ma_IDirectSoundBuffer_Release(pDeviceStateDSound->pPlaybackBuffer); + } + if (pDeviceStateDSound->pPlaybackPrimaryBuffer != NULL) { + ma_IDirectSoundBuffer_Release(pDeviceStateDSound->pPlaybackPrimaryBuffer); + } + if (pDeviceStateDSound->pPlayback != NULL) { + ma_IDirectSound_Release(pDeviceStateDSound->pPlayback); + } + + ma_free(pDeviceStateDSound, ma_device_get_allocation_callbacks(pDevice)); +} + + +static void ma_device_loop__dsound(ma_device* pDevice) +{ + ma_device_state_dsound* pDeviceStateDSound = ma_device_get_backend_state__dsound(pDevice); ma_result result = MA_SUCCESS; ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); @@ -26640,10 +26872,10 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); + hr = ma_IDirectSoundCaptureBuffer_Start(pDeviceStateDSound->pCaptureBuffer, MA_DSCBSTART_LOOPING); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); - return ma_result_from_HRESULT(hr); + return; } } @@ -26654,9 +26886,9 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) { DWORD physicalCaptureCursorInBytes; DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); + hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition(pDeviceStateDSound->pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); + return; } /* If nothing is available we just sleep for a bit and return from this iteration. */ @@ -26694,10 +26926,10 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) continue; /* Nothing is available in the capture buffer. */ } - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); + hr = ma_IDirectSoundCaptureBuffer_Lock(pDeviceStateDSound->pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - return ma_result_from_HRESULT(hr); + return; } @@ -26734,7 +26966,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ /* We need the physical play and write cursors. */ - if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { + if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition(pDeviceStateDSound->pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { break; } @@ -26769,11 +27001,11 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) if (availableBytesPlayback == 0) { /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ if (!isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + hr = ma_IDirectSoundBuffer_Play(pDeviceStateDSound->pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + ma_IDirectSoundCaptureBuffer_Stop(pDeviceStateDSound->pCaptureBuffer); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); + return; } isPlaybackDeviceStarted = MA_TRUE; } else { @@ -26793,7 +27025,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; } - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); + hr = ma_IDirectSoundBuffer_Lock(pDeviceStateDSound->pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); result = ma_result_from_HRESULT(hr); @@ -26836,7 +27068,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) } - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); + hr = ma_IDirectSoundBuffer_Unlock(pDeviceStateDSound->pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); result = ma_result_from_HRESULT(hr); @@ -26855,11 +27087,11 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) */ framesWrittenToPlaybackDevice += framesWrittenThisIteration; if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + hr = ma_IDirectSoundBuffer_Play(pDeviceStateDSound->pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + ma_IDirectSoundCaptureBuffer_Stop(pDeviceStateDSound->pCaptureBuffer); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); + return; } isPlaybackDeviceStarted = MA_TRUE; } @@ -26876,10 +27108,10 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) /* At this point we're done with the mapped portion of the capture buffer. */ - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); + hr = ma_IDirectSoundCaptureBuffer_Unlock(pDeviceStateDSound->pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); + return; } prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); } break; @@ -26890,9 +27122,9 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) { DWORD physicalCaptureCursorInBytes; DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); + hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition(pDeviceStateDSound->pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); if (FAILED(hr)) { - return MA_ERROR; + return; } /* If the previous capture position is the same as the current position we need to wait a bit longer. */ @@ -26927,7 +27159,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) continue; /* Nothing is available in the capture buffer. */ } - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); + hr = ma_IDirectSoundCaptureBuffer_Lock(pDeviceStateDSound->pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); result = ma_result_from_HRESULT(hr); @@ -26939,10 +27171,10 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); + hr = ma_IDirectSoundCaptureBuffer_Unlock(pDeviceStateDSound->pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); + return; } prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; @@ -26958,18 +27190,18 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) DWORD availableBytesPlayback; DWORD physicalPlayCursorInBytes; DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); + hr = ma_IDirectSoundBuffer_GetCurrentPosition(pDeviceStateDSound->pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); if (FAILED(hr)) { break; } - hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus); + hr = ma_IDirectSoundBuffer_GetStatus(pDeviceStateDSound->pPlaybackBuffer, &playbackBufferStatus); if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus); - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + hr = ma_IDirectSoundBuffer_Play(pDeviceStateDSound->pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus); - return ma_result_from_HRESULT(hr); + return; } isPlaybackDeviceStarted = MA_TRUE; @@ -27008,10 +27240,10 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + hr = ma_IDirectSoundBuffer_Play(pDeviceStateDSound->pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); + return; } isPlaybackDeviceStarted = MA_TRUE; } else { @@ -27030,7 +27262,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; } - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); + hr = ma_IDirectSoundBuffer_Lock(pDeviceStateDSound->pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); result = ma_result_from_HRESULT(hr); @@ -27040,7 +27272,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) /* At this point we have a buffer for output. */ ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); + hr = ma_IDirectSoundBuffer_Unlock(pDeviceStateDSound->pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); result = ma_result_from_HRESULT(hr); @@ -27059,30 +27291,30 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) */ framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + hr = ma_IDirectSoundBuffer_Play(pDeviceStateDSound->pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); + return; } isPlaybackDeviceStarted = MA_TRUE; } } break; - default: return MA_INVALID_ARGS; /* Invalid device type. */ + default: return; /* Invalid device type. */ } if (result != MA_SUCCESS) { - return result; + return; } } /* Getting here means the device is being stopped. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + hr = ma_IDirectSoundCaptureBuffer_Stop(pDeviceStateDSound->pCaptureBuffer); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); + return; } } @@ -27093,7 +27325,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) DWORD availableBytesPlayback = 0; DWORD physicalPlayCursorInBytes; DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); + hr = ma_IDirectSoundBuffer_GetCurrentPosition(pDeviceStateDSound->pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); if (FAILED(hr)) { break; } @@ -27128,72 +27360,36 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) } } - hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); + hr = ma_IDirectSoundBuffer_Stop(pDeviceStateDSound->pPlaybackBuffer); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); + return; } - ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); + ma_IDirectSoundBuffer_SetCurrentPosition(pDeviceStateDSound->pPlaybackBuffer, 0); } - - return MA_SUCCESS; } -static ma_result ma_context_uninit__dsound(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_DSound = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_dsound); + ma_backend_info__dsound, + ma_context_init__dsound, + ma_context_uninit__dsound, + ma_context_enumerate_devices__dsound, + ma_context_get_device_info__dsound, + ma_device_init__dsound, + ma_device_uninit__dsound, + NULL, /* onDeviceStart. Started in onDeviceLoop. */ + NULL, /* onDeviceStop. Stopped in onDeviceLoop. */ + NULL, /* onDeviceRead */ + NULL, /* onDeviceWrite */ + ma_device_loop__dsound, + NULL /* onDeviceWakeup */ +}; - ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); - if (pContext->dsound.hDSoundDLL == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); - - /* - We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too - well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient - place to just disable the DirectSound backend for Windows 95. - */ - if (pContext->dsound.DirectSoundCreate == NULL || - pContext->dsound.DirectSoundEnumerateA == NULL || - pContext->dsound.DirectSoundCaptureCreate == NULL || - pContext->dsound.DirectSoundCaptureEnumerateA == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.hWnd = pConfig->dsound.hWnd; - - pCallbacks->onContextInit = ma_context_init__dsound; - pCallbacks->onContextUninit = ma_context_uninit__dsound; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; - pCallbacks->onDeviceInit = ma_device_init__dsound; - pCallbacks->onDeviceUninit = ma_device_uninit__dsound; - pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ - pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ - pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_dsound = &ma_gDeviceBackendVTable_DSound; +#else +ma_device_backend_vtable* ma_device_backend_dsound = NULL; #endif @@ -27298,23 +27494,139 @@ typedef struct GUID NameGuid; } MA_WAVEINCAPS2A; -typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs )(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA )(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen )(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose )(MA_HWAVEOUT hwo); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader )(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); -typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite )(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset )(MA_HWAVEOUT hwo); +typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs )(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA )(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen )(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose )(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader )(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader )(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer )(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart )(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset )(MA_HWAVEIN hwi); + + +typedef struct ma_context_state_winmm +{ + ma_handle hWinMM; + MA_PFN_waveOutGetNumDevs waveOutGetNumDevs; + MA_PFN_waveOutGetDevCapsA waveOutGetDevCapsA; + MA_PFN_waveOutOpen waveOutOpen; + MA_PFN_waveOutClose waveOutClose; + MA_PFN_waveOutPrepareHeader waveOutPrepareHeader; + MA_PFN_waveOutUnprepareHeader waveOutUnprepareHeader; + MA_PFN_waveOutWrite waveOutWrite; + MA_PFN_waveOutReset waveOutReset; + MA_PFN_waveInGetNumDevs waveInGetNumDevs; + MA_PFN_waveInGetDevCapsA waveInGetDevCapsA; + MA_PFN_waveInOpen waveInOpen; + MA_PFN_waveInClose waveInClose; + MA_PFN_waveInPrepareHeader waveInPrepareHeader; + MA_PFN_waveInUnprepareHeader waveInUnprepareHeader; + MA_PFN_waveInAddBuffer waveInAddBuffer; + MA_PFN_waveInStart waveInStart; + MA_PFN_waveInReset waveInReset; +} ma_context_state_winmm; + +typedef struct ma_device_state_winmm +{ + MA_HWAVEOUT hDevicePlayback; + MA_HWAVEIN hDeviceCapture; + HANDLE hEventPlayback; + HANDLE hEventCapture; + ma_uint32 fragmentSizeInFrames; + ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ + ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ + ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ + ma_uint32 headerFramesConsumedCapture; /* ^^^ */ + MA_WAVEHDR* pWAVEHDRPlayback; /* One instantiation for each period. */ + MA_WAVEHDR* pWAVEHDRCapture; /* One instantiation for each period. */ + ma_uint8* pIntermediaryBufferPlayback; + ma_uint8* pIntermediaryBufferCapture; + ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ +} ma_device_state_winmm; + + +static ma_context_state_winmm* ma_context_get_backend_state__winmm(ma_context* pContext) +{ + return (ma_context_state_winmm*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_winmm* ma_device_get_backend_state__winmm(ma_device* pDevice) +{ + return (ma_device_state_winmm*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__winmm(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "WinMM"; +} + +static ma_result ma_context_init__winmm(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_winmm* pContextStateWinMM; + const ma_context_config_winmm* pContextConfigWinMM = (const ma_context_config_winmm*)pContextBackendConfig; + ma_context_config_winmm defaultConfigWinMM; + + if (pContextConfigWinMM == NULL) { + defaultConfigWinMM = ma_context_config_winmm_init(); + pContextConfigWinMM = &defaultConfigWinMM; + } + + (void)pContextConfigWinMM; + + pContextStateWinMM = (ma_context_state_winmm*)ma_calloc(sizeof(*pContextStateWinMM), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateWinMM == NULL) { + return MA_OUT_OF_MEMORY; + } + + pContextStateWinMM->hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); + if (pContextStateWinMM->hWinMM == NULL) { + ma_free(pContextStateWinMM, ma_context_get_allocation_callbacks(pContext)); + return MA_NO_BACKEND; + } + + pContextStateWinMM->waveOutGetNumDevs = (MA_PFN_waveOutGetNumDevs )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveOutGetNumDevs"); + pContextStateWinMM->waveOutGetDevCapsA = (MA_PFN_waveOutGetDevCapsA )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveOutGetDevCapsA"); + pContextStateWinMM->waveOutOpen = (MA_PFN_waveOutOpen )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveOutOpen"); + pContextStateWinMM->waveOutClose = (MA_PFN_waveOutClose )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveOutClose"); + pContextStateWinMM->waveOutPrepareHeader = (MA_PFN_waveOutPrepareHeader )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveOutPrepareHeader"); + pContextStateWinMM->waveOutUnprepareHeader = (MA_PFN_waveOutUnprepareHeader)ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveOutUnprepareHeader"); + pContextStateWinMM->waveOutWrite = (MA_PFN_waveOutWrite )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveOutWrite"); + pContextStateWinMM->waveOutReset = (MA_PFN_waveOutReset )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveOutReset"); + pContextStateWinMM->waveInGetNumDevs = (MA_PFN_waveInGetNumDevs )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveInGetNumDevs"); + pContextStateWinMM->waveInGetDevCapsA = (MA_PFN_waveInGetDevCapsA )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveInGetDevCapsA"); + pContextStateWinMM->waveInOpen = (MA_PFN_waveInOpen )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveInOpen"); + pContextStateWinMM->waveInClose = (MA_PFN_waveInClose )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveInClose"); + pContextStateWinMM->waveInPrepareHeader = (MA_PFN_waveInPrepareHeader )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveInPrepareHeader"); + pContextStateWinMM->waveInUnprepareHeader = (MA_PFN_waveInUnprepareHeader )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveInUnprepareHeader"); + pContextStateWinMM->waveInAddBuffer = (MA_PFN_waveInAddBuffer )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveInAddBuffer"); + pContextStateWinMM->waveInStart = (MA_PFN_waveInStart )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveInStart"); + pContextStateWinMM->waveInReset = (MA_PFN_waveInReset )ma_dlsym(ma_context_get_log(pContext), pContextStateWinMM->hWinMM, "waveInReset"); + + *ppContextState = pContextStateWinMM; + + return MA_SUCCESS; +} + +static void ma_context_uninit__winmm(ma_context* pContext) +{ + ma_context_state_winmm* pContextStateWinMM = ma_context_get_backend_state__winmm(pContext); + + ma_dlclose(ma_context_get_log(pContext), pContextStateWinMM->hWinMM); + + ma_free(pContextStateWinMM, ma_context_get_allocation_callbacks(pContext)); +} + static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) { @@ -27604,6 +27916,7 @@ static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContex static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { + ma_context_state_winmm* pContextStateWinMM = ma_context_get_backend_state__winmm(pContext); UINT playbackDeviceCount; UINT captureDeviceCount; UINT iPlaybackDevice; @@ -27613,14 +27926,14 @@ static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_en MA_ASSERT(callback != NULL); /* Playback. */ - playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); + playbackDeviceCount = pContextStateWinMM->waveOutGetNumDevs(); for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { MA_MMRESULT result; MA_WAVEOUTCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + result = pContextStateWinMM->waveOutGetDevCapsA(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); if (result == MA_MMSYSERR_NOERROR) { ma_device_info deviceInfo; @@ -27642,14 +27955,14 @@ static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_en } /* Capture. */ - captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); + captureDeviceCount = pContextStateWinMM->waveInGetNumDevs(); for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { MA_MMRESULT result; MA_WAVEINCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + result = pContextStateWinMM->waveInGetDevCapsA(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); if (result == MA_MMSYSERR_NOERROR) { ma_device_info deviceInfo; @@ -27675,6 +27988,7 @@ static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_en static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { + ma_context_state_winmm* pContextStateWinMM = ma_context_get_backend_state__winmm(pContext); UINT winMMDeviceID; MA_ASSERT(pContext != NULL); @@ -27697,7 +28011,7 @@ static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_devi MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + result = pContextStateWinMM->waveOutGetDevCapsA(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); if (result == MA_MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); } @@ -27707,7 +28021,7 @@ static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_devi MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + result = pContextStateWinMM->waveInGetDevCapsA(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); if (result == MA_MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); } @@ -27717,35 +28031,14 @@ static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_devi } -static ma_result ma_device_uninit__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - CloseHandle((HANDLE)pDevice->winmm.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate) { /* WinMM has a minimum period size of 40ms. */ ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); ma_uint32 periodSizeInFrames; - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, ma_performance_profile_low_latency); if (periodSizeInFrames < minPeriodSizeInFrames) { periodSizeInFrames = minPeriodSizeInFrames; } @@ -27753,8 +28046,13 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const return periodSizeInFrames; } -static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__winmm(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { + ma_device_state_winmm* pDeviceStateWinMM; + const ma_device_config_winmm* pDeviceConfigWinMM = (const ma_device_config_winmm*)pDeviceBackendConfig; + ma_device_config_winmm defaultConfigWinMM; + ma_context_state_winmm* pContextStateWinMM = ma_context_get_backend_state__winmm(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); const char* errorMsg = ""; ma_result errorCode = MA_ERROR; ma_result result = MA_SUCCESS; @@ -27762,20 +28060,26 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi UINT winMMDeviceIDPlayback = 0; UINT winMMDeviceIDCapture = 0; - MA_ASSERT(pDevice != NULL); + if (pDeviceConfigWinMM == NULL) { + defaultConfigWinMM = ma_device_config_winmm_init(); + pDeviceConfigWinMM = &defaultConfigWinMM; + } - MA_ZERO_OBJECT(&pDevice->winmm); - - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* No exclusive mode with WinMM. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + if (((deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } + pDeviceStateWinMM = (ma_device_state_winmm*)ma_calloc(sizeof(*pDeviceStateWinMM), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateWinMM == NULL) { + return MA_OUT_OF_MEMORY; + } + if (pDescriptorPlayback->pDeviceID != NULL) { winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; } @@ -27784,20 +28088,20 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi } /* The capture device needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { MA_WAVEINCAPSA caps; MA_WAVEFORMATEX wf; MA_MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventCapture == NULL) { + pDeviceStateWinMM->hEventCapture = CreateEventA(NULL, TRUE, TRUE, NULL); + if (pDeviceStateWinMM->hEventCapture == NULL) { errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; } /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { + if (pContextStateWinMM->waveInGetDevCapsA(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; goto on_error; } @@ -27808,7 +28112,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi goto on_error; } - resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + resultMM = pContextStateWinMM->waveInOpen(&pDeviceStateWinMM->hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDeviceStateWinMM->hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); if (resultMM != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; @@ -27819,23 +28123,23 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi pDescriptorCapture->sampleRate = wf.nSamplesPerSec; ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); + pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate); } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { MA_WAVEOUTCAPSA caps; MA_WAVEFORMATEX wf; MA_MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventPlayback == NULL) { + pDeviceStateWinMM->hEventPlayback = CreateEventA(NULL, TRUE, TRUE, NULL); + if (pDeviceStateWinMM->hEventPlayback == NULL) { errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; } /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { + if (pContextStateWinMM->waveOutGetDevCapsA(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; goto on_error; } @@ -27846,7 +28150,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi goto on_error; } - resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + resultMM = pContextStateWinMM->waveOutOpen(&pDeviceStateWinMM->hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDeviceStateWinMM->hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); if (resultMM != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; @@ -27857,7 +28161,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate); } /* @@ -27866,105 +28170,107 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] */ heapSize = 0; - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); } - pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); - if (pDevice->winmm._pHeapData == NULL) { + pDeviceStateWinMM->_pHeapData = (ma_uint8*)ma_calloc(heapSize, ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateWinMM->_pHeapData == NULL) { errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; goto on_error; } - MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); + MA_ZERO_MEMORY(pDeviceStateWinMM->_pHeapData, heapSize); - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { ma_uint32 iPeriod; - if (pConfig->deviceType == ma_device_type_capture) { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); + if (deviceType == ma_device_type_capture) { + pDeviceStateWinMM->pWAVEHDRCapture = (MA_WAVEHDR*)pDeviceStateWinMM->_pHeapData; + pDeviceStateWinMM->pIntermediaryBufferCapture = pDeviceStateWinMM->_pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); } else { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); + pDeviceStateWinMM->pWAVEHDRCapture = (MA_WAVEHDR*)pDeviceStateWinMM->_pHeapData; + pDeviceStateWinMM->pIntermediaryBufferCapture = pDeviceStateWinMM->_pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); } /* Prepare headers. */ for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + pDeviceStateWinMM->pWAVEHDRCapture[iPeriod].lpData = (char*)(pDeviceStateWinMM->pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); + pDeviceStateWinMM->pWAVEHDRCapture[iPeriod].dwBufferLength = periodSizeInBytes; + pDeviceStateWinMM->pWAVEHDRCapture[iPeriod].dwFlags = 0L; + pDeviceStateWinMM->pWAVEHDRCapture[iPeriod].dwLoops = 0L; + pContextStateWinMM->waveInPrepareHeader(pDeviceStateWinMM->hDeviceCapture, &pDeviceStateWinMM->pWAVEHDRCapture[iPeriod], sizeof(MA_WAVEHDR)); /* The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means it's unlocked and available for writing. A value of 1 means it's locked. */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; + pDeviceStateWinMM->pWAVEHDRCapture[iPeriod].dwUser = 0; } } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_uint32 iPeriod; - if (pConfig->deviceType == ma_device_type_playback) { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); + if (deviceType == ma_device_type_playback) { + pDeviceStateWinMM->pWAVEHDRPlayback = (MA_WAVEHDR*)pDeviceStateWinMM->_pHeapData; + pDeviceStateWinMM->pIntermediaryBufferPlayback = pDeviceStateWinMM->_pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); } else { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); + pDeviceStateWinMM->pWAVEHDRPlayback = (MA_WAVEHDR*)pDeviceStateWinMM->_pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); + pDeviceStateWinMM->pIntermediaryBufferPlayback = pDeviceStateWinMM->_pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); } /* Prepare headers. */ for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); + pDeviceStateWinMM->pWAVEHDRPlayback[iPeriod].lpData = (char*)(pDeviceStateWinMM->pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); + pDeviceStateWinMM->pWAVEHDRPlayback[iPeriod].dwBufferLength = periodSizeInBytes; + pDeviceStateWinMM->pWAVEHDRPlayback[iPeriod].dwFlags = 0L; + pDeviceStateWinMM->pWAVEHDRPlayback[iPeriod].dwLoops = 0L; + pContextStateWinMM->waveOutPrepareHeader(pDeviceStateWinMM->hDevicePlayback, &pDeviceStateWinMM->pWAVEHDRPlayback[iPeriod], sizeof(MA_WAVEHDR)); /* The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means it's unlocked and available for writing. A value of 1 means it's locked. */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; + pDeviceStateWinMM->pWAVEHDRPlayback[iPeriod].dwUser = 0; } } + *ppDeviceState = pDeviceStateWinMM; + return MA_SUCCESS; on_error: if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { + if (pDeviceStateWinMM->pWAVEHDRCapture != NULL) { ma_uint32 iPeriod; for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + pContextStateWinMM->waveInUnprepareHeader(pDeviceStateWinMM->hDeviceCapture, &pDeviceStateWinMM->pWAVEHDRCapture[iPeriod], sizeof(MA_WAVEHDR)); } } - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + pContextStateWinMM->waveInClose(pDeviceStateWinMM->hDeviceCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { + if (pDeviceStateWinMM->pWAVEHDRCapture != NULL) { ma_uint32 iPeriod; for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); + pContextStateWinMM->waveOutUnprepareHeader(pDeviceStateWinMM->hDevicePlayback, &pDeviceStateWinMM->pWAVEHDRPlayback[iPeriod], sizeof(MA_WAVEHDR)); } } - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + pContextStateWinMM->waveOutClose(pDeviceStateWinMM->hDevicePlayback); } - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); + ma_free(pDeviceStateWinMM->_pHeapData, ma_device_get_allocation_callbacks(pDevice)); if (errorMsg != NULL && errorMsg[0] != '\0') { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); @@ -27973,23 +28279,46 @@ on_error: return errorCode; } +static void ma_device_uninit__winmm(ma_device* pDevice) +{ + ma_device_state_winmm* pDeviceStateWinMM = ma_device_get_backend_state__winmm(pDevice); + ma_context_state_winmm* pContextStateWinMM = ma_context_get_backend_state__winmm(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStateWinMM->waveInClose(pDeviceStateWinMM->hDeviceCapture); + CloseHandle(pDeviceStateWinMM->hEventCapture); + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStateWinMM->waveOutReset(pDeviceStateWinMM->hDevicePlayback); + pContextStateWinMM->waveOutClose(pDeviceStateWinMM->hDevicePlayback); + CloseHandle(pDeviceStateWinMM->hEventPlayback); + } + + ma_free(pDeviceStateWinMM->_pHeapData, ma_device_get_allocation_callbacks(pDevice)); + ma_free(pDeviceStateWinMM, ma_device_get_allocation_callbacks(pDevice)); +} + static ma_result ma_device_start__winmm(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_winmm* pDeviceStateWinMM = ma_device_get_backend_state__winmm(pDevice); + ma_context_state_winmm* pContextStateWinMM = ma_context_get_backend_state__winmm(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { MA_MMRESULT resultMM; MA_WAVEHDR* pWAVEHDR; ma_uint32 iPeriod; - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + pWAVEHDR = pDeviceStateWinMM->pWAVEHDRCapture; /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); + ResetEvent(pDeviceStateWinMM->hEventCapture); /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + resultMM = pContextStateWinMM->waveInAddBuffer(pDeviceStateWinMM->hDeviceCapture, &pDeviceStateWinMM->pWAVEHDRCapture[iPeriod], sizeof(MA_WAVEHDR)); if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); return ma_result_from_MMRESULT(resultMM); @@ -28000,14 +28329,14 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) } /* Capture devices need to be explicitly started, unlike playback devices. */ - resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + resultMM = pContextStateWinMM->waveInStart(pDeviceStateWinMM->hDeviceCapture); if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); return ma_result_from_MMRESULT(resultMM); } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ } @@ -28016,16 +28345,17 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) static ma_result ma_device_stop__winmm(ma_device* pDevice) { + ma_device_state_winmm* pDeviceStateWinMM = ma_device_get_backend_state__winmm(pDevice); + ma_context_state_winmm* pContextStateWinMM = ma_context_get_backend_state__winmm(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); MA_MMRESULT resultMM; - MA_ASSERT(pDevice != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.hDeviceCapture == NULL) { + if (pDeviceStateWinMM->hDeviceCapture == NULL) { return MA_INVALID_ARGS; } - resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + resultMM = pContextStateWinMM->waveInReset(pDeviceStateWinMM->hDeviceCapture); if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); } @@ -28035,15 +28365,15 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) ma_uint32 iPeriod; MA_WAVEHDR* pWAVEHDR; - if (pDevice->winmm.hDevicePlayback == NULL) { + if (pDeviceStateWinMM->hDevicePlayback == NULL) { return MA_INVALID_ARGS; } /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + pWAVEHDR = pDeviceStateWinMM->pWAVEHDRPlayback; for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { + if (WaitForSingleObject(pDeviceStateWinMM->hEventPlayback, INFINITE) != WAIT_OBJECT_0) { break; /* An error occurred so just abandon ship and stop the device without draining. */ } @@ -28051,7 +28381,7 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) } } - resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + resultMM = pContextStateWinMM->waveOutReset(pDeviceStateWinMM->hDevicePlayback); if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); } @@ -28062,6 +28392,8 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { + ma_device_state_winmm* pDeviceStateWinMM = ma_device_get_backend_state__winmm(pDevice); + ma_context_state_winmm* pContextStateWinMM = ma_context_get_backend_state__winmm(ma_device_get_context(pDevice)); ma_result result = MA_SUCCESS; MA_MMRESULT resultMM; ma_uint32 totalFramesWritten; @@ -28074,38 +28406,38 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram *pFramesWritten = 0; } - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + pWAVEHDR = pDeviceStateWinMM->pWAVEHDRPlayback; /* Keep processing as much data as possible. */ totalFramesWritten = 0; while (totalFramesWritten < frameCount) { /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ + if (pWAVEHDR[pDeviceStateWinMM->iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ /* This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to write it out and move on to the next iteration. */ ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; + ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDeviceStateWinMM->iNextHeaderPlayback].dwBufferLength/bpf) - pDeviceStateWinMM->headerFramesConsumedPlayback; ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); - void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); + void* pDst = ma_offset_ptr(pWAVEHDR[pDeviceStateWinMM->iNextHeaderPlayback].lpData, pDeviceStateWinMM->headerFramesConsumedPlayback*bpf); MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; + pDeviceStateWinMM->headerFramesConsumedPlayback += framesToCopy; totalFramesWritten += framesToCopy; /* If we've consumed the buffer entirely we need to write it out to the device. */ - if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + if (pDeviceStateWinMM->headerFramesConsumedPlayback == (pWAVEHDR[pDeviceStateWinMM->iNextHeaderPlayback].dwBufferLength/bpf)) { + pWAVEHDR[pDeviceStateWinMM->iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ + pWAVEHDR[pDeviceStateWinMM->iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); + ResetEvent(pDeviceStateWinMM->hEventPlayback); /* The device will be started here. */ - resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); + resultMM = pContextStateWinMM->waveOutWrite(pDeviceStateWinMM->hDevicePlayback, &pWAVEHDR[pDeviceStateWinMM->iNextHeaderPlayback], sizeof(MA_WAVEHDR)); if (resultMM != MA_MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); @@ -28113,8 +28445,8 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram } /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; - pDevice->winmm.headerFramesConsumedPlayback = 0; + pDeviceStateWinMM->iNextHeaderPlayback = (pDeviceStateWinMM->iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; + pDeviceStateWinMM->headerFramesConsumedPlayback = 0; } /* If at this point we have consumed the entire input buffer we can return. */ @@ -28128,15 +28460,15 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram } /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { + if (WaitForSingleObject(pDeviceStateWinMM->hEventPlayback, INFINITE) != WAIT_OBJECT_0) { result = MA_ERROR; break; } /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ - pDevice->winmm.headerFramesConsumedPlayback = 0; + if ((pWAVEHDR[pDeviceStateWinMM->iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { + pWAVEHDR[pDeviceStateWinMM->iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ + pDeviceStateWinMM->headerFramesConsumedPlayback = 0; } /* If the device has been stopped we need to break. */ @@ -28154,6 +28486,8 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { + ma_device_state_winmm* pDeviceStateWinMM = ma_device_get_backend_state__winmm(pDevice); + ma_context_state_winmm* pContextStateWinMM = ma_context_get_backend_state__winmm(ma_device_get_context(pDevice)); ma_result result = MA_SUCCESS; MA_MMRESULT resultMM; ma_uint32 totalFramesRead; @@ -28166,35 +28500,35 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ *pFramesRead = 0; } - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + pWAVEHDR = pDeviceStateWinMM->pWAVEHDRCapture; /* Keep processing as much data as possible. */ totalFramesRead = 0; while (totalFramesRead < frameCount) { /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ + if (pWAVEHDR[pDeviceStateWinMM->iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; + ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDeviceStateWinMM->iNextHeaderCapture].dwBufferLength/bpf) - pDeviceStateWinMM->headerFramesConsumedCapture; ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); - const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); + const void* pSrc = ma_offset_ptr(pWAVEHDR[pDeviceStateWinMM->iNextHeaderCapture].lpData, pDeviceStateWinMM->headerFramesConsumedCapture*bpf); void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - pDevice->winmm.headerFramesConsumedCapture += framesToCopy; + pDeviceStateWinMM->headerFramesConsumedCapture += framesToCopy; totalFramesRead += framesToCopy; /* If we've consumed the buffer entirely we need to add it back to the device. */ - if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + if (pDeviceStateWinMM->headerFramesConsumedCapture == (pWAVEHDR[pDeviceStateWinMM->iNextHeaderCapture].dwBufferLength/bpf)) { + pWAVEHDR[pDeviceStateWinMM->iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ + pWAVEHDR[pDeviceStateWinMM->iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); + ResetEvent(pDeviceStateWinMM->hEventCapture); /* The device will be started here. */ - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); + resultMM = pContextStateWinMM->waveInAddBuffer(pDeviceStateWinMM->hDeviceCapture, &pDeviceStateWinMM->pWAVEHDRCapture[pDeviceStateWinMM->iNextHeaderCapture], sizeof(MA_WAVEHDR)); if (resultMM != MA_MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); @@ -28202,8 +28536,8 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ } /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; - pDevice->winmm.headerFramesConsumedCapture = 0; + pDeviceStateWinMM->iNextHeaderCapture = (pDeviceStateWinMM->iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; + pDeviceStateWinMM->headerFramesConsumedCapture = 0; } /* If at this point we have filled the entire input buffer we can return. */ @@ -28217,15 +28551,15 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ } /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { + if (WaitForSingleObject(pDeviceStateWinMM->hEventCapture, INFINITE) != WAIT_OBJECT_0) { result = MA_ERROR; break; } /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ - pDevice->winmm.headerFramesConsumedCapture = 0; + if ((pWAVEHDR[pDeviceStateWinMM->iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { + pWAVEHDR[pDeviceStateWinMM->iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ + pDeviceStateWinMM->headerFramesConsumedCapture = 0; } /* If the device has been stopped we need to break. */ @@ -28241,58 +28575,26 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ return result; } -static ma_result ma_context_uninit__winmm(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_WinMM = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_winmm); + ma_backend_info__winmm, + ma_context_init__winmm, + ma_context_uninit__winmm, + ma_context_enumerate_devices__winmm, + ma_context_get_device_info__winmm, + ma_device_init__winmm, + ma_device_uninit__winmm, + ma_device_start__winmm, + ma_device_stop__winmm, + ma_device_read__winmm, + ma_device_write__winmm, + NULL, /* onDeviceLopp */ + NULL /* onDeviceWakeup */ +}; - ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); - return MA_SUCCESS; -} - -static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); - if (pContext->winmm.hWinMM == NULL) { - return MA_NO_BACKEND; - } - - pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); - - pCallbacks->onContextInit = ma_context_init__winmm; - pCallbacks->onContextUninit = ma_context_uninit__winmm; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; - pCallbacks->onDeviceInit = ma_device_init__winmm; - pCallbacks->onDeviceUninit = ma_device_uninit__winmm; - pCallbacks->onDeviceStart = ma_device_start__winmm; - pCallbacks->onDeviceStop = ma_device_stop__winmm; - pCallbacks->onDeviceRead = ma_device_read__winmm; - pCallbacks->onDeviceWrite = ma_device_write__winmm; - pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_winmm = &ma_gDeviceBackendVTable_WinMM; +#else +ma_device_backend_vtable* ma_device_backend_winmm = NULL; #endif @@ -28611,6 +28913,96 @@ static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { }; +typedef struct ma_context_state_alsa +{ + ma_mutex internalDeviceEnumLock; + ma_bool32 useVerboseDeviceEnumeration; + + ma_handle asoundSO; + ma_snd_pcm_open_proc snd_pcm_open; + ma_snd_pcm_close_proc snd_pcm_close; + ma_snd_pcm_hw_params_sizeof_proc snd_pcm_hw_params_sizeof; + ma_snd_pcm_hw_params_any_proc snd_pcm_hw_params_any; + ma_snd_pcm_hw_params_set_format_proc snd_pcm_hw_params_set_format; + ma_snd_pcm_hw_params_set_format_first_proc snd_pcm_hw_params_set_format_first; + ma_snd_pcm_hw_params_get_format_mask_proc snd_pcm_hw_params_get_format_mask; + ma_snd_pcm_hw_params_set_channels_proc snd_pcm_hw_params_set_channels; + ma_snd_pcm_hw_params_set_channels_near_proc snd_pcm_hw_params_set_channels_near; + ma_snd_pcm_hw_params_set_channels_minmax_proc snd_pcm_hw_params_set_channels_minmax; + ma_snd_pcm_hw_params_set_rate_resample_proc snd_pcm_hw_params_set_rate_resample; + ma_snd_pcm_hw_params_set_rate_proc snd_pcm_hw_params_set_rate; + ma_snd_pcm_hw_params_set_rate_near_proc snd_pcm_hw_params_set_rate_near; + ma_snd_pcm_hw_params_set_rate_minmax_proc snd_pcm_hw_params_set_rate_minmax; + ma_snd_pcm_hw_params_set_buffer_size_near_proc snd_pcm_hw_params_set_buffer_size_near; + ma_snd_pcm_hw_params_set_periods_near_proc snd_pcm_hw_params_set_periods_near; + ma_snd_pcm_hw_params_set_access_proc snd_pcm_hw_params_set_access; + ma_snd_pcm_hw_params_get_format_proc snd_pcm_hw_params_get_format; + ma_snd_pcm_hw_params_get_channels_proc snd_pcm_hw_params_get_channels; + ma_snd_pcm_hw_params_get_channels_min_proc snd_pcm_hw_params_get_channels_min; + ma_snd_pcm_hw_params_get_channels_max_proc snd_pcm_hw_params_get_channels_max; + ma_snd_pcm_hw_params_get_rate_proc snd_pcm_hw_params_get_rate; + ma_snd_pcm_hw_params_get_rate_min_proc snd_pcm_hw_params_get_rate_min; + ma_snd_pcm_hw_params_get_rate_max_proc snd_pcm_hw_params_get_rate_max; + ma_snd_pcm_hw_params_get_buffer_size_proc snd_pcm_hw_params_get_buffer_size; + ma_snd_pcm_hw_params_get_periods_proc snd_pcm_hw_params_get_periods; + ma_snd_pcm_hw_params_get_access_proc snd_pcm_hw_params_get_access; + ma_snd_pcm_hw_params_test_format_proc snd_pcm_hw_params_test_format; + ma_snd_pcm_hw_params_test_channels_proc snd_pcm_hw_params_test_channels; + ma_snd_pcm_hw_params_test_rate_proc snd_pcm_hw_params_test_rate; + ma_snd_pcm_hw_params_proc snd_pcm_hw_params; + ma_snd_pcm_sw_params_sizeof_proc snd_pcm_sw_params_sizeof; + ma_snd_pcm_sw_params_current_proc snd_pcm_sw_params_current; + ma_snd_pcm_sw_params_get_boundary_proc snd_pcm_sw_params_get_boundary; + ma_snd_pcm_sw_params_set_avail_min_proc snd_pcm_sw_params_set_avail_min; + ma_snd_pcm_sw_params_set_start_threshold_proc snd_pcm_sw_params_set_start_threshold; + ma_snd_pcm_sw_params_set_stop_threshold_proc snd_pcm_sw_params_set_stop_threshold; + ma_snd_pcm_sw_params_proc snd_pcm_sw_params; + ma_snd_pcm_format_mask_sizeof_proc snd_pcm_format_mask_sizeof; + ma_snd_pcm_format_mask_test_proc snd_pcm_format_mask_test; + ma_snd_pcm_get_chmap_proc snd_pcm_get_chmap; + ma_snd_pcm_state_proc snd_pcm_state; + ma_snd_pcm_prepare_proc snd_pcm_prepare; + ma_snd_pcm_start_proc snd_pcm_start; + ma_snd_pcm_drop_proc snd_pcm_drop; + ma_snd_pcm_drain_proc snd_pcm_drain; + ma_snd_pcm_reset_proc snd_pcm_reset; + ma_snd_device_name_hint_proc snd_device_name_hint; + ma_snd_device_name_get_hint_proc snd_device_name_get_hint; + ma_snd_card_get_index_proc snd_card_get_index; + ma_snd_device_name_free_hint_proc snd_device_name_free_hint; + ma_snd_pcm_mmap_begin_proc snd_pcm_mmap_begin; + ma_snd_pcm_mmap_commit_proc snd_pcm_mmap_commit; + ma_snd_pcm_recover_proc snd_pcm_recover; + ma_snd_pcm_readi_proc snd_pcm_readi; + ma_snd_pcm_writei_proc snd_pcm_writei; + ma_snd_pcm_avail_proc snd_pcm_avail; + ma_snd_pcm_avail_update_proc snd_pcm_avail_update; + ma_snd_pcm_wait_proc snd_pcm_wait; + ma_snd_pcm_nonblock_proc snd_pcm_nonblock; + ma_snd_pcm_info_proc snd_pcm_info; + ma_snd_pcm_info_sizeof_proc snd_pcm_info_sizeof; + ma_snd_pcm_info_get_name_proc snd_pcm_info_get_name; + ma_snd_pcm_poll_descriptors_proc snd_pcm_poll_descriptors; + ma_snd_pcm_poll_descriptors_count_proc snd_pcm_poll_descriptors_count; + ma_snd_pcm_poll_descriptors_revents_proc snd_pcm_poll_descriptors_revents; + ma_snd_config_update_free_global_proc snd_config_update_free_global; +} ma_context_state_alsa; + +typedef struct ma_device_state_alsa +{ + ma_snd_pcm_t* pPCMPlayback; + ma_snd_pcm_t* pPCMCapture; + struct pollfd* pPollDescriptorsPlayback; + struct pollfd* pPollDescriptorsCapture; + int pollDescriptorCountPlayback; + int pollDescriptorCountCapture; + int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ + int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ + ma_bool8 isUsingMMapPlayback; + ma_bool8 isUsingMMapCapture; +} ma_device_state_alsa; + + static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) { ma_snd_pcm_format_t ALSAFormats[] = { @@ -28810,7 +29202,7 @@ static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) return MA_TRUE; } -static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ +static int ma_convert_device_name_to_hw_format__alsa(ma_context_state_alsa* pContextStateALSA, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ { /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ @@ -28851,7 +29243,7 @@ static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ } - cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); + cardIndex = pContextStateALSA->snd_card_get_index(card); if (cardIndex < 0) { return -2; /* Failed to retrieve the card index. */ } @@ -28888,12 +29280,12 @@ static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uin } -static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) +static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_context_state_alsa* pContextStateALSA, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) { ma_snd_pcm_t* pPCM; ma_snd_pcm_stream_t stream; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pContextStateALSA != NULL); MA_ASSERT(ppPCM != NULL); *ppPCM = NULL; @@ -28941,7 +29333,7 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s isDeviceOpen = MA_FALSE; for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { - if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { + if (pContextStateALSA->snd_pcm_open(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { isDeviceOpen = MA_TRUE; break; } @@ -28967,7 +29359,7 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s if (deviceID.alsa[0] != ':') { /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); + resultALSA = pContextStateALSA->snd_pcm_open(&pPCM, deviceID.alsa, stream, openMode); } else { char hwid[256]; @@ -28984,7 +29376,7 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s } if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); + resultALSA = pContextStateALSA->snd_pcm_open(&pPCM, hwid, stream, openMode); } } @@ -28992,7 +29384,7 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s if (resultALSA != 0) { ma_strcpy_s(hwid, sizeof(hwid), "hw"); if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); + resultALSA = pContextStateALSA->snd_pcm_open(&pPCM, hwid, stream, openMode); } } } @@ -29008,8 +29400,308 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s } +static ma_context_state_alsa* ma_context_get_backend_state__alsa(ma_context* pContext) +{ + return (ma_context_state_alsa*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_alsa* ma_device_get_backend_state__alsa(ma_device* pDevice) +{ + return (ma_device_state_alsa*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__alsa(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "ALSA"; +} + +static ma_result ma_context_init__alsa(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_alsa* pContextStateALSA; + const ma_context_config_alsa* pContextConfigALSA = (ma_context_config_alsa*)pContextBackendConfig; + ma_context_config_alsa defaultConfigALSA; + ma_log* pLog = ma_context_get_log(pContext); + ma_result result; + + if (pContextConfigALSA == NULL) { + defaultConfigALSA = ma_context_config_alsa_init(); + pContextConfigALSA = &defaultConfigALSA; + } + + pContextStateALSA = (ma_context_state_alsa*)ma_calloc(sizeof(*pContextStateALSA), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateALSA == NULL) { + return MA_OUT_OF_MEMORY; + } + + #ifndef MA_NO_RUNTIME_LINKING + { + const char* libasoundNames[] = { + "libasound.so.2", + "libasound.so" + }; + size_t i; + + for (i = 0; i < ma_countof(libasoundNames); ++i) { + pContextStateALSA->asoundSO = ma_dlopen(pLog, libasoundNames[i]); + if (pContextStateALSA->asoundSO != NULL) { + break; + } + } + + if (pContextStateALSA->asoundSO == NULL) { + ma_free(pContextStateALSA, ma_context_get_allocation_callbacks(pContext)); + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); + return MA_NO_BACKEND; + } + + pContextStateALSA->snd_pcm_open = (ma_snd_pcm_open_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_open"); + pContextStateALSA->snd_pcm_close = (ma_snd_pcm_close_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_close"); + pContextStateALSA->snd_pcm_hw_params_sizeof = (ma_snd_pcm_hw_params_sizeof_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_sizeof"); + pContextStateALSA->snd_pcm_hw_params_any = (ma_snd_pcm_hw_params_any_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_any"); + pContextStateALSA->snd_pcm_hw_params_set_format = (ma_snd_pcm_hw_params_set_format_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_format"); + pContextStateALSA->snd_pcm_hw_params_set_format_first = (ma_snd_pcm_hw_params_set_format_first_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_format_first"); + pContextStateALSA->snd_pcm_hw_params_get_format_mask = (ma_snd_pcm_hw_params_get_format_mask_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_format_mask"); + pContextStateALSA->snd_pcm_hw_params_set_channels = (ma_snd_pcm_hw_params_set_channels_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_channels"); + pContextStateALSA->snd_pcm_hw_params_set_channels_near = (ma_snd_pcm_hw_params_set_channels_near_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_channels_near"); + pContextStateALSA->snd_pcm_hw_params_set_channels_minmax = (ma_snd_pcm_hw_params_set_channels_minmax_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_channels_minmax"); + pContextStateALSA->snd_pcm_hw_params_set_rate_resample = (ma_snd_pcm_hw_params_set_rate_resample_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_rate_resample"); + pContextStateALSA->snd_pcm_hw_params_set_rate = (ma_snd_pcm_hw_params_set_rate_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_rate"); + pContextStateALSA->snd_pcm_hw_params_set_rate_near = (ma_snd_pcm_hw_params_set_rate_near_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_rate_near"); + pContextStateALSA->snd_pcm_hw_params_set_rate_minmax = (ma_snd_pcm_hw_params_set_rate_minmax_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_rate_minmax"); + pContextStateALSA->snd_pcm_hw_params_set_buffer_size_near = (ma_snd_pcm_hw_params_set_buffer_size_near_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); + pContextStateALSA->snd_pcm_hw_params_set_periods_near = (ma_snd_pcm_hw_params_set_periods_near_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_periods_near"); + pContextStateALSA->snd_pcm_hw_params_set_access = (ma_snd_pcm_hw_params_set_access_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_set_access"); + pContextStateALSA->snd_pcm_hw_params_get_format = (ma_snd_pcm_hw_params_get_format_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_format"); + pContextStateALSA->snd_pcm_hw_params_get_channels = (ma_snd_pcm_hw_params_get_channels_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_channels"); + pContextStateALSA->snd_pcm_hw_params_get_channels_min = (ma_snd_pcm_hw_params_get_channels_min_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_channels_min"); + pContextStateALSA->snd_pcm_hw_params_get_channels_max = (ma_snd_pcm_hw_params_get_channels_max_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_channels_max"); + pContextStateALSA->snd_pcm_hw_params_get_rate = (ma_snd_pcm_hw_params_get_rate_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_rate"); + pContextStateALSA->snd_pcm_hw_params_get_rate_min = (ma_snd_pcm_hw_params_get_rate_min_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_rate_min"); + pContextStateALSA->snd_pcm_hw_params_get_rate_max = (ma_snd_pcm_hw_params_get_rate_max_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_rate_max"); + pContextStateALSA->snd_pcm_hw_params_get_buffer_size = (ma_snd_pcm_hw_params_get_buffer_size_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_buffer_size"); + pContextStateALSA->snd_pcm_hw_params_get_periods = (ma_snd_pcm_hw_params_get_periods_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_periods"); + pContextStateALSA->snd_pcm_hw_params_get_access = (ma_snd_pcm_hw_params_get_access_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_get_access"); + pContextStateALSA->snd_pcm_hw_params_test_format = (ma_snd_pcm_hw_params_test_format_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_test_format"); + pContextStateALSA->snd_pcm_hw_params_test_channels = (ma_snd_pcm_hw_params_test_channels_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_test_channels"); + pContextStateALSA->snd_pcm_hw_params_test_rate = (ma_snd_pcm_hw_params_test_rate_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params_test_rate"); + pContextStateALSA->snd_pcm_hw_params = (ma_snd_pcm_hw_params_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_hw_params"); + pContextStateALSA->snd_pcm_sw_params_sizeof = (ma_snd_pcm_sw_params_sizeof_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_sw_params_sizeof"); + pContextStateALSA->snd_pcm_sw_params_current = (ma_snd_pcm_sw_params_current_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_sw_params_current"); + pContextStateALSA->snd_pcm_sw_params_get_boundary = (ma_snd_pcm_sw_params_get_boundary_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_sw_params_get_boundary"); + pContextStateALSA->snd_pcm_sw_params_set_avail_min = (ma_snd_pcm_sw_params_set_avail_min_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_sw_params_set_avail_min"); + pContextStateALSA->snd_pcm_sw_params_set_start_threshold = (ma_snd_pcm_sw_params_set_start_threshold_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_sw_params_set_start_threshold"); + pContextStateALSA->snd_pcm_sw_params_set_stop_threshold = (ma_snd_pcm_sw_params_set_stop_threshold_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_sw_params_set_stop_threshold"); + pContextStateALSA->snd_pcm_sw_params = (ma_snd_pcm_sw_params_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_sw_params"); + pContextStateALSA->snd_pcm_format_mask_sizeof = (ma_snd_pcm_format_mask_sizeof_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_format_mask_sizeof"); + pContextStateALSA->snd_pcm_format_mask_test = (ma_snd_pcm_format_mask_test_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_format_mask_test"); + pContextStateALSA->snd_pcm_get_chmap = (ma_snd_pcm_get_chmap_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_get_chmap"); + pContextStateALSA->snd_pcm_state = (ma_snd_pcm_state_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_state"); + pContextStateALSA->snd_pcm_prepare = (ma_snd_pcm_prepare_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_prepare"); + pContextStateALSA->snd_pcm_start = (ma_snd_pcm_start_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_start"); + pContextStateALSA->snd_pcm_drop = (ma_snd_pcm_drop_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_drop"); + pContextStateALSA->snd_pcm_drain = (ma_snd_pcm_drain_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_drain"); + pContextStateALSA->snd_pcm_reset = (ma_snd_pcm_reset_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_reset"); + pContextStateALSA->snd_device_name_hint = (ma_snd_device_name_hint_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_device_name_hint"); + pContextStateALSA->snd_device_name_get_hint = (ma_snd_device_name_get_hint_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_device_name_get_hint"); + pContextStateALSA->snd_card_get_index = (ma_snd_card_get_index_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_card_get_index"); + pContextStateALSA->snd_device_name_free_hint = (ma_snd_device_name_free_hint_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_device_name_free_hint"); + pContextStateALSA->snd_pcm_mmap_begin = (ma_snd_pcm_mmap_begin_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_mmap_begin"); + pContextStateALSA->snd_pcm_mmap_commit = (ma_snd_pcm_mmap_commit_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_mmap_commit"); + pContextStateALSA->snd_pcm_recover = (ma_snd_pcm_recover_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_recover"); + pContextStateALSA->snd_pcm_readi = (ma_snd_pcm_readi_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_readi"); + pContextStateALSA->snd_pcm_writei = (ma_snd_pcm_writei_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_writei"); + pContextStateALSA->snd_pcm_avail = (ma_snd_pcm_avail_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_avail"); + pContextStateALSA->snd_pcm_avail_update = (ma_snd_pcm_avail_update_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_avail_update"); + pContextStateALSA->snd_pcm_wait = (ma_snd_pcm_wait_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_wait"); + pContextStateALSA->snd_pcm_nonblock = (ma_snd_pcm_nonblock_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_nonblock"); + pContextStateALSA->snd_pcm_info = (ma_snd_pcm_info_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_info"); + pContextStateALSA->snd_pcm_info_sizeof = (ma_snd_pcm_info_sizeof_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_info_sizeof"); + pContextStateALSA->snd_pcm_info_get_name = (ma_snd_pcm_info_get_name_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_info_get_name"); + pContextStateALSA->snd_pcm_poll_descriptors = (ma_snd_pcm_poll_descriptors_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_poll_descriptors"); + pContextStateALSA->snd_pcm_poll_descriptors_count = (ma_snd_pcm_poll_descriptors_count_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_poll_descriptors_count"); + pContextStateALSA->snd_pcm_poll_descriptors_revents = (ma_snd_pcm_poll_descriptors_revents_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_pcm_poll_descriptors_revents"); + pContextStateALSA->snd_config_update_free_global = (ma_snd_config_update_free_global_proc )ma_dlsym(pLog, pContextStateALSA->asoundSO, "snd_config_update_free_global"); + } + #else + { + /* The system below is just for type safety. */ + ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; + ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; + ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; + ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; + ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; + ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; + ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; + ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; + ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; + ma_snd_pcm_hw_params_set_channels_minmax_proc _snd_pcm_hw_params_set_channels_minmax = snd_pcm_hw_params_set_channels_minmax; + ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; + ma_snd_pcm_hw_params_set_rate_proc _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; + ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; + ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; + ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; + ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; + ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; + ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; + ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; + ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; + ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; + ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; + ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; + ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; + ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; + ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; + ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; + ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; + ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; + ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; + ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; + ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; + ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; + ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; + ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; + ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; + ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; + ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; + ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; + ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; + ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; + ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; + ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; + ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; + ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; + ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; + ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; + ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; + ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; + ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; + ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; + ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; + ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; + ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; + ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; + ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; + ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; + ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; + ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; + ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; + ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; + ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; + ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; + ma_snd_pcm_poll_descriptors_proc _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; + ma_snd_pcm_poll_descriptors_count_proc _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; + ma_snd_pcm_poll_descriptors_revents_proc _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; + ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; + + pContextStateALSA->snd_pcm_open = _snd_pcm_open; + pContextStateALSA->snd_pcm_close = _snd_pcm_close; + pContextStateALSA->snd_pcm_hw_params_sizeof = _snd_pcm_hw_params_sizeof; + pContextStateALSA->snd_pcm_hw_params_any = _snd_pcm_hw_params_any; + pContextStateALSA->snd_pcm_hw_params_set_format = _snd_pcm_hw_params_set_format; + pContextStateALSA->snd_pcm_hw_params_set_format_first = _snd_pcm_hw_params_set_format_first; + pContextStateALSA->snd_pcm_hw_params_get_format_mask = _snd_pcm_hw_params_get_format_mask; + pContextStateALSA->snd_pcm_hw_params_set_channels = _snd_pcm_hw_params_set_channels; + pContextStateALSA->snd_pcm_hw_params_set_channels_near = _snd_pcm_hw_params_set_channels_near; + pContextStateALSA->snd_pcm_hw_params_set_channels_minmax = _snd_pcm_hw_params_set_channels_minmax; + pContextStateALSA->snd_pcm_hw_params_set_rate_resample = _snd_pcm_hw_params_set_rate_resample; + pContextStateALSA->snd_pcm_hw_params_set_rate = _snd_pcm_hw_params_set_rate; + pContextStateALSA->snd_pcm_hw_params_set_rate_near = _snd_pcm_hw_params_set_rate_near; + pContextStateALSA->snd_pcm_hw_params_set_rate_minmax = _snd_pcm_hw_params_set_rate_minmax; + pContextStateALSA->snd_pcm_hw_params_set_buffer_size_near = _snd_pcm_hw_params_set_buffer_size_near; + pContextStateALSA->snd_pcm_hw_params_set_periods_near = _snd_pcm_hw_params_set_periods_near; + pContextStateALSA->snd_pcm_hw_params_set_access = _snd_pcm_hw_params_set_access; + pContextStateALSA->snd_pcm_hw_params_get_format = _snd_pcm_hw_params_get_format; + pContextStateALSA->snd_pcm_hw_params_get_channels = _snd_pcm_hw_params_get_channels; + pContextStateALSA->snd_pcm_hw_params_get_channels_min = _snd_pcm_hw_params_get_channels_min; + pContextStateALSA->snd_pcm_hw_params_get_channels_max = _snd_pcm_hw_params_get_channels_max; + pContextStateALSA->snd_pcm_hw_params_get_rate = _snd_pcm_hw_params_get_rate; + pContextStateALSA->snd_pcm_hw_params_get_rate_min = _snd_pcm_hw_params_get_rate_min; + pContextStateALSA->snd_pcm_hw_params_get_rate_max = _snd_pcm_hw_params_get_rate_max; + pContextStateALSA->snd_pcm_hw_params_get_buffer_size = _snd_pcm_hw_params_get_buffer_size; + pContextStateALSA->snd_pcm_hw_params_get_periods = _snd_pcm_hw_params_get_periods; + pContextStateALSA->snd_pcm_hw_params_get_access = _snd_pcm_hw_params_get_access; + pContextStateALSA->snd_pcm_hw_params_test_format = _snd_pcm_hw_params_test_format; + pContextStateALSA->snd_pcm_hw_params_test_channels = _snd_pcm_hw_params_test_channels; + pContextStateALSA->snd_pcm_hw_params_test_rate = _snd_pcm_hw_params_test_rate; + pContextStateALSA->snd_pcm_hw_params = _snd_pcm_hw_params; + pContextStateALSA->snd_pcm_sw_params_sizeof = _snd_pcm_sw_params_sizeof; + pContextStateALSA->snd_pcm_sw_params_current = _snd_pcm_sw_params_current; + pContextStateALSA->snd_pcm_sw_params_get_boundary = _snd_pcm_sw_params_get_boundary; + pContextStateALSA->snd_pcm_sw_params_set_avail_min = _snd_pcm_sw_params_set_avail_min; + pContextStateALSA->snd_pcm_sw_params_set_start_threshold = _snd_pcm_sw_params_set_start_threshold; + pContextStateALSA->snd_pcm_sw_params_set_stop_threshold = _snd_pcm_sw_params_set_stop_threshold; + pContextStateALSA->snd_pcm_sw_params = _snd_pcm_sw_params; + pContextStateALSA->snd_pcm_format_mask_sizeof = _snd_pcm_format_mask_sizeof; + pContextStateALSA->snd_pcm_format_mask_test = _snd_pcm_format_mask_test; + pContextStateALSA->snd_pcm_get_chmap = _snd_pcm_get_chmap; + pContextStateALSA->snd_pcm_state = _snd_pcm_state; + pContextStateALSA->snd_pcm_prepare = _snd_pcm_prepare; + pContextStateALSA->snd_pcm_start = _snd_pcm_start; + pContextStateALSA->snd_pcm_drop = _snd_pcm_drop; + pContextStateALSA->snd_pcm_drain = _snd_pcm_drain; + pContextStateALSA->snd_pcm_reset = _snd_pcm_reset; + pContextStateALSA->snd_device_name_hint = _snd_device_name_hint; + pContextStateALSA->snd_device_name_get_hint = _snd_device_name_get_hint; + pContextStateALSA->snd_card_get_index = _snd_card_get_index; + pContextStateALSA->snd_device_name_free_hint = _snd_device_name_free_hint; + pContextStateALSA->snd_pcm_mmap_begin = _snd_pcm_mmap_begin; + pContextStateALSA->snd_pcm_mmap_commit = _snd_pcm_mmap_commit; + pContextStateALSA->snd_pcm_recover = _snd_pcm_recover; + pContextStateALSA->snd_pcm_readi = _snd_pcm_readi; + pContextStateALSA->snd_pcm_writei = _snd_pcm_writei; + pContextStateALSA->snd_pcm_avail = _snd_pcm_avail; + pContextStateALSA->snd_pcm_avail_update = _snd_pcm_avail_update; + pContextStateALSA->snd_pcm_wait = _snd_pcm_wait; + pContextStateALSA->snd_pcm_nonblock = _snd_pcm_nonblock; + pContextStateALSA->snd_pcm_info = _snd_pcm_info; + pContextStateALSA->snd_pcm_info_sizeof = _snd_pcm_info_sizeof; + pContextStateALSA->snd_pcm_info_get_name = _snd_pcm_info_get_name; + pContextStateALSA->snd_pcm_poll_descriptors = _snd_pcm_poll_descriptors; + pContextStateALSA->snd_pcm_poll_descriptors_count = _snd_pcm_poll_descriptors_count; + pContextStateALSA->snd_pcm_poll_descriptors_revents = _snd_pcm_poll_descriptors_revents; + pContextStateALSA->snd_config_update_free_global = _snd_config_update_free_global; + } + #endif + + pContextStateALSA->useVerboseDeviceEnumeration = pContextConfigALSA->useVerboseDeviceEnumeration; + + result = ma_mutex_init(&pContextStateALSA->internalDeviceEnumLock); + if (result != MA_SUCCESS) { + ma_free(pContextStateALSA, ma_context_get_allocation_callbacks(pContext)); + ma_log_postf(pLog, MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); + return result; + } + + *ppContextState = pContextStateALSA; + + return MA_SUCCESS; +} + +static void ma_context_uninit__alsa(ma_context* pContext) +{ + ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(pContext); + + MA_ASSERT(pContextStateALSA != NULL); + + /* Clean up memory for memory leak checkers. */ + pContextStateALSA->snd_config_update_free_global(); + + #ifndef MA_NO_RUNTIME_LINKING + { + ma_dlclose(ma_context_get_log(pContext), pContextStateALSA->asoundSO); + } + #endif + + ma_mutex_uninit(&pContextStateALSA->internalDeviceEnumLock); + + ma_free(pContextStateALSA, ma_context_get_allocation_callbacks(pContext)); +} + static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { + ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(pContext); int resultALSA; ma_bool32 cbResult = MA_TRUE; char** ppDeviceHints; @@ -29017,22 +29709,22 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu ma_uint32 uniqueIDCount = 0; char** ppNextDeviceHint; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pContextStateALSA != NULL); MA_ASSERT(callback != NULL); - ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); + ma_mutex_lock(&pContextStateALSA->internalDeviceEnumLock); - resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); + resultALSA = pContextStateALSA->snd_device_name_hint(-1, "pcm", (void***)&ppDeviceHints); if (resultALSA < 0) { - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); + ma_mutex_unlock(&pContextStateALSA->internalDeviceEnumLock); return ma_result_from_errno(-resultALSA); } ppNextDeviceHint = ppDeviceHints; while (*ppNextDeviceHint != NULL) { - char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); - char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); - char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); + char* NAME = pContextStateALSA->snd_device_name_get_hint(*ppNextDeviceHint, "NAME"); + char* DESC = pContextStateALSA->snd_device_name_get_hint(*ppNextDeviceHint, "DESC"); + char* IOID = pContextStateALSA->snd_device_name_get_hint(*ppNextDeviceHint, "IOID"); ma_device_type deviceType = ma_device_type_playback; ma_bool32 stopEnumeration = MA_FALSE; char hwid[sizeof(pUniqueIDs->alsa)]; @@ -29046,12 +29738,12 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu } if (NAME != NULL) { - if (pContext->alsa.useVerboseDeviceEnumeration) { + if (pContextStateALSA->useVerboseDeviceEnumeration) { /* Verbose mode. Use the name exactly as-is. */ ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); } else { /* Simplified mode. Use ":%d,%d" format. */ - if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { + if (ma_convert_device_name_to_hw_format__alsa(pContextStateALSA, hwid, sizeof(hwid), NAME) == 0) { /* At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device @@ -29071,7 +29763,7 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu } else { /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); - ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); + ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, ma_context_get_allocation_callbacks(pContext)); if (pNewUniqueIDs == NULL) { goto next_device; /* Failed to allocate memory. */ } @@ -29114,7 +29806,7 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu if (line2 != NULL) { line2 += 1; /* Skip past the new-line character. */ - if (pContext->alsa.useVerboseDeviceEnumeration) { + if (pContextStateALSA->useVerboseDeviceEnumeration) { /* Verbose mode. Put the second line in brackets. */ ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); @@ -29169,10 +29861,10 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu } } - ma_free(pUniqueIDs, &pContext->allocationCallbacks); - ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); + ma_free(pUniqueIDs, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_device_name_free_hint((void**)ppDeviceHints); - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); + ma_mutex_unlock(&pContextStateALSA->internalDeviceEnumLock); return MA_SUCCESS; } @@ -29206,13 +29898,13 @@ static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_device_type d return !pData->foundDevice; } -static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) +static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context_state_alsa* pContextStateALSA, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) { MA_ASSERT(pPCM != NULL); MA_ASSERT(pHWParams != NULL); MA_ASSERT(pDeviceInfo != NULL); - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { + if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && pContextStateALSA->snd_pcm_hw_params_test_rate(pPCM, pHWParams, sampleRate, 0) == 0) { pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; @@ -29221,7 +29913,7 @@ static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pC } } -static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) +static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context_state_alsa* pContextStateALSA, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) { ma_uint32 iSampleRate; unsigned int minSampleRate; @@ -29229,8 +29921,8 @@ static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ /* There could be a range. */ - ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); - ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); + pContextStateALSA->snd_pcm_hw_params_get_rate_min(pHWParams, &minSampleRate, &sampleRateDir); + pContextStateALSA->snd_pcm_hw_params_get_rate_max(pHWParams, &maxSampleRate, &sampleRateDir); /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculous. */ minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); @@ -29240,22 +29932,23 @@ static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); + ma_context_test_rate_and_add_native_data_format__alsa(pContextStateALSA, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); } } /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ if (!ma_is_standard_sample_rate(minSampleRate)) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); + ma_context_test_rate_and_add_native_data_format__alsa(pContextStateALSA, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); } if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); + ma_context_test_rate_and_add_native_data_format__alsa(pContextStateALSA, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); } } static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { + ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(pContext); ma_context_get_device_info_enum_callback_data__alsa data; ma_result result; int resultALSA; @@ -29264,7 +29957,7 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic ma_uint32 iFormat; ma_uint32 iChannel; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pContextStateALSA != NULL); /* We just enumerate to find basic information about the device. */ data.deviceType = deviceType; @@ -29285,22 +29978,22 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic } /* For detailed info we need to open the device. */ - result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); + result = ma_context_open_pcm__alsa(pContext, pContextStateALSA, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); if (result != MA_SUCCESS) { return result; } /* We need to initialize a HW parameters object in order to know what formats are supported. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); + pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(pContextStateALSA->snd_pcm_hw_params_sizeof(), ma_context_get_allocation_callbacks(pContext)); if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + pContextStateALSA->snd_pcm_close(pPCM); return MA_OUT_OF_MEMORY; } - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + resultALSA = pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams); if (resultALSA < 0) { - ma_free(pHWParams, &pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); return ma_result_from_errno(-resultALSA); } @@ -29323,10 +30016,10 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic For each format we need to make sure we reset the configuration space so we don't return channel counts and rates that aren't compatible with a format. */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams); /* Test the format first. If this fails it means the format is not supported and we can skip it. */ - if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { + if (pContextStateALSA->snd_pcm_hw_params_test_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { /* The format is supported. */ unsigned int minChannels; unsigned int maxChannels; @@ -29335,11 +30028,11 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic The configuration space needs to be restricted to this format so we can get an accurate picture of which sample rates and channel counts are support with this format. */ - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); + pContextStateALSA->snd_pcm_hw_params_set_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); /* Now we need to check for supported channels. */ - ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); - ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); + pContextStateALSA->snd_pcm_hw_params_get_channels_min(pHWParams, &minChannels); + pContextStateALSA->snd_pcm_hw_params_get_channels_max(pHWParams, &maxChannels); if (minChannels > MA_MAX_CHANNELS) { continue; /* Too many channels. */ @@ -29357,7 +30050,7 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ + ma_context_iterate_rates_and_add_native_data_format__alsa(pContextStateALSA, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ } else { /* The device only supports a specific set of channels. We need to iterate over all of them. */ for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { @@ -29365,17 +30058,17 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic unsigned int channels = iChannel; /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); + pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams); + pContextStateALSA->snd_pcm_hw_params_set_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { + if (pContextStateALSA->snd_pcm_hw_params_test_channels(pPCM, pHWParams, channels) == 0) { /* The channel count is supported. */ /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ - ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); + pContextStateALSA->snd_pcm_hw_params_set_channels(pPCM, pHWParams, channels); /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); + ma_context_iterate_rates_and_add_native_data_format__alsa(pContextStateALSA, pPCM, pHWParams, format, channels, 0, pDeviceInfo); } else { /* The channel count is not supported. Skip. */ } @@ -29386,32 +30079,14 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic } } - ma_free(pHWParams, &pContext->allocationCallbacks); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + pContextStateALSA->snd_pcm_close(pPCM); return MA_SUCCESS; } -static ma_result ma_device_uninit__alsa(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - close(pDevice->alsa.wakeupfdCapture); - ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); - } - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - close(pDevice->alsa.wakeupfdPlayback); - ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +static ma_result ma_device_init_by_type__alsa(ma_context* pContext, ma_context_state_alsa* pContextStateALSA, ma_device_state_alsa* pDeviceStateALSA, const ma_device_config_alsa* pDeviceConfigALSA, ma_device_descriptor* pDescriptor, ma_device_type deviceType) { ma_result result; int resultALSA; @@ -29432,42 +30107,43 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic struct pollfd* pPollDescriptors; int wakeupfd; - MA_ASSERT(pConfig != NULL); + MA_ASSERT(pContextStateALSA != NULL); + MA_ASSERT(pDeviceStateALSA != NULL); + MA_ASSERT(pDeviceConfigALSA != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ - MA_ASSERT(pDevice != NULL); formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); openMode = 0; - if (pConfig->alsa.noAutoResample) { + if (pDeviceConfigALSA->noAutoResample) { openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; } - if (pConfig->alsa.noAutoChannels) { + if (pDeviceConfigALSA->noAutoChannels) { openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; } - if (pConfig->alsa.noAutoFormat) { + if (pDeviceConfigALSA->noAutoFormat) { openMode |= MA_SND_PCM_NO_AUTO_FORMAT; } - result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); + result = ma_context_open_pcm__alsa(pContext, pContextStateALSA, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); if (result != MA_SUCCESS) { return result; } /* Hardware parameters. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); + pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(pContextStateALSA->snd_pcm_hw_params_sizeof(), ma_context_get_allocation_callbacks(pContext)); if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); return MA_OUT_OF_MEMORY; } - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + resultALSA = pContextStateALSA->snd_pcm_hw_params_any(pPCM, pHWParams); if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); return ma_result_from_errno(-resultALSA); } @@ -29476,19 +30152,19 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic #if 0 /* NOTE: MMAP mode temporarily disabled. */ if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */ if (!pConfig->alsa.noMMap) { - if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { - pDevice->alsa.isUsingMMap = MA_TRUE; + if (pContextStateALSA->snd_pcm_hw_params_set_access(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { + pDeviceStateALSA->isUsingMMap = MA_TRUE; } } } #endif if (!isUsingMMap) { - resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); + resultALSA = pContextStateALSA->snd_pcm_hw_params_set_access(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); return ma_result_from_errno(-resultALSA); } } @@ -29504,39 +30180,39 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. */ - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { + if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || pContextStateALSA->snd_pcm_hw_params_test_format(pPCM, pHWParams, formatALSA) != 0) { /* We're either requesting the native format or the specified format is not supported. */ size_t iFormat; formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { + if (pContextStateALSA->snd_pcm_hw_params_test_format(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); break; } } if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); return MA_FORMAT_NOT_SUPPORTED; } } - resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); + resultALSA = pContextStateALSA->snd_pcm_hw_params_set_format(pPCM, pHWParams, formatALSA); if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); return ma_result_from_errno(-resultALSA); } internalFormat = ma_format_from_alsa(formatALSA); if (internalFormat == ma_format_unknown) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); return MA_FORMAT_NOT_SUPPORTED; } } @@ -29548,11 +30224,11 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic channels = MA_DEFAULT_CHANNELS; } - resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); + resultALSA = pContextStateALSA->snd_pcm_hw_params_set_channels_near(pPCM, pHWParams, &channels); if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); return ma_result_from_errno(-resultALSA); } @@ -29580,18 +30256,18 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. */ - ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); + pContextStateALSA->snd_pcm_hw_params_set_rate_resample(pPCM, pHWParams, 0); sampleRate = pDescriptor->sampleRate; if (sampleRate == 0) { sampleRate = MA_DEFAULT_SAMPLE_RATE; } - resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); + resultALSA = pContextStateALSA->snd_pcm_hw_params_set_rate_near(pPCM, pHWParams, &sampleRate, 0); if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); return ma_result_from_errno(-resultALSA); } @@ -29602,11 +30278,11 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic { ma_uint32 periods = pDescriptor->periodCount; - resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); + resultALSA = pContextStateALSA->snd_pcm_hw_params_set_periods_near(pPCM, pHWParams, &periods, NULL); if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); return ma_result_from_errno(-resultALSA); } @@ -29615,13 +30291,13 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic /* Buffer Size */ { - ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; + ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, ma_performance_profile_low_latency) * internalPeriods; - resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); + resultALSA = pContextStateALSA->snd_pcm_hw_params_set_buffer_size_near(pPCM, pHWParams, &actualBufferSizeInFrames); if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); return ma_result_from_errno(-resultALSA); } @@ -29629,43 +30305,43 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic } /* Apply hardware parameters. */ - resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); + resultALSA = pContextStateALSA->snd_pcm_hw_params(pPCM, pHWParams); if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); return ma_result_from_errno(-resultALSA); } - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, ma_context_get_allocation_callbacks(pContext)); pHWParams = NULL; /* Software parameters. */ - pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); + pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(pContextStateALSA->snd_pcm_sw_params_sizeof(), ma_context_get_allocation_callbacks(pContext)); if (pSWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); return MA_OUT_OF_MEMORY; } - resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); + resultALSA = pContextStateALSA->snd_pcm_sw_params_current(pPCM, pSWParams); if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); + ma_free(pSWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); return ma_result_from_errno(-resultALSA); } - resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); + resultALSA = pContextStateALSA->snd_pcm_sw_params_set_avail_min(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); + ma_free(pSWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); return ma_result_from_errno(-resultALSA); } - resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); + resultALSA = pContextStateALSA->snd_pcm_sw_params_get_boundary(pSWParams, &bufferBoundary); if (resultALSA < 0) { bufferBoundary = internalPeriodSizeInFrames * internalPeriods; } @@ -29675,40 +30351,40 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to the size of a period. But for full-duplex we need to set it such that it is at least two periods. */ - resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); + resultALSA = pContextStateALSA->snd_pcm_sw_params_set_start_threshold(pPCM, pSWParams, internalPeriodSizeInFrames*2); if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); + ma_free(pSWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); return ma_result_from_errno(-resultALSA); } - resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); + resultALSA = pContextStateALSA->snd_pcm_sw_params_set_stop_threshold(pPCM, pSWParams, bufferBoundary); if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); + ma_free(pSWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); return ma_result_from_errno(-resultALSA); } } - resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); + resultALSA = pContextStateALSA->snd_pcm_sw_params(pPCM, pSWParams); if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); + ma_free(pSWParams, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); return ma_result_from_errno(-resultALSA); } - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pSWParams, ma_context_get_allocation_callbacks(pContext)); pSWParams = NULL; /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ { ma_snd_pcm_chmap_t* pChmap = NULL; - if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { - pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); + if (pContextStateALSA->snd_pcm_get_chmap != NULL) { + pChmap = pContextStateALSA->snd_pcm_get_chmap(pPCM); } if (pChmap != NULL) { @@ -29768,17 +30444,17 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic available for reading or writing. There's no well defined maximum for this so we're just going to allocate this on the heap. */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); + pollDescriptorCount = pContextStateALSA->snd_pcm_poll_descriptors_count(pPCM); if (pollDescriptorCount <= 0) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); return MA_ERROR; } - pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ + pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), ma_context_get_allocation_callbacks(pContext)); /* +1 because we want room for the wakeup descriptor. */ if (pPollDescriptors == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); return MA_OUT_OF_MEMORY; } @@ -29788,9 +30464,9 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic */ wakeupfd = eventfd(0, 0); if (wakeupfd < 0) { - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); + ma_free(pPollDescriptors, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); return ma_result_from_errno(errno); } @@ -29800,43 +30476,43 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic pPollDescriptors[0].revents = 0; /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ + pollDescriptorCount = pContextStateALSA->snd_pcm_poll_descriptors(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ if (pollDescriptorCount <= 0) { close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); + ma_free(pPollDescriptors, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); return MA_ERROR; } if (deviceType == ma_device_type_capture) { - pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; - pDevice->alsa.wakeupfdCapture = wakeupfd; + pDeviceStateALSA->pollDescriptorCountCapture = pollDescriptorCount; + pDeviceStateALSA->pPollDescriptorsCapture = pPollDescriptors; + pDeviceStateALSA->wakeupfdCapture = wakeupfd; } else { - pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; - pDevice->alsa.wakeupfdPlayback = wakeupfd; + pDeviceStateALSA->pollDescriptorCountPlayback = pollDescriptorCount; + pDeviceStateALSA->pPollDescriptorsPlayback = pPollDescriptors; + pDeviceStateALSA->wakeupfdPlayback = wakeupfd; } /* We're done. Prepare the device. */ - resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); + resultALSA = pContextStateALSA->snd_pcm_prepare(pPCM); if (resultALSA < 0) { close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); + ma_free(pPollDescriptors, ma_context_get_allocation_callbacks(pContext)); + pContextStateALSA->snd_pcm_close(pPCM); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); return ma_result_from_errno(-resultALSA); } if (deviceType == ma_device_type_capture) { - pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapCapture = isUsingMMap; + pDeviceStateALSA->pPCMCapture = pPCM; + pDeviceStateALSA->isUsingMMapCapture = isUsingMMap; } else { - pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapPlayback = isUsingMMap; + pDeviceStateALSA->pPCMPlayback = pPCM; + pDeviceStateALSA->isUsingMMapPlayback = isUsingMMap; } pDescriptor->format = internalFormat; @@ -29849,46 +30525,86 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic return MA_SUCCESS; } -static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__alsa(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { - MA_ASSERT(pDevice != NULL); + ma_device_state_alsa* pDeviceStateALSA; + const ma_device_config_alsa* pDeviceConfigALSA = (const ma_device_config_alsa*)pDeviceBackendConfig; + ma_device_config_alsa defaultConfigALSA; + ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); - MA_ZERO_OBJECT(&pDevice->alsa); + MA_ASSERT(pContextStateALSA != NULL); - if (pConfig->deviceType == ma_device_type_loopback) { + if (pDeviceConfigALSA == NULL) { + defaultConfigALSA = ma_device_config_alsa_init(); + pDeviceConfigALSA = &defaultConfigALSA; + } + + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + pDeviceStateALSA = (ma_device_state_alsa*)ma_calloc(sizeof(*pDeviceStateALSA), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateALSA == NULL) { + return MA_OUT_OF_MEMORY; + } + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_by_type__alsa(ma_device_get_context(pDevice), pContextStateALSA, pDeviceStateALSA, pDeviceConfigALSA, pDescriptorCapture, ma_device_type_capture); if (result != MA_SUCCESS) { + ma_free(pDeviceStateALSA, ma_device_get_allocation_callbacks(pDevice)); return result; } } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_by_type__alsa(ma_device_get_context(pDevice), pContextStateALSA, pDeviceStateALSA, pDeviceConfigALSA, pDescriptorPlayback, ma_device_type_playback); if (result != MA_SUCCESS) { + ma_free(pDeviceStateALSA, ma_device_get_allocation_callbacks(pDevice)); return result; } } + *ppDeviceState = pDeviceStateALSA; + return MA_SUCCESS; } +static void ma_device_uninit__alsa(ma_device* pDevice) +{ + ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice); + ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice)); + + if (pDeviceStateALSA->pPCMCapture) { + pContextStateALSA->snd_pcm_close(pDeviceStateALSA->pPCMCapture); + close(pDeviceStateALSA->wakeupfdCapture); + ma_free(pDeviceStateALSA->pPollDescriptorsCapture, ma_device_get_allocation_callbacks(pDevice)); + } + + if (pDeviceStateALSA->pPCMPlayback) { + pContextStateALSA->snd_pcm_close(pDeviceStateALSA->pPCMPlayback); + close(pDeviceStateALSA->wakeupfdPlayback); + ma_free(pDeviceStateALSA->pPollDescriptorsPlayback, ma_device_get_allocation_callbacks(pDevice)); + } + + ma_free(pDeviceStateALSA, ma_device_get_allocation_callbacks(pDevice)); +} + static ma_result ma_device_start__alsa(ma_device* pDevice) { + ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice); + ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice)); int resultALSA; - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + if (pDeviceStateALSA->pPCMCapture != NULL) { + resultALSA = pContextStateALSA->snd_pcm_start(pDeviceStateALSA->pPCMCapture); if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); return ma_result_from_errno(-resultALSA); } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDeviceStateALSA->pPCMPlayback != NULL) { /* When data is written to the device we wait for the device to get ready to receive data with poll(). In my testing I have observed that poll() can sometimes block forever unless the device is started explicitly with snd_pcm_start() @@ -29899,7 +30615,7 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) to call into snd_pcm_writei() in an attempt to prevent the underrun, we would run the risk of a weird deadlock issue as documented inside ma_device_write__alsa(). */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + resultALSA = pContextStateALSA->snd_pcm_start(pDeviceStateALSA->pPCMPlayback); if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device."); return ma_result_from_errno(-resultALSA); @@ -29911,55 +30627,58 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) static ma_result ma_device_stop__alsa(ma_device* pDevice) { + ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice); + ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice)); + int resultPoll; + int resultRead; + /* The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. */ - int resultPoll; - int resultRead; - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDeviceStateALSA->pPCMCapture != NULL) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + pContextStateALSA->snd_pcm_drop(pDeviceStateALSA->pPCMCapture); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); /* We need to prepare the device again, otherwise we won't be able to restart the device. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { + if (pContextStateALSA->snd_pcm_prepare(pDeviceStateALSA->pPCMCapture) < 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); } /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); + resultPoll = poll(pDeviceStateALSA->pPollDescriptorsCapture, 1, 0); if (resultPoll > 0) { ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); + resultRead = read(pDeviceStateALSA->pPollDescriptorsCapture[0].fd, &t, sizeof(t)); if (resultRead != sizeof(t)) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead); } } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDeviceStateALSA->pPCMPlayback != NULL) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + pContextStateALSA->snd_pcm_drop(pDeviceStateALSA->pPCMPlayback); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); /* We need to prepare the device again, otherwise we won't be able to restart the device. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { + if (pContextStateALSA->snd_pcm_prepare(pDeviceStateALSA->pPCMPlayback) < 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); } /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); + resultPoll = poll(pDeviceStateALSA->pPollDescriptorsPlayback, 1, 0); if (resultPoll > 0) { ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); + resultRead = read(pDeviceStateALSA->pPollDescriptorsPlayback[0].fd, &t, sizeof(t)); if (resultRead != sizeof(t)) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead); } @@ -29969,7 +30688,7 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) return MA_SUCCESS; } -static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) +static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_context_state_alsa* pContextStateALSA, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) { for (;;) { unsigned short revents; @@ -30008,18 +30727,18 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st Getting here means that some data should be able to be read. We need to use ALSA to translate the revents flags for us. */ - resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ + resultALSA = pContextStateALSA->snd_pcm_poll_descriptors_revents(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); return ma_result_from_errno(-resultALSA); } if ((revents & POLLERR) != 0) { - ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); + ma_snd_pcm_state_t state = pContextStateALSA->snd_pcm_state(pPCM); if (state == MA_SND_PCM_STATE_XRUN) { /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", pContextStateALSA->snd_pcm_state(pPCM)); } } @@ -30031,38 +30750,38 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st return MA_SUCCESS; } -static ma_result ma_device_wait_read__alsa(ma_device* pDevice) +static ma_result ma_device_wait_read__alsa(ma_device* pDevice, ma_context_state_alsa* pContextStateALSA, ma_device_state_alsa* pDeviceStateALSA) { - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ + return ma_device_wait__alsa(pDevice, pContextStateALSA, pDeviceStateALSA->pPCMCapture, pDeviceStateALSA->pPollDescriptorsCapture, pDeviceStateALSA->pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ } -static ma_result ma_device_wait_write__alsa(ma_device* pDevice) +static ma_result ma_device_wait_write__alsa(ma_device* pDevice, ma_context_state_alsa* pContextStateALSA, ma_device_state_alsa* pDeviceStateALSA) { - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ + return ma_device_wait__alsa(pDevice, pContextStateALSA, pDeviceStateALSA->pPCMPlayback, pDeviceStateALSA->pPollDescriptorsPlayback, pDeviceStateALSA->pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ } static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) { + ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice); + ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice)); ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesRead != NULL); - if (pFramesRead != NULL) { - *pFramesRead = 0; - } + *pFramesRead = 0; - while (ma_device_get_state(pDevice) == ma_device_state_started) { + for (;;) { ma_result result; /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ - result = ma_device_wait_read__alsa(pDevice); + result = ma_device_wait_read__alsa(pDevice, pContextStateALSA, pDeviceStateALSA); if (result != MA_SUCCESS) { return result; } /* Getting here means we should have data available. */ - resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); + resultALSA = pContextStateALSA->snd_pcm_readi(pDeviceStateALSA->pPCMCapture, pFramesOut, frameCount); if (resultALSA >= 0) { break; /* Success. */ } else { @@ -30073,13 +30792,13 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); /* Overrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); + resultALSA = pContextStateALSA->snd_pcm_recover(pDeviceStateALSA->pPCMCapture, resultALSA, MA_TRUE); if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); return ma_result_from_errno((int)-resultALSA); } - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + resultALSA = pContextStateALSA->snd_pcm_start(pDeviceStateALSA->pPCMCapture); if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); return ma_result_from_errno((int)-resultALSA); @@ -30097,27 +30816,27 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u return MA_SUCCESS; } -static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFramesIn, ma_uint32 frameCount, ma_uint32* pFramesWritten) { + ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice); + ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice)); ma_snd_pcm_sframes_t resultALSA = 0; + + MA_ASSERT(pFramesIn != NULL); + MA_ASSERT(pFramesWritten != NULL); - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrames != NULL); + *pFramesWritten = 0; - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { + for (;;) { ma_result result; /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ - result = ma_device_wait_write__alsa(pDevice); + result = ma_device_wait_write__alsa(pDevice, pContextStateALSA, pDeviceStateALSA); if (result != MA_SUCCESS) { return result; } - resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); + resultALSA = pContextStateALSA->snd_pcm_writei(pDeviceStateALSA->pPCMPlayback, pFramesIn, frameCount); if (resultALSA >= 0) { break; /* Success. */ } else { @@ -30128,7 +30847,7 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); /* Underrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ + resultALSA = pContextStateALSA->snd_pcm_recover(pDeviceStateALSA->pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); return ma_result_from_errno((int)-resultALSA); @@ -30141,7 +30860,7 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't quite right here. */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + resultALSA = pContextStateALSA->snd_pcm_start(pDeviceStateALSA->pPCMPlayback); if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); return ma_result_from_errno((int)-resultALSA); @@ -30159,299 +30878,52 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, return MA_SUCCESS; } -static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) +static void ma_device_wakeup__alsa(ma_device* pDevice) { + ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice); ma_uint64 t = 1; int resultWrite = 0; - MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDeviceStateALSA != NULL); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ - if (pDevice->alsa.pPollDescriptorsCapture != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); + if (pDeviceStateALSA->pPollDescriptorsCapture != NULL) { + resultWrite = write(pDeviceStateALSA->wakeupfdCapture, &t, sizeof(t)); } - if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); + if (pDeviceStateALSA->pPollDescriptorsPlayback != NULL) { + resultWrite = write(pDeviceStateALSA->wakeupfdPlayback, &t, sizeof(t)); } if (resultWrite < 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); - return ma_result_from_errno(errno); + return; } ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); - - return MA_SUCCESS; } -static ma_result ma_context_uninit__alsa(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_ALSA = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_alsa); + ma_backend_info__alsa, + ma_context_init__alsa, + ma_context_uninit__alsa, + ma_context_enumerate_devices__alsa, + ma_context_get_device_info__alsa, + ma_device_init__alsa, + ma_device_uninit__alsa, + ma_device_start__alsa, + ma_device_stop__alsa, + ma_device_read__alsa, + ma_device_write__alsa, + NULL, /* onDeviceLoop */ + ma_device_wakeup__alsa +}; - /* Clean up memory for memory leak checkers. */ - ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); -#endif - - ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libasoundNames[] = { - "libasound.so.2", - "libasound.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); - if (pContext->alsa.asoundSO != NULL) { - break; - } - } - - if (pContext->alsa.asoundSO == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); - return MA_NO_BACKEND; - } - - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); +ma_device_backend_vtable* ma_device_backend_alsa = &ma_gDeviceBackendVTable_ALSA; #else - /* The system below is just for type safety. */ - ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; - ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; - ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; - ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; - ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; - ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; - ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; - ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; - ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; - ma_snd_pcm_hw_params_set_channels_minmax_proc _snd_pcm_hw_params_set_channels_minmax = snd_pcm_hw_params_set_channels_minmax; - ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; - ma_snd_pcm_hw_params_set_rate_proc _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; - ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; - ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; - ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; - ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; - ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; - ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; - ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; - ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; - ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; - ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; - ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; - ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; - ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; - ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; - ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; - ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; - ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; - ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; - ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; - ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; - ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; - ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; - ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; - ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; - ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; - ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; - ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; - ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; - ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; - ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; - ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; - ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; - ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; - ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; - ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; - ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; - ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; - ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; - ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; - ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; - ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; - ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; - ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; - ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; - ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; - ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; - ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; - ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; - ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; - ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; - ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; - ma_snd_pcm_poll_descriptors_proc _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; - ma_snd_pcm_poll_descriptors_count_proc _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; - ma_snd_pcm_poll_descriptors_revents_proc _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; - ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; - - pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; - pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; - pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; - pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; - pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; - pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; - pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; - pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; - pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; - pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; - pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; - pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; - pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; - pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; - pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; - pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; - pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; - pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; - pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; - pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; - pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; - pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; - pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; - pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; - pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; -#endif - - pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; - - result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__alsa; - pCallbacks->onContextUninit = ma_context_uninit__alsa; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; - pCallbacks->onDeviceInit = ma_device_init__alsa; - pCallbacks->onDeviceUninit = ma_device_uninit__alsa; - pCallbacks->onDeviceStart = ma_device_start__alsa; - pCallbacks->onDeviceStop = ma_device_stop__alsa; - pCallbacks->onDeviceRead = ma_device_read__alsa; - pCallbacks->onDeviceWrite = ma_device_write__alsa; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_alsa = NULL; #endif /* MA_HAS_ALSA */ @@ -30536,8 +31008,8 @@ that point (it may still need to load files or whatnot). Instead, this callback stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been -started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data -callback is not fired. +started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, +the main data callback is not fired. This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device @@ -31084,76 +31556,168 @@ typedef void (* ma_pa_free_cb_t) (void* p); #endif -typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); -typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); -typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); -typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); -typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); -typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); -typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); -typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); -typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); -typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); -typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); -typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); -typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); -typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); -typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); -typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); +typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc )(void); +typedef void (* ma_pa_mainloop_free_proc )(ma_pa_mainloop* m); +typedef void (* ma_pa_mainloop_quit_proc )(ma_pa_mainloop* m, int retval); +typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc )(ma_pa_mainloop* m); +typedef int (* ma_pa_mainloop_iterate_proc )(ma_pa_mainloop* m, int block, int* retval); +typedef void (* ma_pa_mainloop_wakeup_proc )(ma_pa_mainloop* m); +typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc )(void); +typedef void (* ma_pa_threaded_mainloop_free_proc )(ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_start_proc )(ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_stop_proc )(ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_lock_proc )(ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_unlock_proc )(ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_wait_proc )(ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_signal_proc )(ma_pa_threaded_mainloop* m, int wait_for_accept); +typedef void (* ma_pa_threaded_mainloop_accept_proc )(ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_get_retval_proc )(ma_pa_threaded_mainloop* m); +typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc )(ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_in_thread_proc )(ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_set_name_proc )(ma_pa_threaded_mainloop* m, const char* name); +typedef ma_pa_context* (* ma_pa_context_new_proc )(ma_pa_mainloop_api* mainloop, const char* name); +typedef void (* ma_pa_context_unref_proc )(ma_pa_context* c); +typedef int (* ma_pa_context_connect_proc )(ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); +typedef void (* ma_pa_context_disconnect_proc )(ma_pa_context* c); +typedef void (* ma_pa_context_set_state_callback_proc )(ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); +typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc )(ma_pa_context* c); +typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc )(ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc )(ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc )(ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); -typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); -typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); -typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); -typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); -typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); -typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); -typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); -typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); -typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); -typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); -typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); -typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); -typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); -typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); -typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); -typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); -typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); -typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); -typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); +typedef void (* ma_pa_operation_unref_proc )(ma_pa_operation* o); +typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc )(ma_pa_operation* o); +typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc )(ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); +typedef int (* ma_pa_channel_map_valid_proc )(const ma_pa_channel_map* m); +typedef int (* ma_pa_channel_map_compatible_proc )(const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); +typedef ma_pa_stream* (* ma_pa_stream_new_proc )(ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); +typedef void (* ma_pa_stream_unref_proc )(ma_pa_stream* s); +typedef int (* ma_pa_stream_connect_playback_proc )(ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); +typedef int (* ma_pa_stream_connect_record_proc )(ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); +typedef int (* ma_pa_stream_disconnect_proc )(ma_pa_stream* s); +typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc )(ma_pa_stream* s); +typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc )(ma_pa_stream* s); +typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc )(ma_pa_stream* s); +typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc )(ma_pa_stream* s); +typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc )(ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); +typedef const char* (* ma_pa_stream_get_device_name_proc )(ma_pa_stream* s); +typedef void (* ma_pa_stream_set_write_callback_proc )(ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_read_callback_proc )(ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_suspended_callback_proc )(ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_moved_callback_proc )(ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); +typedef int (* ma_pa_stream_is_suspended_proc )(const ma_pa_stream* s); +typedef ma_pa_operation* (* ma_pa_stream_flush_proc )(ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_stream_drain_proc )(ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); +typedef int (* ma_pa_stream_is_corked_proc )(ma_pa_stream* s); +typedef ma_pa_operation* (* ma_pa_stream_cork_proc )(ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_stream_trigger_proc )(ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); +typedef int (* ma_pa_stream_begin_write_proc )(ma_pa_stream* s, void** data, size_t* nbytes); +typedef int (* ma_pa_stream_write_proc )(ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); +typedef int (* ma_pa_stream_peek_proc )(ma_pa_stream* s, const void** data, size_t* nbytes); +typedef int (* ma_pa_stream_drop_proc )(ma_pa_stream* s); +typedef size_t (* ma_pa_stream_writable_size_proc )(ma_pa_stream* s); +typedef size_t (* ma_pa_stream_readable_size_proc )(ma_pa_stream* s); + + +typedef struct ma_context_state_pulseaudio +{ + ma_handle pulseSO; + ma_pa_mainloop_new_proc pa_mainloop_new; + ma_pa_mainloop_free_proc pa_mainloop_free; + ma_pa_mainloop_quit_proc pa_mainloop_quit; + ma_pa_mainloop_get_api_proc pa_mainloop_get_api; + ma_pa_mainloop_iterate_proc pa_mainloop_iterate; + ma_pa_mainloop_wakeup_proc pa_mainloop_wakeup; + ma_pa_threaded_mainloop_new_proc pa_threaded_mainloop_new; + ma_pa_threaded_mainloop_free_proc pa_threaded_mainloop_free; + ma_pa_threaded_mainloop_start_proc pa_threaded_mainloop_start; + ma_pa_threaded_mainloop_stop_proc pa_threaded_mainloop_stop; + ma_pa_threaded_mainloop_lock_proc pa_threaded_mainloop_lock; + ma_pa_threaded_mainloop_unlock_proc pa_threaded_mainloop_unlock; + ma_pa_threaded_mainloop_wait_proc pa_threaded_mainloop_wait; + ma_pa_threaded_mainloop_signal_proc pa_threaded_mainloop_signal; + ma_pa_threaded_mainloop_accept_proc pa_threaded_mainloop_accept; + ma_pa_threaded_mainloop_get_retval_proc pa_threaded_mainloop_get_retval; + ma_pa_threaded_mainloop_get_api_proc pa_threaded_mainloop_get_api; + ma_pa_threaded_mainloop_in_thread_proc pa_threaded_mainloop_in_thread; + ma_pa_threaded_mainloop_set_name_proc pa_threaded_mainloop_set_name; + ma_pa_context_new_proc pa_context_new; + ma_pa_context_unref_proc pa_context_unref; + ma_pa_context_connect_proc pa_context_connect; + ma_pa_context_disconnect_proc pa_context_disconnect; + ma_pa_context_set_state_callback_proc pa_context_set_state_callback; + ma_pa_context_get_state_proc pa_context_get_state; + ma_pa_context_get_sink_info_list_proc pa_context_get_sink_info_list; + ma_pa_context_get_source_info_list_proc pa_context_get_source_info_list; + ma_pa_context_get_sink_info_by_name_proc pa_context_get_sink_info_by_name; + ma_pa_context_get_source_info_by_name_proc pa_context_get_source_info_by_name; + ma_pa_operation_unref_proc pa_operation_unref; + ma_pa_operation_get_state_proc pa_operation_get_state; + ma_pa_channel_map_init_extend_proc pa_channel_map_init_extend; + ma_pa_channel_map_valid_proc pa_channel_map_valid; + ma_pa_channel_map_compatible_proc pa_channel_map_compatible; + ma_pa_stream_new_proc pa_stream_new; + ma_pa_stream_unref_proc pa_stream_unref; + ma_pa_stream_connect_playback_proc pa_stream_connect_playback; + ma_pa_stream_connect_record_proc pa_stream_connect_record; + ma_pa_stream_disconnect_proc pa_stream_disconnect; + ma_pa_stream_get_state_proc pa_stream_get_state; + ma_pa_stream_get_sample_spec_proc pa_stream_get_sample_spec; + ma_pa_stream_get_channel_map_proc pa_stream_get_channel_map; + ma_pa_stream_get_buffer_attr_proc pa_stream_get_buffer_attr; + ma_pa_stream_set_buffer_attr_proc pa_stream_set_buffer_attr; + ma_pa_stream_get_device_name_proc pa_stream_get_device_name; + ma_pa_stream_set_write_callback_proc pa_stream_set_write_callback; + ma_pa_stream_set_read_callback_proc pa_stream_set_read_callback; + ma_pa_stream_set_suspended_callback_proc pa_stream_set_suspended_callback; + ma_pa_stream_set_moved_callback_proc pa_stream_set_moved_callback; + ma_pa_stream_is_suspended_proc pa_stream_is_suspended; + ma_pa_stream_flush_proc pa_stream_flush; + ma_pa_stream_drain_proc pa_stream_drain; + ma_pa_stream_is_corked_proc pa_stream_is_corked; + ma_pa_stream_cork_proc pa_stream_cork; + ma_pa_stream_trigger_proc pa_stream_trigger; + ma_pa_stream_begin_write_proc pa_stream_begin_write; + ma_pa_stream_write_proc pa_stream_write; + ma_pa_stream_peek_proc pa_stream_peek; + ma_pa_stream_drop_proc pa_stream_drop; + ma_pa_stream_writable_size_proc pa_stream_writable_size; + ma_pa_stream_readable_size_proc pa_stream_readable_size; + + ma_pa_mainloop* pMainLoop; + ma_pa_context* pPulseContext; + char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ + char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ +} ma_context_state_pulseaudio; + +typedef struct ma_device_state_pulseaudio +{ + ma_pa_mainloop* pMainLoop; + ma_pa_context* pPulseContext; + struct + { + ma_pa_stream* pStream; + ma_format format; + ma_uint32 channels; + } capture; + struct + { + ma_pa_stream* pStream; + ma_format format; + ma_uint32 channels; + } playback; +} ma_device_state_pulseaudio; + typedef struct { ma_uint32 count; ma_uint32 capacity; ma_device_info* pInfo; -} ma_pulse_device_enum_data; +} ma_pulseaudio_device_enum_data; -static ma_result ma_result_from_pulse(int result) + +static ma_result ma_result_from_pulseaudio(int result) { if (result < 0) { return MA_ERROR; @@ -31169,7 +31733,7 @@ static ma_result ma_result_from_pulse(int result) } #if 0 -static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) +static ma_pa_sample_format_t ma_format_to_pulseaudio(ma_format format) { if (ma_is_little_endian()) { switch (format) { @@ -31197,7 +31761,7 @@ static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) } #endif -static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) +static ma_format ma_format_from_pulseaudio(ma_pa_sample_format_t format) { if (ma_is_little_endian()) { switch (format) { @@ -31224,7 +31788,7 @@ static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) } } -static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) +static ma_channel ma_channel_position_from_pulseaudio(ma_pa_channel_position_t position) { switch (position) { @@ -31285,7 +31849,7 @@ static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t positi } #if 0 -static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) +static ma_pa_channel_position_t ma_channel_position_to_pulseaudio(ma_channel position) { switch (position) { @@ -31327,30 +31891,30 @@ static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position } #endif -static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) +static ma_result ma_wait_for_operation__pulseaudio(ma_context_state_pulseaudio* pContextStatePulseAudio, ma_pa_mainloop* pMainLoop, ma_pa_operation* pOP) { int resultPA; ma_pa_operation_state_t state; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pContextStatePulseAudio != NULL); MA_ASSERT(pOP != NULL); for (;;) { - state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); + state = pContextStatePulseAudio->pa_operation_get_state(pOP); if (state != MA_PA_OPERATION_RUNNING) { break; /* Done. */ } - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); + resultPA = pContextStatePulseAudio->pa_mainloop_iterate(pMainLoop, 1, NULL); if (resultPA < 0) { - return ma_result_from_pulse(resultPA); + return ma_result_from_pulseaudio(resultPA); } } return MA_SUCCESS; } -static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) +static ma_result ma_wait_for_operation_and_unref__pulseaudio(ma_context_state_pulseaudio* pContextStatePulseAudio, ma_pa_mainloop* pMainLoop, ma_pa_operation* pOP) { ma_result result; @@ -31358,19 +31922,19 @@ static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma return MA_INVALID_ARGS; } - result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + result = ma_wait_for_operation__pulseaudio(pContextStatePulseAudio, pMainLoop, pOP); + pContextStatePulseAudio->pa_operation_unref(pOP); return result; } -static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) +static ma_result ma_wait_for_pa_context_to_connect__pulseaudio(ma_context* pContext, ma_context_state_pulseaudio* pContextStatePulseAudio, ma_pa_mainloop* pMainLoop, ma_pa_context* pPulseContext) { int resultPA; ma_pa_context_state_t state; for (;;) { - state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); + state = pContextStatePulseAudio->pa_context_get_state(pPulseContext); if (state == MA_PA_CONTEXT_READY) { break; /* Done. */ } @@ -31380,9 +31944,9 @@ static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, return MA_ERROR; } - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); + resultPA = pContextStatePulseAudio->pa_mainloop_iterate(pMainLoop, 1, NULL); if (resultPA < 0) { - return ma_result_from_pulse(resultPA); + return ma_result_from_pulseaudio(resultPA); } } @@ -31390,13 +31954,13 @@ static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, return MA_SUCCESS; } -static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) +static ma_result ma_wait_for_pa_stream_to_connect__pulseaudio(ma_context* pContext, ma_context_state_pulseaudio* pContextStatePulseAudio, ma_pa_mainloop* pMainLoop, ma_pa_stream* pStream) { int resultPA; ma_pa_stream_state_t state; for (;;) { - state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); + state = pContextStatePulseAudio->pa_stream_get_state(pStream); if (state == MA_PA_STREAM_READY) { break; /* Done. */ } @@ -31406,9 +31970,9 @@ static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, m return MA_ERROR; } - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); + resultPA = pContextStatePulseAudio->pa_mainloop_iterate(pMainLoop, 1, NULL); if (resultPA < 0) { - return ma_result_from_pulse(resultPA); + return ma_result_from_pulseaudio(resultPA); } } @@ -31416,42 +31980,42 @@ static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, m } -static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) +static ma_result ma_init_pa_mainloop_and_pa_context__pulseaudio(ma_context* pContext, ma_context_state_pulseaudio* pContextStatePulseAudio, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_pa_mainloop** ppMainLoop, ma_pa_context** ppPulseContext) { ma_result result; - ma_ptr pMainLoop; - ma_ptr pPulseContext; + ma_pa_mainloop* pMainLoop; + ma_pa_context* pPulseContext; MA_ASSERT(ppMainLoop != NULL); MA_ASSERT(ppPulseContext != NULL); /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ - pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); + pMainLoop = pContextStatePulseAudio->pa_mainloop_new(); if (pMainLoop == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); return MA_FAILED_TO_INIT_BACKEND; } - pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); + pPulseContext = pContextStatePulseAudio->pa_context_new(pContextStatePulseAudio->pa_mainloop_get_api(pMainLoop), pApplicationName); if (pPulseContext == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + pContextStatePulseAudio->pa_mainloop_free(pMainLoop); return MA_FAILED_TO_INIT_BACKEND; } /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ - result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); + result = ma_result_from_pulseaudio(pContextStatePulseAudio->pa_context_connect(pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + pContextStatePulseAudio->pa_mainloop_free(pMainLoop); return result; } /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ - result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); + result = ma_wait_for_pa_context_to_connect__pulseaudio(pContext, pContextStatePulseAudio, pMainLoop, pPulseContext); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + pContextStatePulseAudio->pa_mainloop_free(pMainLoop); return result; } @@ -31511,36 +32075,36 @@ static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const m } -static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) +static ma_result ma_context_get_sink_info__pulseaudio(ma_context_state_pulseaudio* pContextStatePulseAudio, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) { ma_pa_operation* pOP; - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); + pOP = pContextStatePulseAudio->pa_context_get_sink_info_by_name(pContextStatePulseAudio->pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); if (pOP == NULL) { return MA_ERROR; } - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); + return ma_wait_for_operation_and_unref__pulseaudio(pContextStatePulseAudio, pContextStatePulseAudio->pMainLoop, pOP); } -static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) +static ma_result ma_context_get_source_info__pulseaudio(ma_context_state_pulseaudio* pContextStatePulseAudio, const char* pDeviceName, ma_pa_source_info* pSourceInfo) { ma_pa_operation* pOP; - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); + pOP = pContextStatePulseAudio->pa_context_get_source_info_by_name(pContextStatePulseAudio->pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); if (pOP == NULL) { return MA_ERROR; } - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); + return ma_wait_for_operation_and_unref__pulseaudio(pContextStatePulseAudio, pContextStatePulseAudio->pMainLoop, pOP); } -static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) +static ma_result ma_context_get_default_device_index__pulseaudio(ma_context_state_pulseaudio* pContextStatePulseAudio, ma_device_type deviceType, ma_uint32* pIndex) { ma_result result; - MA_ASSERT(pContext != NULL); - MA_ASSERT(pIndex != NULL); + MA_ASSERT(pContextStatePulseAudio != NULL); + MA_ASSERT(pIndex != NULL); if (pIndex != NULL) { *pIndex = (ma_uint32)-1; @@ -31548,7 +32112,7 @@ static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext if (deviceType == ma_device_type_playback) { ma_pa_sink_info sinkInfo; - result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); + result = ma_context_get_sink_info__pulseaudio(pContextStatePulseAudio, NULL, &sinkInfo); if (result != MA_SUCCESS) { return result; } @@ -31560,7 +32124,7 @@ static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext if (deviceType == ma_device_type_capture) { ma_pa_source_info sourceInfo; - result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); + result = ma_context_get_source_info__pulseaudio(pContextStatePulseAudio, NULL, &sourceInfo); if (result != MA_SUCCESS) { return result; } @@ -31574,19 +32138,324 @@ static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext } +static ma_context_state_pulseaudio* ma_context_get_backend_state__pulseaudio(ma_context* pContext) +{ + return (ma_context_state_pulseaudio*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_pulseaudio* ma_device_get_backend_state__pulseaudio(ma_device* pDevice) +{ + return (ma_device_state_pulseaudio*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__pulseaudio(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "PulseAudio"; +} + +static ma_result ma_context_init__pulseaudio(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_pulseaudio* pContextStatePulseAudio; + const ma_context_config_pulseaudio* pContextConfigPulseAudio = (const ma_context_config_pulseaudio*)pContextBackendConfig; + ma_context_config_pulseaudio defaultConfigPulseAudio; + ma_log* pLog = ma_context_get_log(pContext); + ma_result result; + ma_bool32 tryAutoSpawn = MA_FALSE; + + if (pContextConfigPulseAudio == NULL) { + defaultConfigPulseAudio = ma_context_config_pulseaudio_init(); + pContextConfigPulseAudio = &defaultConfigPulseAudio; + } + + pContextStatePulseAudio = (ma_context_state_pulseaudio*)ma_calloc(sizeof(*pContextStatePulseAudio), ma_context_get_allocation_callbacks(pContext)); + if (pContextStatePulseAudio == NULL) { + return MA_OUT_OF_MEMORY; + } + + #ifndef MA_NO_RUNTIME_LINKING + { + const char* libpulseNames[] = { + "libpulse.so", + "libpulse.so.0" + }; + size_t i; + + + for (i = 0; i < ma_countof(libpulseNames); i += 1) { + pContextStatePulseAudio->pulseSO = ma_dlopen(pLog, libpulseNames[i]); + if (pContextStatePulseAudio->pulseSO != NULL) { + break; + } + } + + if (pContextStatePulseAudio->pulseSO == NULL) { + return MA_NO_BACKEND; + } + + pContextStatePulseAudio->pa_mainloop_new = (ma_pa_mainloop_new_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_mainloop_new"); + pContextStatePulseAudio->pa_mainloop_free = (ma_pa_mainloop_free_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_mainloop_free"); + pContextStatePulseAudio->pa_mainloop_quit = (ma_pa_mainloop_quit_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_mainloop_quit"); + pContextStatePulseAudio->pa_mainloop_get_api = (ma_pa_mainloop_get_api_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_mainloop_get_api"); + pContextStatePulseAudio->pa_mainloop_iterate = (ma_pa_mainloop_iterate_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_mainloop_iterate"); + pContextStatePulseAudio->pa_mainloop_wakeup = (ma_pa_mainloop_wakeup_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_mainloop_wakeup"); + pContextStatePulseAudio->pa_threaded_mainloop_new = (ma_pa_threaded_mainloop_new_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_new"); + pContextStatePulseAudio->pa_threaded_mainloop_free = (ma_pa_threaded_mainloop_free_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_free"); + pContextStatePulseAudio->pa_threaded_mainloop_start = (ma_pa_threaded_mainloop_start_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_start"); + pContextStatePulseAudio->pa_threaded_mainloop_stop = (ma_pa_threaded_mainloop_stop_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_stop"); + pContextStatePulseAudio->pa_threaded_mainloop_lock = (ma_pa_threaded_mainloop_lock_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_lock"); + pContextStatePulseAudio->pa_threaded_mainloop_unlock = (ma_pa_threaded_mainloop_unlock_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_unlock"); + pContextStatePulseAudio->pa_threaded_mainloop_wait = (ma_pa_threaded_mainloop_wait_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_wait"); + pContextStatePulseAudio->pa_threaded_mainloop_signal = (ma_pa_threaded_mainloop_signal_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_signal"); + pContextStatePulseAudio->pa_threaded_mainloop_accept = (ma_pa_threaded_mainloop_accept_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_accept"); + pContextStatePulseAudio->pa_threaded_mainloop_get_retval = (ma_pa_threaded_mainloop_get_retval_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_get_retval"); + pContextStatePulseAudio->pa_threaded_mainloop_get_api = (ma_pa_threaded_mainloop_get_api_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_get_api"); + pContextStatePulseAudio->pa_threaded_mainloop_in_thread = (ma_pa_threaded_mainloop_in_thread_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_in_thread"); + pContextStatePulseAudio->pa_threaded_mainloop_set_name = (ma_pa_threaded_mainloop_set_name_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_threaded_mainloop_set_name"); + pContextStatePulseAudio->pa_context_new = (ma_pa_context_new_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_new"); + pContextStatePulseAudio->pa_context_unref = (ma_pa_context_unref_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_unref"); + pContextStatePulseAudio->pa_context_connect = (ma_pa_context_connect_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_connect"); + pContextStatePulseAudio->pa_context_disconnect = (ma_pa_context_disconnect_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_disconnect"); + pContextStatePulseAudio->pa_context_set_state_callback = (ma_pa_context_set_state_callback_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_set_state_callback"); + pContextStatePulseAudio->pa_context_get_state = (ma_pa_context_get_state_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_get_state"); + pContextStatePulseAudio->pa_context_get_sink_info_list = (ma_pa_context_get_sink_info_list_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_get_sink_info_list"); + pContextStatePulseAudio->pa_context_get_source_info_list = (ma_pa_context_get_source_info_list_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_get_source_info_list"); + pContextStatePulseAudio->pa_context_get_sink_info_by_name = (ma_pa_context_get_sink_info_by_name_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_get_sink_info_by_name"); + pContextStatePulseAudio->pa_context_get_source_info_by_name = (ma_pa_context_get_source_info_by_name_proc)ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_context_get_source_info_by_name"); + pContextStatePulseAudio->pa_operation_unref = (ma_pa_operation_unref_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_operation_unref"); + pContextStatePulseAudio->pa_operation_get_state = (ma_pa_operation_get_state_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_operation_get_state"); + pContextStatePulseAudio->pa_channel_map_init_extend = (ma_pa_channel_map_init_extend_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_channel_map_init_extend"); + pContextStatePulseAudio->pa_channel_map_valid = (ma_pa_channel_map_valid_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_channel_map_valid"); + pContextStatePulseAudio->pa_channel_map_compatible = (ma_pa_channel_map_compatible_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_channel_map_compatible"); + pContextStatePulseAudio->pa_stream_new = (ma_pa_stream_new_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_new"); + pContextStatePulseAudio->pa_stream_unref = (ma_pa_stream_unref_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_unref"); + pContextStatePulseAudio->pa_stream_connect_playback = (ma_pa_stream_connect_playback_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_connect_playback"); + pContextStatePulseAudio->pa_stream_connect_record = (ma_pa_stream_connect_record_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_connect_record"); + pContextStatePulseAudio->pa_stream_disconnect = (ma_pa_stream_disconnect_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_disconnect"); + pContextStatePulseAudio->pa_stream_get_state = (ma_pa_stream_get_state_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_get_state"); + pContextStatePulseAudio->pa_stream_get_sample_spec = (ma_pa_stream_get_sample_spec_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_get_sample_spec"); + pContextStatePulseAudio->pa_stream_get_channel_map = (ma_pa_stream_get_channel_map_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_get_channel_map"); + pContextStatePulseAudio->pa_stream_get_buffer_attr = (ma_pa_stream_get_buffer_attr_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_get_buffer_attr"); + pContextStatePulseAudio->pa_stream_set_buffer_attr = (ma_pa_stream_set_buffer_attr_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_set_buffer_attr"); + pContextStatePulseAudio->pa_stream_get_device_name = (ma_pa_stream_get_device_name_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_get_device_name"); + pContextStatePulseAudio->pa_stream_set_write_callback = (ma_pa_stream_set_write_callback_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_set_write_callback"); + pContextStatePulseAudio->pa_stream_set_read_callback = (ma_pa_stream_set_read_callback_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_set_read_callback"); + pContextStatePulseAudio->pa_stream_set_suspended_callback = (ma_pa_stream_set_suspended_callback_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_set_suspended_callback"); + pContextStatePulseAudio->pa_stream_set_moved_callback = (ma_pa_stream_set_moved_callback_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_set_moved_callback"); + pContextStatePulseAudio->pa_stream_is_suspended = (ma_pa_stream_is_suspended_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_is_suspended"); + pContextStatePulseAudio->pa_stream_flush = (ma_pa_stream_flush_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_flush"); + pContextStatePulseAudio->pa_stream_drain = (ma_pa_stream_drain_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_drain"); + pContextStatePulseAudio->pa_stream_is_corked = (ma_pa_stream_is_corked_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_is_corked"); + pContextStatePulseAudio->pa_stream_cork = (ma_pa_stream_cork_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_cork"); + pContextStatePulseAudio->pa_stream_trigger = (ma_pa_stream_trigger_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_trigger"); + pContextStatePulseAudio->pa_stream_begin_write = (ma_pa_stream_begin_write_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_begin_write"); + pContextStatePulseAudio->pa_stream_write = (ma_pa_stream_write_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_write"); + pContextStatePulseAudio->pa_stream_peek = (ma_pa_stream_peek_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_peek"); + pContextStatePulseAudio->pa_stream_drop = (ma_pa_stream_drop_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_drop"); + pContextStatePulseAudio->pa_stream_writable_size = (ma_pa_stream_writable_size_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_writable_size"); + pContextStatePulseAudio->pa_stream_readable_size = (ma_pa_stream_readable_size_proc )ma_dlsym(pLog, pContextStatePulseAudio->pulseSO, "pa_stream_readable_size"); + } + #else + { + /* This strange assignment system is just for type safety. */ + ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; + ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; + ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; + ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; + ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; + ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; + ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; + ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; + ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; + ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; + ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; + ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; + ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; + ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; + ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; + ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; + ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; + ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; + ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; + ma_pa_context_new_proc _pa_context_new = pa_context_new; + ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; + ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; + ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; + ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; + ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; + ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; + ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; + ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; + ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name = pa_context_get_source_info_by_name; + ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; + ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; + ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; + ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; + ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; + ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; + ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; + ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; + ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; + ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; + ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; + ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; + ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; + ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; + ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; + ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; + ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; + ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; + ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; + ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; + ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; + ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; + ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; + ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; + ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; + ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; + ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; + ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; + ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; + ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; + ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; + ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; + + pContextStatePulseAudio->pa_mainloop_new = _pa_mainloop_new; + pContextStatePulseAudio->pa_mainloop_free = _pa_mainloop_free; + pContextStatePulseAudio->pa_mainloop_quit = _pa_mainloop_quit; + pContextStatePulseAudio->pa_mainloop_get_api = _pa_mainloop_get_api; + pContextStatePulseAudio->pa_mainloop_iterate = _pa_mainloop_iterate; + pContextStatePulseAudio->pa_mainloop_wakeup = _pa_mainloop_wakeup; + pContextStatePulseAudio->pa_threaded_mainloop_new = _pa_threaded_mainloop_new; + pContextStatePulseAudio->pa_threaded_mainloop_free = _pa_threaded_mainloop_free; + pContextStatePulseAudio->pa_threaded_mainloop_start = _pa_threaded_mainloop_start; + pContextStatePulseAudio->pa_threaded_mainloop_stop = _pa_threaded_mainloop_stop; + pContextStatePulseAudio->pa_threaded_mainloop_lock = _pa_threaded_mainloop_lock; + pContextStatePulseAudio->pa_threaded_mainloop_unlock = _pa_threaded_mainloop_unlock; + pContextStatePulseAudio->pa_threaded_mainloop_wait = _pa_threaded_mainloop_wait; + pContextStatePulseAudio->pa_threaded_mainloop_signal = _pa_threaded_mainloop_signal; + pContextStatePulseAudio->pa_threaded_mainloop_accept = _pa_threaded_mainloop_accept; + pContextStatePulseAudio->pa_threaded_mainloop_get_retval = _pa_threaded_mainloop_get_retval; + pContextStatePulseAudio->pa_threaded_mainloop_get_api = _pa_threaded_mainloop_get_api; + pContextStatePulseAudio->pa_threaded_mainloop_in_thread = _pa_threaded_mainloop_in_thread; + pContextStatePulseAudio->pa_threaded_mainloop_set_name = _pa_threaded_mainloop_set_name; + pContextStatePulseAudio->pa_context_new = _pa_context_new; + pContextStatePulseAudio->pa_context_unref = _pa_context_unref; + pContextStatePulseAudio->pa_context_connect = _pa_context_connect; + pContextStatePulseAudio->pa_context_disconnect = _pa_context_disconnect; + pContextStatePulseAudio->pa_context_set_state_callback = _pa_context_set_state_callback; + pContextStatePulseAudio->pa_context_get_state = _pa_context_get_state; + pContextStatePulseAudio->pa_context_get_sink_info_list = _pa_context_get_sink_info_list; + pContextStatePulseAudio->pa_context_get_source_info_list = _pa_context_get_source_info_list; + pContextStatePulseAudio->pa_context_get_sink_info_by_name = _pa_context_get_sink_info_by_name; + pContextStatePulseAudio->pa_context_get_source_info_by_name = _pa_context_get_source_info_by_name; + pContextStatePulseAudio->pa_operation_unref = _pa_operation_unref; + pContextStatePulseAudio->pa_operation_get_state = _pa_operation_get_state; + pContextStatePulseAudio->pa_channel_map_init_extend = _pa_channel_map_init_extend; + pContextStatePulseAudio->pa_channel_map_valid = _pa_channel_map_valid; + pContextStatePulseAudio->pa_channel_map_compatible = _pa_channel_map_compatible; + pContextStatePulseAudio->pa_stream_new = _pa_stream_new; + pContextStatePulseAudio->pa_stream_unref = _pa_stream_unref; + pContextStatePulseAudio->pa_stream_connect_playback = _pa_stream_connect_playback; + pContextStatePulseAudio->pa_stream_connect_record = _pa_stream_connect_record; + pContextStatePulseAudio->pa_stream_disconnect = _pa_stream_disconnect; + pContextStatePulseAudio->pa_stream_get_state = _pa_stream_get_state; + pContextStatePulseAudio->pa_stream_get_sample_spec = _pa_stream_get_sample_spec; + pContextStatePulseAudio->pa_stream_get_channel_map = _pa_stream_get_channel_map; + pContextStatePulseAudio->pa_stream_get_buffer_attr = _pa_stream_get_buffer_attr; + pContextStatePulseAudio->pa_stream_set_buffer_attr = _pa_stream_set_buffer_attr; + pContextStatePulseAudio->pa_stream_get_device_name = _pa_stream_get_device_name; + pContextStatePulseAudio->pa_stream_set_write_callback = _pa_stream_set_write_callback; + pContextStatePulseAudio->pa_stream_set_read_callback = _pa_stream_set_read_callback; + pContextStatePulseAudio->pa_stream_set_suspended_callback = _pa_stream_set_suspended_callback; + pContextStatePulseAudio->pa_stream_set_moved_callback = _pa_stream_set_moved_callback; + pContextStatePulseAudio->pa_stream_is_suspended = _pa_stream_is_suspended; + pContextStatePulseAudio->pa_stream_flush = _pa_stream_flush; + pContextStatePulseAudio->pa_stream_drain = _pa_stream_drain; + pContextStatePulseAudio->pa_stream_is_corked = _pa_stream_is_corked; + pContextStatePulseAudio->pa_stream_cork = _pa_stream_cork; + pContextStatePulseAudio->pa_stream_trigger = _pa_stream_trigger; + pContextStatePulseAudio->pa_stream_begin_write = _pa_stream_begin_write; + pContextStatePulseAudio->pa_stream_write = _pa_stream_write; + pContextStatePulseAudio->pa_stream_peek = _pa_stream_peek; + pContextStatePulseAudio->pa_stream_drop = _pa_stream_drop; + pContextStatePulseAudio->pa_stream_writable_size = _pa_stream_writable_size; + pContextStatePulseAudio->pa_stream_readable_size = _pa_stream_readable_size; + } + #endif + + /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ + if (pContextConfigPulseAudio->pApplicationName != NULL) { + pContextStatePulseAudio->pApplicationName = ma_copy_string(pContextConfigPulseAudio->pApplicationName, ma_context_get_allocation_callbacks(pContext)); + if (pContextStatePulseAudio->pApplicationName == NULL) { + return MA_OUT_OF_MEMORY; + } + } + + if (pContextConfigPulseAudio->pServerName != NULL) { + pContextStatePulseAudio->pServerName = ma_copy_string(pContextConfigPulseAudio->pServerName, ma_context_get_allocation_callbacks(pContext)); + if (pContextStatePulseAudio->pServerName == NULL) { + ma_free(pContextStatePulseAudio->pApplicationName, ma_context_get_allocation_callbacks(pContext)); + return MA_OUT_OF_MEMORY; + } + } + + tryAutoSpawn = pContextConfigPulseAudio->tryAutoSpawn; + + + result = ma_init_pa_mainloop_and_pa_context__pulseaudio(pContext, pContextStatePulseAudio, pContextStatePulseAudio->pApplicationName, pContextStatePulseAudio->pServerName, tryAutoSpawn, &pContextStatePulseAudio->pMainLoop, &pContextStatePulseAudio->pPulseContext); + if (result != MA_SUCCESS) { + ma_free(pContextStatePulseAudio->pServerName, ma_context_get_allocation_callbacks(pContext)); + ma_free(pContextStatePulseAudio->pApplicationName, ma_context_get_allocation_callbacks(pContext)); + #ifndef MA_NO_RUNTIME_LINKING + { + ma_dlclose(pLog, pContextStatePulseAudio->pulseSO); + } + #endif + + return result; + } + + *ppContextState = pContextStatePulseAudio; + + return MA_SUCCESS; +} + +static void ma_context_uninit__pulseaudio(ma_context* pContext) +{ + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(pContext); + + MA_ASSERT(pContextStatePulseAudio != NULL); + + pContextStatePulseAudio->pa_context_disconnect(pContextStatePulseAudio->pPulseContext); + pContextStatePulseAudio->pa_context_unref(pContextStatePulseAudio->pPulseContext); + pContextStatePulseAudio->pa_mainloop_free(pContextStatePulseAudio->pMainLoop); + + ma_free(pContextStatePulseAudio->pServerName, ma_context_get_allocation_callbacks(pContext)); + ma_free(pContextStatePulseAudio->pApplicationName, ma_context_get_allocation_callbacks(pContext)); + + #ifndef MA_NO_RUNTIME_LINKING + { + ma_dlclose(ma_context_get_log(pContext), pContextStatePulseAudio->pulseSO); + } + #endif +} + + typedef struct { - ma_context* pContext; + ma_context_state_pulseaudio* pContextStatePulseAudio; ma_enum_devices_callback_proc callback; void* pUserData; ma_bool32 isTerminated; ma_uint32 defaultDeviceIndexPlayback; ma_uint32 defaultDeviceIndexCapture; -} ma_context_enumerate_devices_callback_data__pulse; +} ma_context_enumerate_devices_callback_data__pulseaudio; -static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) +static void ma_context_enumerate_devices_sink_callback__pulseaudio(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) { - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; + ma_context_enumerate_devices_callback_data__pulseaudio* pData = (ma_context_enumerate_devices_callback_data__pulseaudio*)pUserData; ma_device_info deviceInfo; MA_ASSERT(pData != NULL); @@ -31616,9 +32485,9 @@ static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPu (void)pPulseContext; /* Unused. */ } -static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) +static void ma_context_enumerate_devices_source_callback__pulseaudio(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) { - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; + ma_context_enumerate_devices_callback_data__pulseaudio* pData = (ma_context_enumerate_devices_callback_data__pulseaudio*)pUserData; ma_device_info deviceInfo; MA_ASSERT(pData != NULL); @@ -31648,36 +32517,37 @@ static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* p (void)pPulseContext; /* Unused. */ } -static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +static ma_result ma_context_enumerate_devices__pulseaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(pContext); ma_result result = MA_SUCCESS; - ma_context_enumerate_devices_callback_data__pulse callbackData; + ma_context_enumerate_devices_callback_data__pulseaudio callbackData; ma_pa_operation* pOP = NULL; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pContextStatePulseAudio != NULL); MA_ASSERT(callback != NULL); - callbackData.pContext = pContext; - callbackData.callback = callback; - callbackData.pUserData = pUserData; - callbackData.isTerminated = MA_FALSE; + callbackData.pContextStatePulseAudio = pContextStatePulseAudio; + callbackData.callback = callback; + callbackData.pUserData = pUserData; + callbackData.isTerminated = MA_FALSE; callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; /* We need to get the index of the default devices. */ - ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); - ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); + ma_context_get_default_device_index__pulseaudio(pContextStatePulseAudio, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); + ma_context_get_default_device_index__pulseaudio(pContextStatePulseAudio, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); /* Playback. */ if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); + pOP = pContextStatePulseAudio->pa_context_get_sink_info_list(pContextStatePulseAudio->pPulseContext, ma_context_enumerate_devices_sink_callback__pulseaudio, &callbackData); if (pOP == NULL) { result = MA_ERROR; goto done; } - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + result = ma_wait_for_operation__pulseaudio(pContextStatePulseAudio, pContextStatePulseAudio->pMainLoop, pOP); + pContextStatePulseAudio->pa_operation_unref(pOP); if (result != MA_SUCCESS) { goto done; @@ -31687,14 +32557,14 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en /* Capture. */ if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); + pOP = pContextStatePulseAudio->pa_context_get_source_info_list(pContextStatePulseAudio->pPulseContext, ma_context_enumerate_devices_source_callback__pulseaudio, &callbackData); if (pOP == NULL) { result = MA_ERROR; goto done; } - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + result = ma_wait_for_operation__pulseaudio(pContextStatePulseAudio, pContextStatePulseAudio->pMainLoop, pOP); + pContextStatePulseAudio->pa_operation_unref(pOP); if (result != MA_SUCCESS) { goto done; @@ -31711,11 +32581,11 @@ typedef struct ma_device_info* pDeviceInfo; ma_uint32 defaultDeviceIndex; ma_bool32 foundDevice; -} ma_context_get_device_info_callback_data__pulse; +} ma_context_get_device_info_callback_data__pulseaudio; -static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) +static void ma_context_get_device_info_sink_callback__pulseaudio(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) { - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; + ma_context_get_device_info_callback_data__pulseaudio* pData = (ma_context_get_device_info_callback_data__pulseaudio*)pUserData; if (endOfList > 0) { return; @@ -31737,7 +32607,7 @@ static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPuls all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to report the "native" device format. */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); + pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulseaudio(pInfo->sample_spec.format); pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; pData->pDeviceInfo->nativeDataFormats[0].flags = 0; @@ -31750,9 +32620,9 @@ static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPuls (void)pPulseContext; /* Unused. */ } -static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) +static void ma_context_get_device_info_source_callback__pulseaudio(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) { - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; + ma_context_get_device_info_callback_data__pulseaudio* pData = (ma_context_get_device_info_callback_data__pulseaudio*)pUserData; if (endOfList > 0) { return; @@ -31774,7 +32644,7 @@ static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPu all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to report the "native" device format. */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); + pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulseaudio(pInfo->sample_spec.format); pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; pData->pDeviceInfo->nativeDataFormats[0].flags = 0; @@ -31787,14 +32657,15 @@ static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPu (void)pPulseContext; /* Unused. */ } -static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +static ma_result ma_context_get_device_info__pulseaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(pContext); ma_result result = MA_SUCCESS; - ma_context_get_device_info_callback_data__pulse callbackData; + ma_context_get_device_info_callback_data__pulseaudio callbackData; ma_pa_operation* pOP = NULL; const char* pDeviceName = NULL; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pContextStatePulseAudio != NULL); callbackData.pDeviceInfo = pDeviceInfo; callbackData.foundDevice = MA_FALSE; @@ -31805,16 +32676,16 @@ static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_devi pDeviceName = NULL; } - result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); + result = ma_context_get_default_device_index__pulseaudio(pContextStatePulseAudio, deviceType, &callbackData.defaultDeviceIndex); if (deviceType == ma_device_type_playback) { - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); + pOP = pContextStatePulseAudio->pa_context_get_sink_info_by_name(pContextStatePulseAudio->pPulseContext, pDeviceName, ma_context_get_device_info_sink_callback__pulseaudio, &callbackData); } else { - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); + pOP = pContextStatePulseAudio->pa_context_get_source_info_by_name(pContextStatePulseAudio->pPulseContext, pDeviceName, ma_context_get_device_info_source_callback__pulseaudio, &callbackData); } if (pOP != NULL) { - ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); + ma_wait_for_operation_and_unref__pulseaudio(pContextStatePulseAudio, pContextStatePulseAudio->pMainLoop, pOP); } else { result = MA_ERROR; goto done; @@ -31829,40 +32700,10 @@ done: return result; } -static ma_result ma_device_uninit__pulse(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } - - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) { ma_pa_buffer_attr attr; - attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); + attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulseaudio(ss->format), ss->channels); attr.tlength = attr.maxlength / periods; attr.prebuf = (ma_uint32)-1; attr.minreq = (ma_uint32)-1; @@ -31871,7 +32712,7 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra return attr; } -static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) +static ma_pa_stream* ma_device__pa_stream_new__pulseaudio(ma_context_state_pulseaudio* pContextStatePulseAudio, ma_device_state_pulseaudio* pDeviceStatePulseAudio, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) { static ma_atomic_uint32 g_StreamCounter = { 0 }; char actualStreamName[256]; @@ -31886,41 +32727,48 @@ static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const c } ma_atomic_uint32_fetch_add(&g_StreamCounter, 1); - return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); + return pContextStatePulseAudio->pa_stream_new(pDeviceStatePulseAudio->pPulseContext, actualStreamName, ss, cmap); } -static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) +static void ma_device_on_read__pulseaudio(ma_pa_stream* pStream, size_t byteCount, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_pulseaudio* pDeviceStatePulseAudio = ma_device_get_backend_state__pulseaudio(pDevice); + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(ma_device_get_context(pDevice)); ma_uint32 bpf; - ma_uint32 deviceState; + //ma_device_state deviceStatus; ma_uint64 frameCount; ma_uint64 framesProcessed; - MA_ASSERT(pDevice != NULL); - /* Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio can fire this callback before the stream has even started. Ridiculous. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { + #if 0 + deviceStatus = ma_device_get_state(pDevice); + if (deviceStatus != ma_device_state_starting && deviceStatus != ma_device_state_started) { return; } + #else + /* TODO: Check this new logic here. We were previously uses the device status, but now we're using pa_stream_is_corked(). */ + if (pContextStatePulseAudio->pa_stream_is_corked(pStream) == 1) { + return; + } + #endif - bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + bpf = ma_get_bytes_per_frame(pDeviceStatePulseAudio->capture.format, pDeviceStatePulseAudio->capture.channels); MA_ASSERT(bpf > 0); frameCount = byteCount / bpf; framesProcessed = 0; - while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { + while (framesProcessed < frameCount) { const void* pMappedPCMFrames; size_t bytesMapped; ma_uint64 framesMapped; - int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); + int pulseResult = pContextStatePulseAudio->pa_stream_peek(pStream, &pMappedPCMFrames, &bytesMapped); if (pulseResult < 0) { break; /* Failed to map. Abort. */ } @@ -31931,10 +32779,10 @@ static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, vo ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); } else { /* It's a hole. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulseaudio: Hole.\n"); } - pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); + pulseResult = pContextStatePulseAudio->pa_stream_drop(pStream); if (pulseResult < 0) { break; /* Failed to drop the buffer. */ } @@ -31948,45 +32796,41 @@ static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, vo } } -static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) +static ma_result ma_device_write_to_stream__pulseaudio(ma_device* pDevice, ma_context_state_pulseaudio* pContextStatePulseAudio, ma_device_state_pulseaudio* pDeviceStatePulseAudio, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) { ma_result result = MA_SUCCESS; ma_uint64 framesProcessed = 0; size_t bytesMapped; ma_uint32 bpf; - ma_uint32 deviceState; - MA_ASSERT(pDevice != NULL); MA_ASSERT(pStream != NULL); - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + bpf = ma_get_bytes_per_frame(pDeviceStatePulseAudio->playback.format, pDeviceStatePulseAudio->playback.channels); MA_ASSERT(bpf > 0); - deviceState = ma_device_get_state(pDevice); - - bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); + bytesMapped = pContextStatePulseAudio->pa_stream_writable_size(pStream); if (bytesMapped != (size_t)-1) { if (bytesMapped > 0) { ma_uint64 framesMapped; void* pMappedPCMFrames; - int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); + int pulseResult = pContextStatePulseAudio->pa_stream_begin_write(pStream, &pMappedPCMFrames, &bytesMapped); if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); + result = ma_result_from_pulseaudio(pulseResult); goto done; } framesMapped = bytesMapped / bpf; - if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ + if (pContextStatePulseAudio->pa_stream_is_corked(pStream) == 0) { /* Check for starting state just in case this is being used to do the initial fill. */ ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); } else { /* Device is not started. Write silence. */ - ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); + ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDeviceStatePulseAudio->playback.format, pDeviceStatePulseAudio->playback.channels); } - pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); + pulseResult = pContextStatePulseAudio->pa_stream_write(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); + result = ma_result_from_pulseaudio(pulseResult); goto done; /* Failed to write data to stream. */ } @@ -32008,27 +32852,27 @@ done: return result; } -static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) +static void ma_device_on_write__pulseaudio(ma_pa_stream* pStream, size_t byteCount, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_pulseaudio* pDeviceStatePulseAudio = ma_device_get_backend_state__pulseaudio(pDevice); + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(ma_device_get_context(pDevice)); ma_uint32 bpf; ma_uint64 frameCount; ma_uint64 framesProcessed; - ma_uint32 deviceState; ma_result result; - MA_ASSERT(pDevice != NULL); - /* Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio can fire this callback before the stream has even started. Ridiculous. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { + + /* TODO: Check this new logic here. We were previously uses the device status, but now we're using pa_stream_is_corked(). */ + if (pContextStatePulseAudio->pa_stream_is_corked(pStream) == 1) { return; } - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + bpf = ma_get_bytes_per_frame(pDeviceStatePulseAudio->playback.format, pDeviceStatePulseAudio->playback.channels); MA_ASSERT(bpf > 0); frameCount = byteCount / bpf; @@ -32038,12 +32882,11 @@ static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, v ma_uint64 framesProcessedThisIteration; /* Don't keep trying to process frames if the device isn't started. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - break; + if (pContextStatePulseAudio->pa_stream_is_corked(pStream) == 1) { + return; } - result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); + result = ma_device_write_to_stream__pulseaudio(pDevice, pContextStatePulseAudio, pDeviceStatePulseAudio, pStream, &framesProcessedThisIteration); if (result != MA_SUCCESS) { break; } @@ -32052,14 +32895,15 @@ static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, v } } -static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) +static void ma_device_on_suspended__pulseaudio(ma_pa_stream* pStream, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(ma_device_get_context(pDevice)); int suspended; (void)pStream; - suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); + suspended = pContextStatePulseAudio->pa_stream_is_suspended(pStream); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); if (suspended < 0) { @@ -32068,24 +32912,23 @@ static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData if (suspended == 1) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); - ma_device__on_notification_stopped(pDevice); + ma_device_post_notification_stopped(pDevice); } else { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); - ma_device__on_notification_started(pDevice); + ma_device_post_notification_started(pDevice); } } -static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) +static void ma_device_on_rerouted__pulseaudio(ma_pa_stream* pStream, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; (void)pStream; - (void)pUserData; - ma_device__on_notification_rerouted(pDevice); + ma_device_post_notification_rerouted(pDevice); } -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulseaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { /* There have been reports from users where buffers of < ~20ms result glitches when running through @@ -32111,7 +32954,7 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const } } -static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__pulseaudio(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { /* Notes for PulseAudio: @@ -32123,14 +32966,15 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this flag, capture mode will just not work properly until you open another PulseAudio app. */ - + ma_device_state_pulseaudio* pDeviceStatePulseAudio; + const ma_device_config_pulseaudio* pDeviceConfigPulseAudio = (const ma_device_config_pulseaudio*)pDeviceBackendConfig; + ma_device_config_pulseaudio defaultConfigPulseAudio; + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result = MA_SUCCESS; int error = 0; const char* devPlayback = NULL; const char* devCapture = NULL; - ma_format format = ma_format_unknown; - ma_uint32 channels = 0; - ma_uint32 sampleRate = 0; ma_pa_sink_info sinkInfo; ma_pa_source_info sourceInfo; ma_pa_sample_spec ss; @@ -32142,49 +32986,49 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ma_uint32 iChannel; ma_pa_stream_flags_t streamFlags; - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->pulse); + if (pDeviceConfigPulseAudio == NULL) { + defaultConfigPulseAudio = ma_device_config_pulseaudio_init(); + pDeviceConfigPulseAudio = &defaultConfigPulseAudio; + } - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* No exclusive mode with the PulseAudio backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { + if (((deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + pDeviceStatePulseAudio = (ma_device_state_pulseaudio*)ma_calloc(sizeof(*pDeviceStatePulseAudio), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStatePulseAudio == NULL) { + return MA_OUT_OF_MEMORY; + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { if (pDescriptorPlayback->pDeviceID != NULL) { devPlayback = pDescriptorPlayback->pDeviceID->pulse; } - - format = pDescriptorPlayback->format; - channels = pDescriptorPlayback->channels; - sampleRate = pDescriptorPlayback->sampleRate; } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { if (pDescriptorCapture->pDeviceID != NULL) { devCapture = pDescriptorCapture->pDeviceID->pulse; } - - format = pDescriptorCapture->format; - channels = pDescriptorCapture->channels; - sampleRate = pDescriptorCapture->sampleRate; } - result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); + result = ma_init_pa_mainloop_and_pa_context__pulseaudio(ma_device_get_context(pDevice), pContextStatePulseAudio, pContextStatePulseAudio->pApplicationName, pContextStatePulseAudio->pServerName, MA_FALSE, &pDeviceStatePulseAudio->pMainLoop, &pDeviceStatePulseAudio->pPulseContext); if (result != MA_SUCCESS) { + ma_free(pDeviceStatePulseAudio, ma_device_get_allocation_callbacks(pDevice)); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); return result; } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + result = ma_context_get_source_info__pulseaudio(pContextStatePulseAudio, devCapture, &sourceInfo); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); goto on_error0; @@ -32204,7 +33048,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); + pContextStatePulseAudio->pa_channel_map_init_extend(&cmap, ss.channels, pDeviceConfigPulseAudio->channelMap); /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { @@ -32212,7 +33056,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { + if (ma_format_from_pulseaudio(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; } else { @@ -32233,13 +33077,13 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } /* We now have enough information to calculate our actual period size in frames. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); + pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulseaudio(pDescriptorCapture, ss.rate, ma_performance_profile_low_latency); attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); - if (pDevice->pulse.pStreamCapture == NULL) { + pDeviceStatePulseAudio->capture.pStream = ma_device__pa_stream_new__pulseaudio(pContextStatePulseAudio, pDeviceStatePulseAudio, pDeviceConfigPulseAudio->pStreamNameCapture, &ss, &cmap); + if (pDeviceStatePulseAudio->capture.pStream == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); result = MA_ERROR; goto on_error0; @@ -32247,13 +33091,13 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* The callback needs to be set before connecting the stream. */ - ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); + pContextStatePulseAudio->pa_stream_set_read_callback(pDeviceStatePulseAudio->capture.pStream, ma_device_on_read__pulseaudio, pDevice); /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); + pContextStatePulseAudio->pa_stream_set_suspended_callback(pDeviceStatePulseAudio->capture.pStream, ma_device_on_suspended__pulseaudio, pDevice); /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); + pContextStatePulseAudio->pa_stream_set_moved_callback(pDeviceStatePulseAudio->capture.pStream, ma_device_on_rerouted__pulseaudio, pDevice); /* Connect after we've got all of our internal state set up. */ @@ -32261,29 +33105,29 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi streamFlags |= MA_PA_STREAM_DONT_MOVE; } - error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); + error = pContextStatePulseAudio->pa_stream_connect_record(pDeviceStatePulseAudio->capture.pStream, devCapture, &attr, streamFlags); if (error != MA_PA_OK) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); - result = ma_result_from_pulse(error); + result = ma_result_from_pulseaudio(error); goto on_error1; } - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); + result = ma_wait_for_pa_stream_to_connect__pulseaudio(ma_device_get_context(pDevice), pContextStatePulseAudio, pDeviceStatePulseAudio->pMainLoop, pDeviceStatePulseAudio->capture.pStream); if (result != MA_SUCCESS) { goto on_error2; } /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + pActualSS = pContextStatePulseAudio->pa_stream_get_sample_spec(pDeviceStatePulseAudio->capture.pStream); if (pActualSS != NULL) { ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulseaudio(ss.format)), ss.channels, ss.rate); } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); } - pDescriptorCapture->format = ma_format_from_pulse(ss.format); + pDescriptorCapture->format = ma_format_from_pulseaudio(ss.format); pDescriptorCapture->channels = ss.channels; pDescriptorCapture->sampleRate = ss.rate; @@ -32295,7 +33139,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + pActualChannelMap = pContextStatePulseAudio->pa_stream_get_channel_map(pDeviceStatePulseAudio->capture.pStream); if (pActualChannelMap == NULL) { pActualChannelMap = &cmap; /* Fallback just in case. */ } @@ -32309,7 +33153,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi */ if (pDescriptorCapture->channels > 2) { for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) { - pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); + pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulseaudio(pActualChannelMap->map[iChannel]); } } else { /* Hack for mono and stereo. */ @@ -32325,7 +33169,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + pActualAttr = pContextStatePulseAudio->pa_stream_get_buffer_attr(pDeviceStatePulseAudio->capture.pStream); if (pActualAttr != NULL) { attr = *pActualAttr; } @@ -32338,10 +33182,13 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); + + pDeviceStatePulseAudio->capture.format = pDescriptorCapture->format; + pDeviceStatePulseAudio->capture.channels = pDescriptorCapture->channels; } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + result = ma_context_get_sink_info__pulseaudio(pContextStatePulseAudio, devPlayback, &sinkInfo); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); goto on_error2; @@ -32361,7 +33208,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); + pContextStatePulseAudio->pa_channel_map_init_extend(&cmap, ss.channels, pDeviceConfigPulseAudio->channelMap); /* Use the requested sample rate if one was specified. */ @@ -32370,7 +33217,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { + if (ma_format_from_pulseaudio(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; } else { @@ -32391,14 +33238,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } /* We now have enough information to calculate the actual buffer size in frames. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); + pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulseaudio(pDescriptorPlayback, ss.rate, ma_performance_profile_low_latency); attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); - if (pDevice->pulse.pStreamPlayback == NULL) { + pDeviceStatePulseAudio->playback.pStream = ma_device__pa_stream_new__pulseaudio(pContextStatePulseAudio, pDeviceStatePulseAudio, pDeviceConfigPulseAudio->pStreamNamePlayback, &ss, &cmap); + if (pDeviceStatePulseAudio->playback.pStream == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); result = MA_ERROR; goto on_error2; @@ -32409,13 +33256,13 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a device state of ma_device_state_uninitialized. */ - ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); + pContextStatePulseAudio->pa_stream_set_write_callback(pDeviceStatePulseAudio->playback.pStream, ma_device_on_write__pulseaudio, pDevice); /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); + pContextStatePulseAudio->pa_stream_set_suspended_callback(pDeviceStatePulseAudio->playback.pStream, ma_device_on_suspended__pulseaudio, pDevice); /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); + pContextStatePulseAudio->pa_stream_set_moved_callback(pDeviceStatePulseAudio->playback.pStream, ma_device_on_rerouted__pulseaudio, pDevice); /* Connect after we've got all of our internal state set up. */ @@ -32423,29 +33270,29 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi streamFlags |= MA_PA_STREAM_DONT_MOVE; } - error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); + error = pContextStatePulseAudio->pa_stream_connect_playback(pDeviceStatePulseAudio->playback.pStream, devPlayback, &attr, streamFlags, NULL, NULL); if (error != MA_PA_OK) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); - result = ma_result_from_pulse(error); + result = ma_result_from_pulseaudio(error); goto on_error3; } - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); + result = ma_wait_for_pa_stream_to_connect__pulseaudio(ma_device_get_context(pDevice), pContextStatePulseAudio, pDeviceStatePulseAudio->pMainLoop, pDeviceStatePulseAudio->playback.pStream); if (result != MA_SUCCESS) { goto on_error3; } /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + pActualSS = pContextStatePulseAudio->pa_stream_get_sample_spec(pDeviceStatePulseAudio->playback.pStream); if (pActualSS != NULL) { ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulseaudio(ss.format)), ss.channels, ss.rate); } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); } - pDescriptorPlayback->format = ma_format_from_pulse(ss.format); + pDescriptorPlayback->format = ma_format_from_pulseaudio(ss.format); pDescriptorPlayback->channels = ss.channels; pDescriptorPlayback->sampleRate = ss.rate; @@ -32457,7 +33304,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + pActualChannelMap = pContextStatePulseAudio->pa_stream_get_channel_map(pDeviceStatePulseAudio->playback.pStream); if (pActualChannelMap == NULL) { pActualChannelMap = &cmap; /* Fallback just in case. */ } @@ -32471,7 +33318,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi */ if (pDescriptorPlayback->channels > 2) { for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) { - pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); + pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulseaudio(pActualChannelMap->map[iChannel]); } } else { /* Hack for mono and stereo. */ @@ -32487,7 +33334,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + pActualAttr = pContextStatePulseAudio->pa_stream_get_buffer_attr(pDeviceStatePulseAudio->playback.pStream); if (pActualAttr != NULL) { attr = *pActualAttr; } @@ -32500,52 +33347,62 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); + + pDeviceStatePulseAudio->playback.format = pDescriptorPlayback->format; + pDeviceStatePulseAudio->playback.channels = pDescriptorPlayback->channels; } - - /* - We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main - part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for - us later on because that will only do it if it's a fully asynchronous backend - i.e. the - onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. - */ - if (pConfig->deviceType == ma_device_type_duplex) { - ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; - ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; - ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; - - result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); - goto on_error4; - } - } + *ppDeviceState = pDeviceStatePulseAudio; return MA_SUCCESS; on_error4: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStatePulseAudio->pa_stream_disconnect(pDeviceStatePulseAudio->playback.pStream); } on_error3: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStatePulseAudio->pa_stream_unref(pDeviceStatePulseAudio->playback.pStream); } on_error2: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStatePulseAudio->pa_stream_disconnect(pDeviceStatePulseAudio->capture.pStream); } on_error1: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStatePulseAudio->pa_stream_unref(pDeviceStatePulseAudio->capture.pStream); } on_error0: + ma_free(pDeviceStatePulseAudio, ma_device_get_allocation_callbacks(pDevice)); return result; } +static void ma_device_uninit__pulseaudio(ma_device* pDevice) +{ + ma_device_state_pulseaudio* pDeviceStatePulseAudio = ma_device_get_backend_state__pulseaudio(pDevice); + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); -static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStatePulseAudio->pa_stream_disconnect(pDeviceStatePulseAudio->capture.pStream); + pContextStatePulseAudio->pa_stream_unref(pDeviceStatePulseAudio->capture.pStream); + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStatePulseAudio->pa_stream_disconnect(pDeviceStatePulseAudio->playback.pStream); + pContextStatePulseAudio->pa_stream_unref(pDeviceStatePulseAudio->playback.pStream); + } + + pContextStatePulseAudio->pa_context_disconnect(pDeviceStatePulseAudio->pPulseContext); + pContextStatePulseAudio->pa_context_unref(pDeviceStatePulseAudio->pPulseContext); + pContextStatePulseAudio->pa_mainloop_free(pDeviceStatePulseAudio->pMainLoop); + + ma_free(pDeviceStatePulseAudio, ma_device_get_allocation_callbacks(pDevice)); +} + + +static void ma_pulseaudio_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) { ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; MA_ASSERT(pIsSuccessful != NULL); @@ -32555,9 +33412,8 @@ static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int succ (void)pStream; /* Unused. */ } -static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) +static ma_result ma_device__cork_stream__pulseaudio(ma_device* pDevice, ma_context_state_pulseaudio* pContextStatePulseAudio, ma_device_state_pulseaudio* pDeviceStatePulseAudio, ma_device_type deviceType, int cork) { - ma_context* pContext = pDevice->pContext; ma_bool32 wasSuccessful; ma_pa_stream* pStream; ma_pa_operation* pOP; @@ -32570,16 +33426,16 @@ static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_typ wasSuccessful = MA_FALSE; - pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); + pStream = ((deviceType == ma_device_type_capture) ? pDeviceStatePulseAudio->capture.pStream : pDeviceStatePulseAudio->playback.pStream); MA_ASSERT(pStream != NULL); - pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); + pOP = pContextStatePulseAudio->pa_stream_cork(pStream, cork, ma_pulseaudio_operation_complete_callback, &wasSuccessful); if (pOP == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); return MA_ERROR; } - result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); + result = ma_wait_for_operation_and_unref__pulseaudio(pContextStatePulseAudio, pDeviceStatePulseAudio->pMainLoop, pOP); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); return result; @@ -32593,28 +33449,29 @@ static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_typ return MA_SUCCESS; } -static ma_result ma_device_start__pulse(ma_device* pDevice) +static ma_result ma_device_start__pulseaudio(ma_device* pDevice) { + ma_device_state_pulseaudio* pDeviceStatePulseAudio = ma_device_get_backend_state__pulseaudio(pDevice); + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result; - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + result = ma_device__cork_stream__pulseaudio(pDevice, pContextStatePulseAudio, pDeviceStatePulseAudio, ma_device_type_capture, 0); if (result != MA_SUCCESS) { return result; } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { /* We need to fill some data before uncorking. Not doing this will result in the write callback never getting fired. We're not going to abort if writing fails because I still want the device to get uncorked. */ - ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ + ma_device_write_to_stream__pulseaudio(pDevice, pContextStatePulseAudio, pDeviceStatePulseAudio, pDeviceStatePulseAudio->playback.pStream, NULL); /* No need to check the result here. Always want to fall through to an uncork. */ - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); + result = ma_device__cork_stream__pulseaudio(pDevice, pContextStatePulseAudio, pDeviceStatePulseAudio, ma_device_type_playback, 0); if (result != MA_SUCCESS) { return result; } @@ -32623,32 +33480,33 @@ static ma_result ma_device_start__pulse(ma_device* pDevice) return MA_SUCCESS; } -static ma_result ma_device_stop__pulse(ma_device* pDevice) +static ma_result ma_device_stop__pulseaudio(ma_device* pDevice) { + ma_device_state_pulseaudio* pDeviceStatePulseAudio = ma_device_get_backend_state__pulseaudio(pDevice); + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result; - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + result = ma_device__cork_stream__pulseaudio(pDevice, pContextStatePulseAudio, pDeviceStatePulseAudio, ma_device_type_capture, 1); if (result != MA_SUCCESS) { return result; } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { /* Ideally we would drain the device here, but there's been cases where PulseAudio seems to be broken on some systems to the point where no audio processing seems to happen. When this happens, draining never completes and we get stuck here. For now I'm disabling draining of the device so we don't just freeze the application. */ - #if 0 - ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - #endif + #if 0 + ma_pa_operation* pOP = pContextStatePulseAudio->pa_stream_drain(pDeviceStatePulseAudio->pStreamPlayback, ma_pulseaudio_operation_complete_callback, &wasSuccessful); + ma_wait_for_operation_and_unref__pulseaudio(pContextStatePulseAudio, pDeviceStatePulseAudio->pMainLoop, pOP); + #endif - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); + result = ma_device__cork_stream__pulseaudio(pDevice, pContextStatePulseAudio, pDeviceStatePulseAudio, ma_device_type_playback, 1); if (result != MA_SUCCESS) { return result; } @@ -32657,12 +33515,12 @@ static ma_result ma_device_stop__pulse(ma_device* pDevice) return MA_SUCCESS; } -static ma_result ma_device_data_loop__pulse(ma_device* pDevice) +static void ma_device_loop__pulseaudio(ma_device* pDevice) { + ma_device_state_pulseaudio* pDeviceStatePulseAudio = ma_device_get_backend_state__pulseaudio(pDevice); + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(ma_device_get_context(pDevice)); int resultPA; - MA_ASSERT(pDevice != NULL); - /* NOTE: Don't start the device here. It'll be done at a higher level. */ /* @@ -32670,291 +33528,43 @@ static ma_result ma_device_data_loop__pulse(ma_device* pDevice) the callbacks deal with it. */ while (ma_device_get_state(pDevice) == ma_device_state_started) { - resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); + resultPA = pContextStatePulseAudio->pa_mainloop_iterate(pDeviceStatePulseAudio->pMainLoop, 1, NULL); if (resultPA < 0) { break; } } /* NOTE: Don't stop the device here. It'll be done at a higher level. */ - return MA_SUCCESS; } -static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) +static void ma_device_wakeup__pulseaudio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_pulseaudio* pDeviceStatePulseAudio = ma_device_get_backend_state__pulseaudio(pDevice); + ma_context_state_pulseaudio* pContextStatePulseAudio = ma_context_get_backend_state__pulseaudio(ma_device_get_context(pDevice)); - ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; + pContextStatePulseAudio->pa_mainloop_wakeup(pDeviceStatePulseAudio->pMainLoop); } -static ma_result ma_context_uninit__pulse(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_PulseAudio = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_pulseaudio); + ma_backend_info__pulseaudio, + ma_context_init__pulseaudio, + ma_context_uninit__pulseaudio, + ma_context_enumerate_devices__pulseaudio, + ma_context_get_device_info__pulseaudio, + ma_device_init__pulseaudio, + ma_device_uninit__pulseaudio, + ma_device_start__pulseaudio, + ma_device_stop__pulseaudio, + NULL, /* onDeviceRead */ + NULL, /* onDeviceWrite */ + ma_device_loop__pulseaudio, + ma_device_wakeup__pulseaudio +}; - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); - - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libpulseNames[] = { - "libpulse.so", - "libpulse.so.0" - }; - size_t i; - - for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); - if (pContext->pulse.pulseSO != NULL) { - break; - } - } - - if (pContext->pulse.pulseSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); - pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); +ma_device_backend_vtable* ma_device_backend_pulseaudio = &ma_gDeviceBackendVTable_PulseAudio; #else - /* This strange assignment system is just for type safety. */ - ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; - ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; - ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; - ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; - ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; - ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; - ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; - ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; - ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; - ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; - ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; - ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; - ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; - ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; - ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; - ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; - ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; - ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; - ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; - ma_pa_context_new_proc _pa_context_new = pa_context_new; - ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; - ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; - ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; - ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; - ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; - ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; - ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; - ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; - ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; - ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; - ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; - ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; - ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; - ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; - ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; - ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; - ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; - ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; - ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; - ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; - ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; - ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; - ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; - ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; - ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; - ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; - ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; - ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; - ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; - ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; - ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; - ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; - ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; - ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; - ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; - ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; - ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; - ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; - ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; - ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; - ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; - - pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; - pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; - pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; - pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; - pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; - pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; - pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; - pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; - pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; - pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; - pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; - pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; - pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; - pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; - pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; - pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; - pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; - pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; - pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; - pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; - pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; - pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; - pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; - pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; - pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; - pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; - pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; - pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; - pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; - pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; - pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; - pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; - pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; - pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; - pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; - pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; - pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; - pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; - pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; - pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; -#endif - - /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ - pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); - if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { - return MA_OUT_OF_MEMORY; - } - - pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); - if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); - #endif - return result; - } - - /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ - pCallbacks->onContextInit = ma_context_init__pulse; - pCallbacks->onContextUninit = ma_context_uninit__pulse; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; - pCallbacks->onDeviceInit = ma_device_init__pulse; - pCallbacks->onDeviceUninit = ma_device_uninit__pulse; - pCallbacks->onDeviceStart = ma_device_start__pulse; - pCallbacks->onDeviceStop = ma_device_stop__pulse; - pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_pulseaudio = NULL; #endif @@ -32998,41 +33608,78 @@ typedef void (* ma_JackShutdownCallback) (void* arg); #define ma_JackPortIsPhysical 4 #endif -typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); -typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_client_name_size_proc) (void); -typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); +typedef ma_jack_client_t* (* ma_jack_client_open_proc )(const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); +typedef int (* ma_jack_client_close_proc )(ma_jack_client_t* client); +typedef int (* ma_jack_client_name_size_proc )(void); +typedef int (* ma_jack_set_process_callback_proc )(ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); -typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); -typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); -typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); -typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); -typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); -typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); -typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); -typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); -typedef void (* ma_jack_free_proc) (void* ptr); +typedef void (* ma_jack_on_shutdown_proc )(ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); +typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc )(ma_jack_client_t* client); +typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc )(ma_jack_client_t* client); +typedef const char** (* ma_jack_get_ports_proc )(ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); +typedef int (* ma_jack_activate_proc )(ma_jack_client_t* client); +typedef int (* ma_jack_deactivate_proc )(ma_jack_client_t* client); +typedef int (* ma_jack_connect_proc )(ma_jack_client_t* client, const char* source_port, const char* destination_port); +typedef ma_jack_port_t* (* ma_jack_port_register_proc )(ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); +typedef const char* (* ma_jack_port_name_proc )(const ma_jack_port_t* port); +typedef void* (* ma_jack_port_get_buffer_proc )(ma_jack_port_t* port, ma_jack_nframes_t nframes); +typedef void (* ma_jack_free_proc )(void* ptr); -static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) +typedef struct ma_context_state_jack +{ + ma_log* pLog; + ma_handle jackSO; + ma_jack_client_open_proc jack_client_open; + ma_jack_client_close_proc jack_client_close; + ma_jack_client_name_size_proc jack_client_name_size; + ma_jack_set_process_callback_proc jack_set_process_callback; + ma_jack_set_buffer_size_callback_proc jack_set_buffer_size_callback; + ma_jack_on_shutdown_proc jack_on_shutdown; + ma_jack_get_sample_rate_proc jack_get_sample_rate; + ma_jack_get_buffer_size_proc jack_get_buffer_size; + ma_jack_get_ports_proc jack_get_ports; + ma_jack_activate_proc jack_activate; + ma_jack_deactivate_proc jack_deactivate; + ma_jack_connect_proc jack_connect; + ma_jack_port_register_proc jack_port_register; + ma_jack_port_name_proc jack_port_name; + ma_jack_port_get_buffer_proc jack_port_get_buffer; + ma_jack_free_proc jack_free; + + char* pClientName; + ma_bool32 tryStartServer; +} ma_context_state_jack; + +typedef struct ma_device_state_jack +{ + ma_context_state_jack* pContextStateJACK; + ma_device_backend_connection* pDeviceBackendConnection; + ma_device_type deviceType; + ma_jack_client_t* pClient; + ma_jack_port_t** ppPortsPlayback; + ma_jack_port_t** ppPortsCapture; + float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ + float* pIntermediaryBufferCapture; +} ma_device_state_jack; + +static ma_result ma_context_open_client__jack(ma_context_state_jack* pContextStateJACK, ma_jack_client_t** ppClient) { size_t maxClientNameSize; char clientName[256]; ma_jack_status_t status; ma_jack_client_t* pClient; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pContextStateJACK != NULL); MA_ASSERT(ppClient != NULL); if (ppClient) { *ppClient = NULL; } - maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ - ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); + maxClientNameSize = pContextStateJACK->jack_client_name_size(); /* Includes null terminator. */ + ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContextStateJACK->pClientName != NULL) ? pContextStateJACK->pClientName : "miniaudio", (size_t)-1); - pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); + pClient = pContextStateJACK->jack_client_open(clientName, (pContextStateJACK->tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); if (pClient == NULL) { return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } @@ -33045,12 +33692,181 @@ static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_clie } +static ma_context_state_jack* ma_context_get_backend_state__jack(ma_context* pContext) +{ + return (ma_context_state_jack*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_jack* ma_device_get_backend_state__jack(ma_device* pDevice) +{ + return (ma_device_state_jack*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__jack(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "JACK"; +} + +static ma_result ma_context_init__jack(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_jack* pContextStateJACK; + const ma_context_config_jack* pContextConfigJACK = (const ma_context_config_jack*)pContextBackendConfig; + ma_context_config_jack defaultConfigJACK; + ma_log* pLog = ma_context_get_log(pContext); + + if (pContextConfigJACK == NULL) { + defaultConfigJACK = ma_context_config_jack_init(); + pContextConfigJACK = &defaultConfigJACK; + } + + pContextStateJACK = (ma_context_state_jack*)ma_calloc(sizeof(*pContextStateJACK), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateJACK == NULL) { + return MA_OUT_OF_MEMORY; + } + + #ifndef MA_NO_RUNTIME_LINKING + { + const char* libjackNames[] = { + #if defined(MA_WIN32) + "libjack.dll", + "libjack64.dll" + #else + "libjack.so", + "libjack.so.0" + #endif + }; + size_t i; + + for (i = 0; i < ma_countof(libjackNames); ++i) { + pContextStateJACK->jackSO = ma_dlopen(pLog, libjackNames[i]); + if (pContextStateJACK->jackSO != NULL) { + break; + } + } + + if (pContextStateJACK->jackSO == NULL) { + ma_free(pContextStateJACK, ma_context_get_allocation_callbacks(pContext)); + return MA_NO_BACKEND; + } + + pContextStateJACK->jack_client_open = (ma_jack_client_open_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_client_open"); + pContextStateJACK->jack_client_close = (ma_jack_client_close_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_client_close"); + pContextStateJACK->jack_client_name_size = (ma_jack_client_name_size_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_client_name_size"); + pContextStateJACK->jack_set_process_callback = (ma_jack_set_process_callback_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_set_process_callback"); + pContextStateJACK->jack_set_buffer_size_callback = (ma_jack_set_buffer_size_callback_proc)ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_set_buffer_size_callback"); + pContextStateJACK->jack_on_shutdown = (ma_jack_on_shutdown_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_on_shutdown"); + pContextStateJACK->jack_get_sample_rate = (ma_jack_get_sample_rate_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_get_sample_rate"); + pContextStateJACK->jack_get_buffer_size = (ma_jack_get_buffer_size_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_get_buffer_size"); + pContextStateJACK->jack_get_ports = (ma_jack_get_ports_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_get_ports"); + pContextStateJACK->jack_activate = (ma_jack_activate_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_activate"); + pContextStateJACK->jack_deactivate = (ma_jack_deactivate_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_deactivate"); + pContextStateJACK->jack_connect = (ma_jack_connect_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_connect"); + pContextStateJACK->jack_port_register = (ma_jack_port_register_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_port_register"); + pContextStateJACK->jack_port_name = (ma_jack_port_name_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_port_name"); + pContextStateJACK->jack_port_get_buffer = (ma_jack_port_get_buffer_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_port_get_buffer"); + pContextStateJACK->jack_free = (ma_jack_free_proc )ma_dlsym(pLog, pContextStateJACK->jackSO, "jack_free"); + } + #else + { + /* + This strange assignment system is here just to ensure type safety of miniaudio's function pointer + types. If anything differs slightly the compiler should throw a warning. + */ + ma_jack_client_open_proc _jack_client_open = jack_client_open; + ma_jack_client_close_proc _jack_client_close = jack_client_close; + ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; + ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; + ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; + ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; + ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; + ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; + ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; + ma_jack_activate_proc _jack_activate = jack_activate; + ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; + ma_jack_connect_proc _jack_connect = jack_connect; + ma_jack_port_register_proc _jack_port_register = jack_port_register; + ma_jack_port_name_proc _jack_port_name = jack_port_name; + ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; + ma_jack_free_proc _jack_free = jack_free; + + pContextStateJACK->jack_client_open = _jack_client_open; + pContextStateJACK->jack_client_close = _jack_client_close; + pContextStateJACK->jack_client_name_size = _jack_client_name_size; + pContextStateJACK->jack_set_process_callback = _jack_set_process_callback; + pContextStateJACK->jack_set_buffer_size_callback = _jack_set_buffer_size_callback; + pContextStateJACK->jack_on_shutdown = _jack_on_shutdown; + pContextStateJACK->jack_get_sample_rate = _jack_get_sample_rate; + pContextStateJACK->jack_get_buffer_size = _jack_get_buffer_size; + pContextStateJACK->jack_get_ports = _jack_get_ports; + pContextStateJACK->jack_activate = _jack_activate; + pContextStateJACK->jack_deactivate = _jack_deactivate; + pContextStateJACK->jack_connect = _jack_connect; + pContextStateJACK->jack_port_register = _jack_port_register; + pContextStateJACK->jack_port_name = _jack_port_name; + pContextStateJACK->jack_port_get_buffer = _jack_port_get_buffer; + pContextStateJACK->jack_free = _jack_free; + } + #endif + + if (pContextConfigJACK->pClientName != NULL) { + pContextStateJACK->pClientName = ma_copy_string(pContextConfigJACK->pClientName, ma_context_get_allocation_callbacks(pContext)); + } + + pContextStateJACK->tryStartServer = pContextConfigJACK->tryStartServer; + + /* + Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting + a temporary client. + */ + { + ma_jack_client_t* pDummyClient; + ma_result result = ma_context_open_client__jack(pContextStateJACK, &pDummyClient); + if (result != MA_SUCCESS) { + ma_free(pContextStateJACK->pClientName, ma_context_get_allocation_callbacks(pContext)); + #ifndef MA_NO_RUNTIME_LINKING + { + ma_dlclose(pLog, pContextStateJACK->jackSO); + } + #endif + + ma_free(pContextStateJACK, ma_context_get_allocation_callbacks(pContext)); + return MA_NO_BACKEND; + } + + pContextStateJACK->jack_client_close(pDummyClient); + } + + *ppContextState = pContextStateJACK; + + return MA_SUCCESS; +} + +static void ma_context_uninit__jack(ma_context* pContext) +{ + ma_context_state_jack* pContextStateJACK = ma_context_get_backend_state__jack(pContext); + + MA_ASSERT(pContextStateJACK != NULL); + + ma_free(pContextStateJACK->pClientName, ma_context_get_allocation_callbacks(pContext)); + pContextStateJACK->pClientName = NULL; + + #ifndef MA_NO_RUNTIME_LINKING + { + ma_dlclose(ma_context_get_log(pContext), pContextStateJACK->jackSO); + } + #endif + + ma_free(pContextStateJACK, ma_context_get_allocation_callbacks(pContext)); +} + static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_bool32 cbResult = MA_TRUE; - MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); + (void)pContext; /* Playback. */ if (cbResult) { @@ -33077,11 +33893,12 @@ static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enu static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { + ma_context_state_jack* pContextStateJACK = ma_context_get_backend_state__jack(pContext); ma_jack_client_t* pClient; ma_result result; const char** ppPorts; - MA_ASSERT(pContext != NULL); + MA_ASSERT(pContextStateJACK != NULL); if (pDeviceID != NULL && pDeviceID->jack != 0) { return MA_NO_DEVICE; /* Don't know the device. */ @@ -33101,18 +33918,18 @@ static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_devic pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; /* The channel count and sample rate can only be determined by opening the device. */ - result = ma_context_open_client__jack(pContext, &pClient); + result = ma_context_open_client__jack(pContextStateJACK, &pClient); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); return result; } - pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); + pDeviceInfo->nativeDataFormats[0].sampleRate = pContextStateJACK->jack_get_sample_rate(pClient); pDeviceInfo->nativeDataFormats[0].channels = 0; - ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); + ppPorts = pContextStateJACK->jack_get_ports(pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); if (ppPorts == NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); + pContextStateJACK->jack_client_close(pClient); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } @@ -33124,40 +33941,14 @@ static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_devic pDeviceInfo->nativeDataFormats[0].flags = 0; pDeviceInfo->nativeDataFormatCount = 1; - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__jack(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->jack.pClient != NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); - } + pContextStateJACK->jack_free((void*)ppPorts); + pContextStateJACK->jack_client_close(pClient); return MA_SUCCESS; } + + static void ma_device__jack_shutdown_callback(void* pUserData) { /* JACK died. Stop the device. */ @@ -33170,31 +33961,33 @@ static void ma_device__jack_shutdown_callback(void* pUserData) static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_jack* pDeviceStateJACK = ma_device_get_backend_state__jack(pDevice); + MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); + float* pNewBuffer = (float*)ma_calloc(newBufferSize, ma_device_get_allocation_callbacks(pDevice)); if (pNewBuffer == NULL) { return MA_OUT_OF_MEMORY; } - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + ma_free(pDeviceStateJACK->pIntermediaryBufferCapture, ma_device_get_allocation_callbacks(pDevice)); - pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; + pDeviceStateJACK->pIntermediaryBufferCapture = pNewBuffer; pDevice->playback.internalPeriodSizeInFrames = frameCount; } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); + float* pNewBuffer = (float*)ma_calloc(newBufferSize, ma_device_get_allocation_callbacks(pDevice)); if (pNewBuffer == NULL) { return MA_OUT_OF_MEMORY; } - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + ma_free(pDeviceStateJACK->pIntermediaryBufferPlayback, ma_device_get_allocation_callbacks(pDevice)); - pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; + pDeviceStateJACK->pIntermediaryBufferPlayback = pNewBuffer; pDevice->playback.internalPeriodSizeInFrames = frameCount; } @@ -33203,22 +33996,18 @@ static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, vo static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) { - ma_device* pDevice; - ma_context* pContext; + ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_jack* pDeviceStateJACK = ma_device_get_backend_state__jack(pDevice); + ma_context_state_jack* pContextStateJACK = ma_context_get_backend_state__jack(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); ma_uint32 iChannel; - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { /* Channels need to be interleaved. */ for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); + const float* pSrc = (const float*)pContextStateJACK->jack_port_get_buffer(pDeviceStateJACK->ppPortsCapture[iChannel], frameCount); if (pSrc != NULL) { - float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; + float* pDst = pDeviceStateJACK->pIntermediaryBufferCapture + iChannel; ma_jack_nframes_t iFrame; for (iFrame = 0; iFrame < frameCount; ++iFrame) { *pDst = *pSrc; @@ -33229,17 +34018,17 @@ static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* } } - ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); + ma_device_handle_backend_data_callback(pDevice, NULL, pDeviceStateJACK->pIntermediaryBufferCapture, frameCount); } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_device_handle_backend_data_callback(pDevice, pDeviceStateJACK->pIntermediaryBufferPlayback, NULL, frameCount); /* Channels need to be deinterleaved. */ for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); + float* pDst = (float*)pContextStateJACK->jack_port_get_buffer(pDeviceStateJACK->ppPortsPlayback[iChannel], frameCount); if (pDst != NULL) { - const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; + const float* pSrc = pDeviceStateJACK->pIntermediaryBufferPlayback + iChannel; ma_jack_nframes_t iFrame; for (iFrame = 0; iFrame < frameCount; ++iFrame) { *pDst = *pSrc; @@ -33254,68 +34043,86 @@ static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* return 0; } -static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static void ma_device_uninit__jack(ma_device* pDevice); + +static ma_result ma_device_init__jack(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { + ma_device_state_jack* pDeviceStateJACK; + const ma_device_config_jack* pDeviceConfigJACK = (const ma_device_config_jack*)pDeviceBackendConfig; + ma_device_config_jack defaultConfigJACK; + ma_context_state_jack* pContextStateJACK = ma_context_get_backend_state__jack(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result; ma_uint32 periodSizeInFrames; - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDevice != NULL); + if (pDeviceConfigJACK == NULL) { + defaultConfigJACK = ma_device_config_jack_init(); + pDeviceConfigJACK = &defaultConfigJACK; + } - if (pConfig->deviceType == ma_device_type_loopback) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); + if (deviceType == ma_device_type_loopback) { + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* Only supporting default devices with JACK. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); + if (((deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || + ((deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); return MA_NO_DEVICE; } /* No exclusive mode with the JACK backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); + if (((deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); return MA_SHARE_MODE_NOT_SUPPORTED; } + pDeviceStateJACK = (ma_device_state_jack*)ma_malloc(sizeof(*pDeviceStateJACK), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateJACK == NULL) { + return MA_OUT_OF_MEMORY; + } + /* Open the client. */ - result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); + result = ma_context_open_client__jack(pContextStateJACK, &pDeviceStateJACK->pClient); if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); + ma_free(pDeviceStateJACK, ma_device_get_allocation_callbacks(pDevice)); + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); return result; } /* Callbacks. */ - if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); + if (pContextStateJACK->jack_set_process_callback(pDeviceStateJACK->pClient, ma_device__jack_process_callback, pDevice) != 0) { + ma_free(pDeviceStateJACK, ma_device_get_allocation_callbacks(pDevice)); + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } - if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); + if (pContextStateJACK->jack_set_buffer_size_callback(pDeviceStateJACK->pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { + ma_free(pDeviceStateJACK, ma_device_get_allocation_callbacks(pDevice)); + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } - ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); + pContextStateJACK->jack_on_shutdown(pDeviceStateJACK->pClient, ma_device__jack_shutdown_callback, pDevice); /* The buffer size in frames can change. */ - periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); + periodSizeInFrames = pContextStateJACK->jack_get_buffer_size(pDeviceStateJACK->pClient); - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { ma_uint32 iPort; const char** ppPorts; pDescriptorCapture->format = ma_format_f32; pDescriptorCapture->channels = 0; - pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); + pDescriptorCapture->sampleRate = pContextStateJACK->jack_get_sample_rate(pDeviceStateJACK->pClient); ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); + ppPorts = pContextStateJACK->jack_get_ports(pDeviceStateJACK->pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + ma_free(pDeviceStateJACK, ma_device_get_allocation_callbacks(pDevice)); + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } @@ -33324,8 +34131,9 @@ static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config pDescriptorCapture->channels += 1; } - pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsCapture == NULL) { + pDeviceStateJACK->ppPortsCapture = (ma_jack_port_t**)ma_malloc(sizeof(*pDeviceStateJACK->ppPortsCapture) * pDescriptorCapture->channels, ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateJACK->ppPortsCapture == NULL) { + ma_free(pDeviceStateJACK, ma_device_get_allocation_callbacks(pDevice)); return MA_OUT_OF_MEMORY; } @@ -33334,39 +34142,39 @@ static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config ma_strcpy_s(name, sizeof(name), "capture"); ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ - pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); - if (pDevice->jack.ppPortsCapture[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + pDeviceStateJACK->ppPortsCapture[iPort] = pContextStateJACK->jack_port_register(pDeviceStateJACK->pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); + if (pDeviceStateJACK->ppPortsCapture[iPort] == NULL) { + pContextStateJACK->jack_free((void*)ppPorts); ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } } - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + pContextStateJACK->jack_free((void*)ppPorts); pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferCapture == NULL) { + pDeviceStateJACK->pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateJACK->pIntermediaryBufferCapture == NULL) { ma_device_uninit__jack(pDevice); return MA_OUT_OF_MEMORY; } } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_uint32 iPort; const char** ppPorts; pDescriptorPlayback->format = ma_format_f32; pDescriptorPlayback->channels = 0; - pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); + pDescriptorPlayback->sampleRate = pContextStateJACK->jack_get_sample_rate(pDeviceStateJACK->pClient); ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); + ppPorts = pContextStateJACK->jack_get_ports(pDeviceStateJACK->pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } @@ -33375,9 +34183,9 @@ static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config pDescriptorPlayback->channels += 1; } - pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsPlayback == NULL) { - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); + pDeviceStateJACK->ppPortsPlayback = (ma_jack_port_t**)ma_malloc(sizeof(*pDeviceStateJACK->ppPortsPlayback) * pDescriptorPlayback->channels, ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateJACK->ppPortsPlayback == NULL) { + ma_free(pDeviceStateJACK->ppPortsCapture, ma_device_get_allocation_callbacks(pDevice)); return MA_OUT_OF_MEMORY; } @@ -33386,89 +34194,114 @@ static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config ma_strcpy_s(name, sizeof(name), "playback"); ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ - pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); - if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + pDeviceStateJACK->ppPortsPlayback[iPort] = pContextStateJACK->jack_port_register(pDeviceStateJACK->pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); + if (pDeviceStateJACK->ppPortsPlayback[iPort] == NULL) { + pContextStateJACK->jack_free((void*)ppPorts); ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); + ma_log_post(pContextStateJACK->pLog, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } } - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + pContextStateJACK->jack_free((void*)ppPorts); pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { + pDeviceStateJACK->pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateJACK->pIntermediaryBufferPlayback == NULL) { ma_device_uninit__jack(pDevice); return MA_OUT_OF_MEMORY; } } + *ppDeviceState = pDeviceStateJACK; + return MA_SUCCESS; } +static void ma_device_uninit__jack(ma_device* pDevice) +{ + ma_device_state_jack* pDeviceStateJACK = ma_device_get_backend_state__jack(pDevice); + ma_context_state_jack* pContextStateJACK = ma_context_get_backend_state__jack(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); + + if (pDeviceStateJACK->pClient != NULL) { + pContextStateJACK->jack_client_close(pDeviceStateJACK->pClient); + } + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_free(pDeviceStateJACK->pIntermediaryBufferCapture, ma_device_get_allocation_callbacks(pDevice)); + ma_free(pDeviceStateJACK->ppPortsCapture, ma_device_get_allocation_callbacks(pDevice)); + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_free(pDeviceStateJACK->pIntermediaryBufferPlayback, ma_device_get_allocation_callbacks(pDevice)); + ma_free(pDeviceStateJACK->ppPortsPlayback, ma_device_get_allocation_callbacks(pDevice)); + } +} + static ma_result ma_device_start__jack(ma_device* pDevice) { - ma_context* pContext = pDevice->pContext; + ma_device_state_jack* pDeviceStateJACK = ma_device_get_backend_state__jack(pDevice); + ma_context_state_jack* pContextStateJACK = ma_context_get_backend_state__jack(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); int resultJACK; size_t i; - resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); + resultJACK = pContextStateJACK->jack_activate(pDeviceStateJACK->pClient); if (resultJACK != 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); return MA_FAILED_TO_START_BACKEND_DEVICE; } - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + const char** ppServerPorts = pContextStateJACK->jack_get_ports(pDeviceStateJACK->pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + pContextStateJACK->jack_deactivate(pDeviceStateJACK->pClient); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); return MA_ERROR; } for (i = 0; ppServerPorts[i] != NULL; ++i) { const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); + const char* pClientPort = pContextStateJACK->jack_port_name(pDeviceStateJACK->ppPortsCapture[i]); - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); + resultJACK = pContextStateJACK->jack_connect(pDeviceStateJACK->pClient, pServerPort, pClientPort); if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + pContextStateJACK->jack_free((void*)ppServerPorts); + pContextStateJACK->jack_deactivate(pDeviceStateJACK->pClient); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); return MA_ERROR; } } - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + pContextStateJACK->jack_free((void*)ppServerPorts); } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + const char** ppServerPorts = pContextStateJACK->jack_get_ports(pDeviceStateJACK->pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + pContextStateJACK->jack_deactivate(pDeviceStateJACK->pClient); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); return MA_ERROR; } for (i = 0; ppServerPorts[i] != NULL; ++i) { const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); + const char* pClientPort = pContextStateJACK->jack_port_name(pDeviceStateJACK->ppPortsPlayback[i]); - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); + resultJACK = pContextStateJACK->jack_connect(pDeviceStateJACK->pClient, pClientPort, pServerPort); if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + pContextStateJACK->jack_free((void*)ppServerPorts); + pContextStateJACK->jack_deactivate(pDeviceStateJACK->pClient); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); return MA_ERROR; } } - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + pContextStateJACK->jack_free((void*)ppServerPorts); } return MA_SUCCESS; @@ -33476,154 +34309,40 @@ static ma_result ma_device_start__jack(ma_device* pDevice) static ma_result ma_device_stop__jack(ma_device* pDevice) { - ma_context* pContext = pDevice->pContext; + ma_device_state_jack* pDeviceStateJACK = ma_device_get_backend_state__jack(pDevice); + ma_context_state_jack* pContextStateJACK = ma_context_get_backend_state__jack(ma_device_get_context(pDevice)); - if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { + if (pContextStateJACK->jack_deactivate(pDeviceStateJACK->pClient) != 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); return MA_ERROR; } - ma_device__on_notification_stopped(pDevice); + ma_device_post_notification_stopped(pDevice); return MA_SUCCESS; } -static ma_result ma_context_uninit__jack(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_JACK = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_jack); + ma_backend_info__jack, + ma_context_init__jack, + ma_context_uninit__jack, + ma_context_enumerate_devices__jack, + ma_context_get_device_info__jack, + ma_device_init__jack, + ma_device_uninit__jack, + ma_device_start__jack, + ma_device_stop__jack, + NULL, /* onDeviceRead */ + NULL, /* onDeviceWrite */ + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ +}; - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - pContext->jack.pClientName = NULL; - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libjackNames[] = { -#if defined(MA_WIN32) - "libjack.dll", - "libjack64.dll" -#endif -#if defined(MA_UNIX) - "libjack.so", - "libjack.so.0" -#endif - }; - size_t i; - - for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); - if (pContext->jack.jackSO != NULL) { - break; - } - } - - if (pContext->jack.jackSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); +ma_device_backend_vtable* ma_device_backend_jack = &ma_gDeviceBackendVTable_JACK; #else - /* - This strange assignment system is here just to ensure type safety of miniaudio's function pointer - types. If anything differs slightly the compiler should throw a warning. - */ - ma_jack_client_open_proc _jack_client_open = jack_client_open; - ma_jack_client_close_proc _jack_client_close = jack_client_close; - ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; - ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; - ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; - ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; - ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; - ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; - ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; - ma_jack_activate_proc _jack_activate = jack_activate; - ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; - ma_jack_connect_proc _jack_connect = jack_connect; - ma_jack_port_register_proc _jack_port_register = jack_port_register; - ma_jack_port_name_proc _jack_port_name = jack_port_name; - ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; - ma_jack_free_proc _jack_free = jack_free; - - pContext->jack.jack_client_open = (ma_proc)_jack_client_open; - pContext->jack.jack_client_close = (ma_proc)_jack_client_close; - pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; - pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; - pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; - pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; - pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; - pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; - pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; - pContext->jack.jack_activate = (ma_proc)_jack_activate; - pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; - pContext->jack.jack_connect = (ma_proc)_jack_connect; - pContext->jack.jack_port_register = (ma_proc)_jack_port_register; - pContext->jack.jack_port_name = (ma_proc)_jack_port_name; - pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; - pContext->jack.jack_free = (ma_proc)_jack_free; -#endif - - if (pConfig->jack.pClientName != NULL) { - pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); - } - pContext->jack.tryStartServer = pConfig->jack.tryStartServer; - - /* - Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting - a temporary client. - */ - { - ma_jack_client_t* pDummyClient; - ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); - if (result != MA_SUCCESS) { - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); - #endif - return MA_NO_BACKEND; - } - - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); - } - - - pCallbacks->onContextInit = ma_context_init__jack; - pCallbacks->onContextUninit = ma_context_uninit__jack; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; - pCallbacks->onDeviceInit = ma_device_init__jack; - pCallbacks->onDeviceUninit = ma_device_uninit__jack; - pCallbacks->onDeviceStart = ma_device_start__jack; - pCallbacks->onDeviceStop = ma_device_stop__jack; - pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_jack = NULL; #endif /* MA_HAS_JACK */ @@ -33670,29 +34389,29 @@ References /* CoreFoundation */ typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); -typedef void (* ma_CFRelease_proc)(CFTypeRef cf); +typedef void (* ma_CFRelease_proc )(CFTypeRef cf); /* CoreAudio */ #if defined(MA_APPLE_DESKTOP) -typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); -typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); -typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); -typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); +typedef OSStatus (* ma_AudioObjectGetPropertyData_proc )(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); +typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc )(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); +typedef OSStatus (* ma_AudioObjectSetPropertyData_proc )(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); +typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc )(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); #endif /* AudioToolbox */ -typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); -typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); -typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); -typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); -typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); -typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); -typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); -typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); +typedef AudioComponent (* ma_AudioComponentFindNext_proc )(AudioComponent inComponent, const AudioComponentDescription* inDesc); +typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); +typedef OSStatus (* ma_AudioComponentInstanceNew_proc )(AudioComponent inComponent, AudioComponentInstance* outInstance); +typedef OSStatus (* ma_AudioOutputUnitStart_proc )(AudioUnit inUnit); +typedef OSStatus (* ma_AudioOutputUnitStop_proc )(AudioUnit inUnit); +typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc )(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); +typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc )(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); +typedef OSStatus (* ma_AudioUnitGetProperty_proc )(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); +typedef OSStatus (* ma_AudioUnitSetProperty_proc )(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); +typedef OSStatus (* ma_AudioUnitInitialize_proc )(AudioUnit inUnit); +typedef OSStatus (* ma_AudioUnitRender_proc )(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); #define MA_COREAUDIO_OUTPUT_BUS 0 @@ -33735,17 +34454,67 @@ size, allocate a block of memory of that size and then call AudioObjectGetProper AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. */ -#if defined(MA_APPLE_MOBILE) -static void ma_device__on_notification_interruption_began(ma_device* pDevice) +typedef struct ma_context_state_coreaudio { - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); + ma_handle hCoreFoundation; + ma_CFStringGetCString_proc CFStringGetCString; + ma_CFRelease_proc CFRelease; + + ma_handle hCoreAudio; + ma_AudioObjectGetPropertyData_proc AudioObjectGetPropertyData; + ma_AudioObjectGetPropertyDataSize_proc AudioObjectGetPropertyDataSize; + ma_AudioObjectSetPropertyData_proc AudioObjectSetPropertyData; + ma_AudioObjectAddPropertyListener_proc AudioObjectAddPropertyListener; + ma_AudioObjectRemovePropertyListener_proc AudioObjectRemovePropertyListener; + + ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ + ma_AudioComponentFindNext_proc AudioComponentFindNext; + ma_AudioComponentInstanceDispose_proc AudioComponentInstanceDispose; + ma_AudioComponentInstanceNew_proc AudioComponentInstanceNew; + ma_AudioOutputUnitStart_proc AudioOutputUnitStart; + ma_AudioOutputUnitStop_proc AudioOutputUnitStop; + ma_AudioUnitAddPropertyListener_proc AudioUnitAddPropertyListener; + ma_AudioUnitGetPropertyInfo_proc AudioUnitGetPropertyInfo; + ma_AudioUnitGetProperty_proc AudioUnitGetProperty; + ma_AudioUnitSetProperty_proc AudioUnitSetProperty; + ma_AudioUnitInitialize_proc AudioUnitInitialize; + ma_AudioUnitRender_proc AudioUnitRender; + + AudioComponent component; + ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ +} ma_context_state_coreaudio; + +typedef struct ma_device_state_coreaudio +{ + ma_uint32 deviceObjectIDPlayback; + ma_uint32 deviceObjectIDCapture; + AudioUnit audioUnitPlayback; + AudioUnit audioUnitCapture; + AudioBufferList* pAudioBufferList; /* Only used for input devices. */ + ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ + ma_event stopEvent; + ma_uint32 originalPeriodSizeInFrames; + ma_uint32 originalPeriodSizeInMilliseconds; + ma_uint32 originalPeriods; + ma_performance_profile originalPerformanceProfile; + ma_bool32 isDefaultPlaybackDevice; + ma_bool32 isDefaultCaptureDevice; + ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ + ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ + void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ +} ma_device_state_coreaudio; + + +static ma_context_state_coreaudio* ma_context_get_backend_state__coreaudio(ma_context* pContext) +{ + return (ma_context_state_coreaudio*)ma_context_get_backend_state(pContext); } -static void ma_device__on_notification_interruption_ended(ma_device* pDevice) +static ma_device_state_coreaudio* ma_device_get_backend_state__coreaudio(ma_device* pDevice) { - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); + return (ma_device_state_coreaudio*)ma_device_get_backend_state(pDevice); } -#endif + static ma_result ma_result_from_OSStatus(OSStatus status) { @@ -34058,6 +34827,7 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioObjectPropertyAddress propAddressDevices; UInt32 deviceObjectsDataSize; OSStatus status; @@ -34075,7 +34845,7 @@ static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt3 propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); + status = pContextStateCoreAudio->AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -34085,7 +34855,7 @@ static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt3 return MA_OUT_OF_MEMORY; } - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); if (status != noErr) { ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); @@ -34099,6 +34869,7 @@ static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt3 static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; @@ -34110,7 +34881,7 @@ static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, Aud propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(*pUID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(objectID, &propAddress, 0, NULL, &dataSize, pUID); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -34120,6 +34891,7 @@ static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, Aud static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); CFStringRef uid; ma_result result; @@ -34130,16 +34902,17 @@ static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID obje return result; } - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { + if (!pContextStateCoreAudio->CFStringGetCString(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { return MA_ERROR; } - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); + pContextStateCoreAudio->CFRelease(uid); return MA_SUCCESS; } static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioObjectPropertyAddress propAddress; CFStringRef deviceName = NULL; UInt32 dataSize; @@ -34152,21 +34925,22 @@ static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID obj propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(deviceName); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); if (status != noErr) { return ma_result_from_OSStatus(status); } - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { + if (!pContextStateCoreAudio->CFStringGetCString(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { return MA_ERROR; } - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); + pContextStateCoreAudio->CFRelease(deviceName); return MA_SUCCESS; } static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; @@ -34180,7 +34954,7 @@ static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioOb propAddress.mScope = scope; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + status = pContextStateCoreAudio->AudioObjectGetPropertyDataSize(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return MA_FALSE; } @@ -34190,7 +34964,7 @@ static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioOb return MA_FALSE; /* Out of memory. */ } - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); if (status != noErr) { ma_free(pBufferList, &pContext->allocationCallbacks); return MA_FALSE; @@ -34218,6 +34992,7 @@ static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, Audio static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; @@ -34235,7 +35010,7 @@ static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, Au propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + status = pContextStateCoreAudio->AudioObjectGetPropertyDataSize(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -34245,7 +35020,7 @@ static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, Au return MA_OUT_OF_MEMORY; } - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); if (status != noErr) { ma_free(pDescriptions, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); @@ -34259,6 +35034,7 @@ static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, Au static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; @@ -34273,7 +35049,7 @@ static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioOb propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + status = pContextStateCoreAudio->AudioObjectGetPropertyDataSize(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -34283,7 +35059,7 @@ static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioOb return MA_OUT_OF_MEMORY; } - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); if (status != noErr) { ma_free(pChannelLayout, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); @@ -34346,6 +35122,7 @@ static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjec static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; @@ -34363,7 +35140,7 @@ static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObje propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + status = pContextStateCoreAudio->AudioObjectGetPropertyDataSize(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -34373,7 +35150,7 @@ static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObje return MA_OUT_OF_MEMORY; } - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); if (status != noErr) { ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); @@ -34471,6 +35248,7 @@ static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioObjectPropertyAddress propAddress; AudioValueRange bufferSizeRange; UInt32 dataSize; @@ -34486,7 +35264,7 @@ static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pC propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(bufferSizeRange); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -34505,6 +35283,7 @@ static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pC static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); ma_result result; ma_uint32 chosenBufferSizeInFrames; AudioObjectPropertyAddress propAddress; @@ -34523,11 +35302,11 @@ static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); + pContextStateCoreAudio->AudioObjectSetPropertyData(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); /* Get the actual size of the buffer. */ dataSize = sizeof(*pPeriodSizeInOut); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -34538,6 +35317,7 @@ static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioObjectPropertyAddress propAddressDefaultDevice; UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); AudioObjectID defaultDeviceObjectID; @@ -34558,7 +35338,7 @@ static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_t } defaultDeviceObjectIDSize = sizeof(AudioObjectID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); + status = pContextStateCoreAudio->AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); if (status == noErr) { *pDeviceObjectID = defaultDeviceObjectID; return MA_SUCCESS; @@ -34808,6 +35588,7 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioUnitScope deviceScope; AudioUnitElement deviceBus; UInt32 channelLayoutSize; @@ -34825,7 +35606,7 @@ static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit au deviceBus = MA_COREAUDIO_INPUT_BUS; } - status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); + status = pContextStateCoreAudio->AudioUnitGetPropertyInfo(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -34835,7 +35616,7 @@ static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit au return MA_OUT_OF_MEMORY; } - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); + status = pContextStateCoreAudio->AudioUnitGetProperty(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); if (status != noErr) { ma_free(pChannelLayout, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); @@ -34853,6 +35634,474 @@ static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit au #endif /* MA_APPLE_DESKTOP */ +#if defined(MA_APPLE_DESKTOP) +static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ +static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; +static ma_mutex g_DeviceTrackingMutex_CoreAudio; +static ma_device** g_ppTrackedDevices_CoreAudio = NULL; +static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; +static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; + +static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) +{ + ma_device_type deviceType; + + /* Not sure if I really need to check this, but it makes me feel better. */ + if (addressCount == 0) { + return noErr; + } + + if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { + deviceType = ma_device_type_playback; + } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { + deviceType = ma_device_type_capture; + } else { + return noErr; /* Should never hit this. */ + } + + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); + { + ma_uint32 iDevice; + for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { + ma_result reinitResult; + ma_device* pDevice; + ma_device_state_coreaudio* pDeviceStateCoreAudio; + ma_context_state_coreaudio* pContextStateCoreAudio; + + pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; + pDeviceStateCoreAudio = ma_device_get_backend_state__coreaudio(pDevice); + pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(ma_device_get_context(pDevice)); + + if (ma_device_get_type(pDevice) == deviceType || ma_device_get_type(pDevice) == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback) { + pDeviceStateCoreAudio->isSwitchingPlaybackDevice = MA_TRUE; + reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); + pDeviceStateCoreAudio->isSwitchingPlaybackDevice = MA_FALSE; + } else { + pDeviceStateCoreAudio->isSwitchingCaptureDevice = MA_TRUE; + reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); + pDeviceStateCoreAudio->isSwitchingCaptureDevice = MA_FALSE; + } + + if (reinitResult == MA_SUCCESS) { + ma_device__post_init_setup(pDevice, deviceType); + + /* Restart the device if required. If this fails we need to stop the device entirely. */ + if (ma_device_get_state(pDevice) == ma_device_state_started) { + OSStatus status; + if (deviceType == ma_device_type_playback) { + status = pContextStateCoreAudio->AudioOutputUnitStart(pDeviceStateCoreAudio->audioUnitPlayback); + if (status != noErr) { + if (ma_device_get_type(pDevice) == ma_device_type_duplex) { + pContextStateCoreAudio->AudioOutputUnitStop(pDeviceStateCoreAudio->audioUnitCapture); + } + ma_device__set_state(pDevice, ma_device_state_stopped); + } + } else if (deviceType == ma_device_type_capture) { + status = pContextStateCoreAudio->AudioOutputUnitStart(pDeviceStateCoreAudio->audioUnitCapture); + if (status != noErr) { + if (ma_device_get_type(pDevice) == ma_device_type_duplex) { + pContextStateCoreAudio->AudioOutputUnitStop(pDeviceStateCoreAudio->audioUnitPlayback); + } + ma_device__set_state(pDevice, ma_device_state_stopped); + } + } + } + + ma_device_post_notification_rerouted(pDevice); + } + } + } + } + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + + /* Unused parameters. */ + (void)objectID; + (void)pUserData; + + return noErr; +} + +static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext, ma_context_state_coreaudio* pContextStateCoreAudio) +{ + (void)pContext; + + ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); + { + /* Don't do anything if we've already initialized device tracking. */ + if (g_DeviceTrackingInitCounter_CoreAudio == 0) { + AudioObjectPropertyAddress propAddress; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); + + propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + pContextStateCoreAudio->AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + pContextStateCoreAudio->AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + } + g_DeviceTrackingInitCounter_CoreAudio += 1; + } + ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); + + return MA_SUCCESS; +} + +static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext, ma_context_state_coreaudio* pContextStateCoreAudio) +{ + ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); + { + if (g_DeviceTrackingInitCounter_CoreAudio > 0) { + g_DeviceTrackingInitCounter_CoreAudio -= 1; + } + + if (g_DeviceTrackingInitCounter_CoreAudio == 0) { + AudioObjectPropertyAddress propAddress; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + pContextStateCoreAudio->AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + pContextStateCoreAudio->AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + /* At this point there should be no tracked devices. If not there's an error somewhere. */ + if (g_ppTrackedDevices_CoreAudio != NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); + ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); + return MA_INVALID_OPERATION; + } + + ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); + } + } + ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); + + return MA_SUCCESS; +} + +static ma_result ma_device__track__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); + { + /* Allocate memory if required. */ + if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { + ma_uint32 newCap; + ma_device** ppNewDevices; + + newCap = g_TrackedDeviceCap_CoreAudio * 2; + if (newCap == 0) { + newCap = 1; + } + + ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, ma_device_get_allocation_callbacks(pDevice)); + if (ppNewDevices == NULL) { + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + return MA_OUT_OF_MEMORY; + } + + g_ppTrackedDevices_CoreAudio = ppNewDevices; + g_TrackedDeviceCap_CoreAudio = newCap; + } + + g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; + g_TrackedDeviceCount_CoreAudio += 1; + } + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + + return MA_SUCCESS; +} + +static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); + { + ma_uint32 iDevice; + for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { + if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { + /* We've found the device. We now need to remove it from the list. */ + ma_uint32 jDevice; + for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { + g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; + } + + g_TrackedDeviceCount_CoreAudio -= 1; + + /* If there's nothing else in the list we need to free memory. */ + if (g_TrackedDeviceCount_CoreAudio == 0) { + ma_free(g_ppTrackedDevices_CoreAudio, ma_device_get_allocation_callbacks(pDevice)); + g_ppTrackedDevices_CoreAudio = NULL; + g_TrackedDeviceCap_CoreAudio = 0; + } + + break; + } + } + } + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + + return MA_SUCCESS; +} +#endif + + +static void ma_backend_info__coreaudio(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "Core Audio"; +} + +#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) +static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) +{ + /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ + MA_ASSERT(category != ma_ios_session_category_default); + MA_ASSERT(category != ma_ios_session_category_none); + + switch (category) { + case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; + case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; + case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; + case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; + case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; + case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; + case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; + case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; + default: return AVAudioSessionCategoryAmbient; + } +} +#endif + +static ma_result ma_context_init__coreaudio(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_coreaudio* pContextStateCoreAudio; + const ma_context_config_coreaudio* pContextConfigCoreAudio = (const ma_context_config_coreaudio*)pContextBackendConfig; + ma_context_config_coreaudio defaultConfigCoreAudio; + + if (pContextConfigCoreAudio == NULL) { + defaultConfigCoreAudio = ma_context_config_coreaudio_init(); + pContextConfigCoreAudio = &defaultConfigCoreAudio; + } + + pContextStateCoreAudio = (ma_context_state_coreaudio*)ma_calloc(sizeof(*pContextStateCoreAudio), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateCoreAudio == NULL) { + return MA_OUT_OF_MEMORY; + } + + #if defined(MA_APPLE_MOBILE) + { + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + AVAudioSessionCategoryOptions options = pContextConfigCoreAudio->sessionCategoryOptions; + + MA_ASSERT(pAudioSession != NULL); + + if (pContextConfigCoreAudio->sessionCategory == ma_ios_session_category_default) { + /* + I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails + we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. + */ + #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) + options |= AVAudioSessionCategoryOptionDefaultToSpeaker; + #endif + + if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { + /* Using PlayAndRecord */ + } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { + /* Using Playback */ + } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { + /* Using Record */ + } else { + /* Leave as default? */ + } + } else { + if (pContextConfigCoreAudio->sessionCategory != ma_ios_session_category_none) { + #if defined(__IPHONE_12_0) + if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pContextConfigCoreAudio->sessionCategory) withOptions:options error:nil]) { + return MA_INVALID_OPERATION; /* Failed to set session category. */ + } + #else + /* Ignore the session category on version 11 and older, but post a warning. */ + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); + #endif + } + } + + if (!pContextConfigCoreAudio->noAudioSessionActivate) { + if (![pAudioSession setActive:true error:nil]) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); + return MA_FAILED_TO_INIT_BACKEND; + } + } + } + } + #endif + + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + { + pContextStateCoreAudio->hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); + if (pContextStateCoreAudio->hCoreFoundation == NULL) { + return MA_API_NOT_FOUND; + } + + pContextStateCoreAudio->CFStringGetCString = (ma_CFStringGetCString_proc)ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreFoundation, "CFStringGetCString"); + pContextStateCoreAudio->CFRelease = (ma_CFRelease_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreFoundation, "CFRelease"); + + + pContextStateCoreAudio->hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); + if (pContextStateCoreAudio->hCoreAudio == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreFoundation); + return MA_API_NOT_FOUND; + } + + pContextStateCoreAudio->AudioObjectGetPropertyData = (ma_AudioObjectGetPropertyData_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio, "AudioObjectGetPropertyData"); + pContextStateCoreAudio->AudioObjectGetPropertyDataSize = (ma_AudioObjectGetPropertyDataSize_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio, "AudioObjectGetPropertyDataSize"); + pContextStateCoreAudio->AudioObjectSetPropertyData = (ma_AudioObjectSetPropertyData_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio, "AudioObjectSetPropertyData"); + pContextStateCoreAudio->AudioObjectAddPropertyListener = (ma_AudioObjectAddPropertyListener_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio, "AudioObjectAddPropertyListener"); + pContextStateCoreAudio->AudioObjectRemovePropertyListener = (ma_AudioObjectRemovePropertyListener_proc)ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio, "AudioObjectRemovePropertyListener"); + + /* + It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still + defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. + The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to + AudioToolbox. + */ + pContextStateCoreAudio->hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); + if (pContextStateCoreAudio->hAudioUnit == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreFoundation); + return MA_API_NOT_FOUND; + } + + if (ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioComponentFindNext") == NULL) { + /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit); + pContextStateCoreAudio->hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); + if (pContextStateCoreAudio->hAudioUnit == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreFoundation); + return MA_API_NOT_FOUND; + } + } + + pContextStateCoreAudio->AudioComponentFindNext = (ma_AudioComponentFindNext_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioComponentFindNext"); + pContextStateCoreAudio->AudioComponentInstanceDispose = (ma_AudioComponentInstanceDispose_proc)ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioComponentInstanceDispose"); + pContextStateCoreAudio->AudioComponentInstanceNew = (ma_AudioComponentInstanceNew_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioComponentInstanceNew"); + pContextStateCoreAudio->AudioOutputUnitStart = (ma_AudioOutputUnitStart_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioOutputUnitStart"); + pContextStateCoreAudio->AudioOutputUnitStop = (ma_AudioOutputUnitStop_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioOutputUnitStop"); + pContextStateCoreAudio->AudioUnitAddPropertyListener = (ma_AudioUnitAddPropertyListener_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioUnitAddPropertyListener"); + pContextStateCoreAudio->AudioUnitGetPropertyInfo = (ma_AudioUnitGetPropertyInfo_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioUnitGetPropertyInfo"); + pContextStateCoreAudio->AudioUnitGetProperty = (ma_AudioUnitGetProperty_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioUnitGetProperty"); + pContextStateCoreAudio->AudioUnitSetProperty = (ma_AudioUnitSetProperty_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioUnitSetProperty"); + pContextStateCoreAudio->AudioUnitInitialize = (ma_AudioUnitInitialize_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioUnitInitialize"); + pContextStateCoreAudio->AudioUnitRender = (ma_AudioUnitRender_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit, "AudioUnitRender"); + } + #else + { + pContextStateCoreAudio->CFStringGetCString = CFStringGetCString; + pContextStateCoreAudio->CFRelease = CFRelease; + + #if defined(MA_APPLE_DESKTOP) + pContextStateCoreAudio->AudioObjectGetPropertyData = AudioObjectGetPropertyData; + pContextStateCoreAudio->AudioObjectGetPropertyDataSize = AudioObjectGetPropertyDataSize; + pContextStateCoreAudio->AudioObjectSetPropertyData = AudioObjectSetPropertyData; + pContextStateCoreAudio->AudioObjectAddPropertyListener = AudioObjectAddPropertyListener; + pContextStateCoreAudio->AudioObjectRemovePropertyListener = AudioObjectRemovePropertyListener; + #endif + + pContextStateCoreAudio->AudioComponentFindNext = AudioComponentFindNext; + pContextStateCoreAudio->AudioComponentInstanceDispose = AudioComponentInstanceDispose; + pContextStateCoreAudio->AudioComponentInstanceNew = AudioComponentInstanceNew; + pContextStateCoreAudio->AudioOutputUnitStart = AudioOutputUnitStart; + pContextStateCoreAudio->AudioOutputUnitStop = AudioOutputUnitStop; + pContextStateCoreAudio->AudioUnitAddPropertyListener = AudioUnitAddPropertyListener; + pContextStateCoreAudio->AudioUnitGetPropertyInfo = AudioUnitGetPropertyInfo; + pContextStateCoreAudio->AudioUnitGetProperty = AudioUnitGetProperty; + pContextStateCoreAudio->AudioUnitSetProperty = AudioUnitSetProperty; + pContextStateCoreAudio->AudioUnitInitialize = AudioUnitInitialize; + pContextStateCoreAudio->AudioUnitRender = AudioUnitRender; + } + #endif + + /* Audio component. */ + { + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + #if defined(MA_APPLE_DESKTOP) + desc.componentSubType = kAudioUnitSubType_HALOutput; + #else + desc.componentSubType = kAudioUnitSubType_RemoteIO; + #endif + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + pContextStateCoreAudio->component = pContextStateCoreAudio->AudioComponentFindNext(NULL, &desc); + if (pContextStateCoreAudio->component == NULL) { + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreFoundation); + #endif + return MA_FAILED_TO_INIT_BACKEND; + } + } + + #if !defined(MA_APPLE_MOBILE) + { + ma_result result = ma_context__init_device_tracking__coreaudio(pContext, pContextStateCoreAudio); + if (result != MA_SUCCESS) { + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreFoundation); + #endif + return result; + } + } + #endif + + pContextStateCoreAudio->noAudioSessionDeactivate = pContextConfigCoreAudio->noAudioSessionDeactivate; + + *ppContextState = pContextStateCoreAudio; + + return MA_SUCCESS; +} + +static void ma_context_uninit__coreaudio(ma_context* pContext) +{ + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); + +#if defined(MA_APPLE_MOBILE) + if (!pContextStateCoreAudio->noAudioSessionDeactivate) { + if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); + return MA_FAILED_TO_INIT_BACKEND; + } + } +#endif + +#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContextStateCoreAudio->hCoreFoundation); +#endif + +#if !defined(MA_APPLE_MOBILE) + ma_context__uninit_device_tracking__coreaudio(pContext, pContextStateCoreAudio); +#endif + + ma_free(pContextStateCoreAudio, ma_context_get_allocation_callbacks(pContext)); +} + + #if !defined(MA_APPLE_DESKTOP) static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) { @@ -35073,6 +36322,7 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ #else /* Mobile */ { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); AudioComponentDescription desc; AudioComponent component; AudioUnit audioUnit; @@ -35128,12 +36378,12 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ desc.componentFlags = 0; desc.componentFlagsMask = 0; - component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); + component = pContextStateCoreAudio->AudioComponentFindNext(NULL, &desc); if (component == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); + status = pContextStateCoreAudio->AudioComponentInstanceNew(component, &audioUnit); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -35142,13 +36392,13 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; propSize = sizeof(bestFormat); - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); + status = pContextStateCoreAudio->AudioUnitGetProperty(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(audioUnit); return ma_result_from_OSStatus(status); } - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(audioUnit); audioUnit = NULL; /* Only a single format is being reported for iOS. */ @@ -35226,23 +36476,25 @@ static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInF static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) { + ma_device_state_coreaudio* pDeviceStateCoreAudio = ma_device_get_backend_state__coreaudio(pDevice); + MA_ASSERT(pDevice != NULL); MA_ASSERT(format != ma_format_unknown); MA_ASSERT(channels > 0); /* Only resize the buffer if necessary. */ - if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { + if (pDeviceStateCoreAudio->audioBufferCapInFrames < sizeInFrames) { AudioBufferList* pNewAudioBufferList; - pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); + pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, ma_device_get_allocation_callbacks(pDevice)); if (pNewAudioBufferList == NULL) { return MA_OUT_OF_MEMORY; } /* At this point we'll have a new AudioBufferList and we can free the old one. */ - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; + ma_free(pDeviceStateCoreAudio->pAudioBufferList, ma_device_get_allocation_callbacks(pDevice)); + pDeviceStateCoreAudio->pAudioBufferList = pNewAudioBufferList; + pDeviceStateCoreAudio->audioBufferCapInFrames = sizeInFrames; } /* Getting here means the capacity of the audio is fine. */ @@ -35335,6 +36587,8 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) { ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_coreaudio* pDeviceStateCoreAudio = ma_device_get_backend_state__coreaudio(pDevice); + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(ma_device_get_context(pDevice)); AudioBufferList* pRenderedBufferList; ma_result result; ma_stream_layout layout; @@ -35343,7 +36597,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla MA_ASSERT(pDevice != NULL); - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; + pRenderedBufferList = (AudioBufferList*)pDeviceStateCoreAudio->pAudioBufferList; MA_ASSERT(pRenderedBufferList); /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ @@ -35366,7 +36620,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla return noErr; } - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; + pRenderedBufferList = (AudioBufferList*)pDeviceStateCoreAudio->pAudioBufferList; MA_ASSERT(pRenderedBufferList); /* @@ -35378,11 +36632,11 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla To work around this we need to explicitly set the size of each buffer to their respective size in bytes. */ for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; + pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDeviceStateCoreAudio->audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; /*printf("DEBUG: nDataByteSize = %d\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ } - status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); + status = pContextStateCoreAudio->AudioUnitRender(pDeviceStateCoreAudio->audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); if (status != noErr) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status); return status; @@ -35463,11 +36717,14 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) { ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_coreaudio* pDeviceStateCoreAudio = ma_device_get_backend_state__coreaudio(pDevice); + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(ma_device_get_context(pDevice)); + MA_ASSERT(pDevice != NULL); /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { + if (((audioUnit == pDeviceStateCoreAudio->audioUnitPlayback) && pDeviceStateCoreAudio->isSwitchingPlaybackDevice) || + ((audioUnit == pDeviceStateCoreAudio->audioUnitCapture) && pDeviceStateCoreAudio->isSwitchingCaptureDevice)) { return; } @@ -35478,11 +36735,11 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio Audio APIs in the callback when the device has been stopped or uninitialized. */ if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_device__on_notification_stopped(pDevice); + ma_device_post_notification_stopped(pDevice); } else { UInt32 isRunning; UInt32 isRunningSize = sizeof(isRunning); - OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); + OSStatus status = pContextStateCoreAudio->AudioUnitGetProperty(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); if (status != noErr) { goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ } @@ -35496,16 +36753,16 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { + if (((audioUnit == pDeviceStateCoreAudio->audioUnitPlayback) && pDeviceStateCoreAudio->isDefaultPlaybackDevice) || + ((audioUnit == pDeviceStateCoreAudio->audioUnitCapture) && pDeviceStateCoreAudio->isDefaultCaptureDevice)) { /* It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it hasn't!). */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { + if (((audioUnit == pDeviceStateCoreAudio->audioUnitPlayback) && pDeviceStateCoreAudio->isSwitchingPlaybackDevice) || + ((audioUnit == pDeviceStateCoreAudio->audioUnitCapture) && pDeviceStateCoreAudio->isSwitchingCaptureDevice)) { goto done; } @@ -35520,7 +36777,7 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio } /* Getting here means we need to stop the device. */ - ma_device__on_notification_stopped(pDevice); + ma_device_post_notification_stopped(pDevice); } } @@ -35528,223 +36785,9 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio done: /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ - ma_event_signal(&pDevice->coreaudio.stopEvent); + ma_event_signal(&pDeviceStateCoreAudio->stopEvent); } -#if defined(MA_APPLE_DESKTOP) -static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ -static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; -static ma_mutex g_DeviceTrackingMutex_CoreAudio; -static ma_device** g_ppTrackedDevices_CoreAudio = NULL; -static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; -static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; - -static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) -{ - ma_device_type deviceType; - - /* Not sure if I really need to check this, but it makes me feel better. */ - if (addressCount == 0) { - return noErr; - } - - if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { - deviceType = ma_device_type_playback; - } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { - deviceType = ma_device_type_capture; - } else { - return noErr; /* Should never hit this. */ - } - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - ma_result reinitResult; - ma_device* pDevice; - - pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; - if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { - if (deviceType == ma_device_type_playback) { - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; - } else { - pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; - } - - if (reinitResult == MA_SUCCESS) { - ma_device__post_init_setup(pDevice, deviceType); - - /* Restart the device if required. If this fails we need to stop the device entirely. */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - OSStatus status; - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } else if (deviceType == ma_device_type_capture) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - } - - ma_device__on_notification_rerouted(pDevice); - } - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - /* Unused parameters. */ - (void)objectID; - (void)pUserData; - - return noErr; -} - -static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - /* Don't do anything if we've already initialized device tracking. */ - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - } - g_DeviceTrackingInitCounter_CoreAudio += 1; - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - if (g_DeviceTrackingInitCounter_CoreAudio > 0) - g_DeviceTrackingInitCounter_CoreAudio -= 1; - - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - /* At this point there should be no tracked devices. If not there's an error somewhere. */ - if (g_ppTrackedDevices_CoreAudio != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - return MA_INVALID_OPERATION; - } - - ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); - } - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__track__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - /* Allocate memory if required. */ - if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { - ma_uint32 newCap; - ma_device** ppNewDevices; - - newCap = g_TrackedDeviceCap_CoreAudio * 2; - if (newCap == 0) { - newCap = 1; - } - - ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); - if (ppNewDevices == NULL) { - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - return MA_OUT_OF_MEMORY; - } - - g_ppTrackedDevices_CoreAudio = ppNewDevices; - g_TrackedDeviceCap_CoreAudio = newCap; - } - - g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; - g_TrackedDeviceCount_CoreAudio += 1; - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { - /* We've found the device. We now need to remove it from the list. */ - ma_uint32 jDevice; - for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { - g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; - } - - g_TrackedDeviceCount_CoreAudio -= 1; - - /* If there's nothing else in the list we need to free memory. */ - if (g_TrackedDeviceCount_CoreAudio == 0) { - ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); - g_ppTrackedDevices_CoreAudio = NULL; - g_TrackedDeviceCap_CoreAudio = 0; - } - - break; - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} -#endif - #if defined(MA_APPLE_MOBILE) @interface ma_ios_notification_handler:NSObject { ma_device* m_pDevice; @@ -35802,13 +36845,13 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) Fire the notification after the device has been stopped to ensure it's in the correct state when the notification handler is invoked. */ - ma_device__on_notification_interruption_began(m_pDevice); + ma_device_post_notification_interruption_began(m_pDevice); } break; case AVAudioSessionInterruptionTypeEnded: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); - ma_device__on_notification_interruption_ended(m_pDevice); + ma_device_post_notification_interruption_ended(m_pDevice); } break; } } @@ -35860,42 +36903,46 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); /* Let the application know about the route change. */ - ma_device__on_notification_rerouted(m_pDevice); + ma_device_post_notification_rerouted(m_pDevice); } @end #endif -static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) +static void ma_device_uninit__coreaudio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); + ma_device_state_coreaudio* pDeviceStateCoreAudio = ma_device_get_backend_state__coreaudio(pDevice); + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(ma_device_get_context(pDevice)); -#if defined(MA_APPLE_DESKTOP) - /* - Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll - just gracefully ignore it. - */ - ma_device__untrack__coreaudio(pDevice); -#endif -#if defined(MA_APPLE_MOBILE) - if (pDevice->coreaudio.pNotificationHandler != NULL) { - ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; - [pNotificationHandler remove_handler]; + #if defined(MA_APPLE_DESKTOP) + { + /* + Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll + just gracefully ignore it. + */ + ma_device__untrack__coreaudio(pDevice); } -#endif + #endif + #if defined(MA_APPLE_MOBILE) + { + if (pDeviceStateCoreAudio->pNotificationHandler != NULL) { + ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDeviceStateCoreAudio->pNotificationHandler; + [pNotificationHandler remove_handler]; + } + } + #endif - if (pDevice->coreaudio.audioUnitCapture != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (pDeviceStateCoreAudio->audioUnitCapture != NULL) { + pContextStateCoreAudio->AudioComponentInstanceDispose(pDeviceStateCoreAudio->audioUnitCapture); } - if (pDevice->coreaudio.audioUnitPlayback != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + if (pDeviceStateCoreAudio->audioUnitPlayback != NULL) { + pContextStateCoreAudio->AudioComponentInstanceDispose(pDeviceStateCoreAudio->audioUnitPlayback); } - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + if (pDeviceStateCoreAudio->pAudioBufferList) { + ma_free(pDeviceStateCoreAudio->pAudioBufferList, ma_device_get_allocation_callbacks(pDevice)); } - return MA_SUCCESS; + ma_free(pDeviceStateCoreAudio, ma_device_get_allocation_callbacks(pDevice)); } typedef struct @@ -35932,6 +36979,7 @@ typedef struct static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ { + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext); ma_result result = MA_SUCCESS; OSStatus status; UInt32 enableIOFlag; @@ -35977,7 +37025,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev /* Audio unit. */ - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); + status = pContextStateCoreAudio->AudioComponentInstanceNew(pContextStateCoreAudio->component, &pData->audioUnit); if (status != noErr) { return ma_result_from_OSStatus(status); } @@ -35989,25 +37037,25 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev enableIOFlag = 0; } - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + status = pContextStateCoreAudio->AudioUnitSetProperty(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(status); } enableIOFlag = (enableIOFlag == 0) ? 1 : 0; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + status = pContextStateCoreAudio->AudioUnitSetProperty(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(status); } /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ #if defined(MA_APPLE_DESKTOP) - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); + status = pContextStateCoreAudio->AudioUnitSetProperty(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(result); } #else @@ -36053,19 +37101,19 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; if (deviceType == ma_device_type_playback) { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); + status = pContextStateCoreAudio->AudioUnitGetProperty(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); } else { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); + status = pContextStateCoreAudio->AudioUnitGetProperty(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); } if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(status); } #if defined(MA_APPLE_DESKTOP) result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return result; } @@ -36107,7 +37155,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); + status = pContextStateCoreAudio->AudioObjectSetPropertyData(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); if (status != noErr) { bestFormat.mSampleRate = origFormat.mSampleRate; } @@ -36115,7 +37163,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev bestFormat.mSampleRate = origFormat.mSampleRate; } - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); + status = pContextStateCoreAudio->AudioUnitSetProperty(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { /* We failed to set the format, so fall back to the current format of the audio unit. */ bestFormat = origFormat; @@ -36161,21 +37209,21 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev } - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); + status = pContextStateCoreAudio->AudioUnitSetProperty(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(status); } #endif result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return result; } if (pData->formatOut == ma_format_unknown) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return MA_FORMAT_NOT_SUPPORTED; } @@ -36259,9 +37307,9 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. */ - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); + status = pContextStateCoreAudio->AudioUnitSetProperty(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(status); } @@ -36274,7 +37322,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); if (pBufferList == NULL) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return MA_OUT_OF_MEMORY; } @@ -36285,35 +37333,35 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev callbackInfo.inputProcRefCon = pDevice_DoNotReference; if (deviceType == ma_device_type_playback) { callbackInfo.inputProc = ma_on_output__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); + status = pContextStateCoreAudio->AudioUnitSetProperty(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(status); } } else { callbackInfo.inputProc = ma_on_input__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); + status = pContextStateCoreAudio->AudioUnitSetProperty(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(status); } } /* We need to listen for stop events. */ if (pData->registerStopEvent) { - status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); + status = pContextStateCoreAudio->AudioUnitAddPropertyListener(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(status); } } /* Initialize the audio unit. */ - status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); + status = pContextStateCoreAudio->AudioUnitInitialize(pData->audioUnit); if (status != noErr) { ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); pData->pAudioBufferList = NULL; - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + pContextStateCoreAudio->AudioComponentInstanceDispose(pData->audioUnit); return ma_result_from_OSStatus(status); } @@ -36334,6 +37382,8 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev #if defined(MA_APPLE_DESKTOP) static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) { + ma_device_state_coreaudio* pDeviceStateCoreAudio = ma_device_get_backend_state__coreaudio(pDevice); + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(ma_device_get_context(pDevice)); ma_device_init_internal_data__coreaudio data; ma_result result; @@ -36350,15 +37400,15 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev data.sampleRateIn = pDevice->sampleRate; MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); data.shareMode = pDevice->capture.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; + data.performanceProfile = pDeviceStateCoreAudio->originalPerformanceProfile; data.registerStopEvent = MA_TRUE; if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + pContextStateCoreAudio->AudioOutputUnitStop(pDeviceStateCoreAudio->audioUnitCapture); + pContextStateCoreAudio->AudioComponentInstanceDispose(pDeviceStateCoreAudio->audioUnitCapture); } - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + if (pDeviceStateCoreAudio->pAudioBufferList) { + ma_free(pDeviceStateCoreAudio->pAudioBufferList, ma_device_get_allocation_callbacks(pDevice)); } } else if (deviceType == ma_device_type_playback) { data.formatIn = pDevice->playback.format; @@ -36366,17 +37416,17 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev data.sampleRateIn = pDevice->sampleRate; MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); data.shareMode = pDevice->playback.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; + data.performanceProfile = pDeviceStateCoreAudio->originalPerformanceProfile; data.registerStopEvent = (pDevice->type != ma_device_type_duplex); if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + pContextStateCoreAudio->AudioOutputUnitStop(pDeviceStateCoreAudio->audioUnitPlayback); + pContextStateCoreAudio->AudioComponentInstanceDispose(pDeviceStateCoreAudio->audioUnitPlayback); } } - data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->coreaudio.originalPeriods; + data.periodSizeInFramesIn = pDeviceStateCoreAudio->originalPeriodSizeInFrames; + data.periodSizeInMillisecondsIn = pDeviceStateCoreAudio->originalPeriodSizeInMilliseconds; + data.periodsIn = pDeviceStateCoreAudio->originalPeriods; /* Need at least 3 periods for duplex. */ if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { @@ -36390,12 +37440,12 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev if (deviceType == ma_device_type_capture) { #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); + pDeviceStateCoreAudio->deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; + ma_get_AudioObject_uid(pDevice->pContext, pDeviceStateCoreAudio->deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; + pDeviceStateCoreAudio->audioUnitCapture = data.audioUnit; + pDeviceStateCoreAudio->pAudioBufferList = data.pAudioBufferList; + pDeviceStateCoreAudio->audioBufferCapInFrames = data.periodSizeInFramesOut; pDevice->capture.internalFormat = data.formatOut; pDevice->capture.internalChannels = data.channelsOut; @@ -36405,10 +37455,10 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev pDevice->capture.internalPeriods = data.periodsOut; } else if (deviceType == ma_device_type_playback) { #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); + pDeviceStateCoreAudio->deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; + ma_get_AudioObject_uid(pDevice->pContext, pDeviceStateCoreAudio->deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; + pDeviceStateCoreAudio->audioUnitPlayback = data.audioUnit; pDevice->playback.internalFormat = data.formatOut; pDevice->playback.internalChannels = data.channelsOut; @@ -36422,27 +37472,39 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev } #endif /* MA_APPLE_DESKTOP */ -static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__coreaudio(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { + ma_device_state_coreaudio* pDeviceStateCoreAudio; + const ma_device_config_coreaudio* pDeviceConfigCoreAudio = (const ma_device_config_coreaudio*)pDeviceBackendConfig; + ma_device_config_coreaudio defaultConfigCoreAudio; + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result; - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); + if (pDeviceConfigCoreAudio == NULL) { + defaultConfigCoreAudio = ma_device_config_coreaudio_init(); + pDeviceConfigCoreAudio = &defaultConfigCoreAudio; + } - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* No exclusive mode with the Core Audio backend for now. */ - if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { + if (((deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || + ((deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } + pDeviceStateCoreAudio = (ma_device_state_coreaudio*)ma_calloc(sizeof(*pDeviceStateCoreAudio), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateCoreAudio == NULL) { + return MA_OUT_OF_MEMORY; + } + /* Capture needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; + data.allowNominalSampleRateChange = pDeviceConfigCoreAudio->allowNominalSampleRateChange; data.formatIn = pDescriptorCapture->format; data.channelsIn = pDescriptorCapture->channels; data.sampleRateIn = pDescriptorCapture->sampleRate; @@ -36451,64 +37513,65 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; data.periodsIn = pDescriptorCapture->periodCount; data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; + data.performanceProfile = ma_performance_profile_low_latency; data.registerStopEvent = MA_TRUE; /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { + if (data.periodsIn < 3 && deviceType == ma_device_type_duplex) { data.periodsIn = 3; } - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); + result = ma_device_init_internal__coreaudio(ma_device_get_context(pDevice), ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); if (result != MA_SUCCESS) { + ma_free(pDeviceStateCoreAudio, ma_device_get_allocation_callbacks(pDevice)); return result; } - pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); + pDeviceStateCoreAudio->isDefaultCaptureDevice = (pDescriptorCapture->pDeviceID == NULL); #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; + pDeviceStateCoreAudio->deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; + pDeviceStateCoreAudio->audioUnitCapture = data.audioUnit; + pDeviceStateCoreAudio->pAudioBufferList = data.pAudioBufferList; + pDeviceStateCoreAudio->audioBufferCapInFrames = data.periodSizeInFramesOut; + pDeviceStateCoreAudio->originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; + pDeviceStateCoreAudio->originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; + pDeviceStateCoreAudio->originalPeriods = pDescriptorCapture->periodCount; + pDeviceStateCoreAudio->originalPerformanceProfile = ma_performance_profile_low_latency; - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; + pDescriptorCapture->format = data.formatOut; + pDescriptorCapture->channels = data.channelsOut; + pDescriptorCapture->sampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; + pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; + pDescriptorCapture->periodCount = data.periodsOut; #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); + ma_get_AudioObject_uid(ma_device_get_context(pDevice), pDeviceStateCoreAudio->deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); /* If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly switch the device in the background. */ - if (pConfig->capture.pDeviceID == NULL) { + if (pDescriptorCapture->pDeviceID == NULL) { ma_device__track__coreaudio(pDevice); } #endif } /* Playback. */ - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; + data.allowNominalSampleRateChange = pDeviceConfigCoreAudio->allowNominalSampleRateChange; data.formatIn = pDescriptorPlayback->format; data.channelsIn = pDescriptorPlayback->channels; data.sampleRateIn = pDescriptorPlayback->sampleRate; MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; + data.performanceProfile = ma_performance_profile_low_latency; /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ - if (pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_duplex) { data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; data.periodsIn = pDescriptorCapture->periodCount; data.registerStopEvent = MA_FALSE; @@ -36519,26 +37582,28 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c data.registerStopEvent = MA_TRUE; } - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); + result = ma_device_init_internal__coreaudio(ma_device_get_context(pDevice), ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + if (deviceType == ma_device_type_duplex) { + pContextStateCoreAudio->AudioComponentInstanceDispose(pDeviceStateCoreAudio->audioUnitCapture); + if (pDeviceStateCoreAudio->pAudioBufferList) { + ma_free(pDeviceStateCoreAudio->pAudioBufferList, ma_device_get_allocation_callbacks(pDevice)); } } + + ma_free(pDeviceStateCoreAudio, ma_device_get_allocation_callbacks(pDevice)); return result; } - pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); + pDeviceStateCoreAudio->isDefaultPlaybackDevice = (pDescriptorPlayback->pDeviceID == NULL); #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; + pDeviceStateCoreAudio->deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; + pDeviceStateCoreAudio->audioUnitPlayback = data.audioUnit; + pDeviceStateCoreAudio->originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; + pDeviceStateCoreAudio->originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; + pDeviceStateCoreAudio->originalPeriods = pDescriptorPlayback->periodCount; + pDeviceStateCoreAudio->originalPerformanceProfile = ma_performance_profile_low_latency; pDescriptorPlayback->format = data.formatOut; pDescriptorPlayback->channels = data.channelsOut; @@ -36548,54 +37613,56 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c pDescriptorPlayback->periodCount = data.periodsOut; #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); + ma_get_AudioObject_uid(ma_device_get_context(pDevice), pDeviceStateCoreAudio->deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); /* If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly switch the device in the background. */ - if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { + if (pDescriptorPlayback->pDeviceID == NULL && (deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { ma_device__track__coreaudio(pDevice); } #endif } - - /* When stopping the device, a callback is called on another thread. We need to wait for this callback before returning from ma_device_stop(). This event is used for this. */ - ma_event_init(&pDevice->coreaudio.stopEvent); + ma_event_init(&pDeviceStateCoreAudio->stopEvent); /* We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done differently on non-Desktop Apple platforms. */ #if defined(MA_APPLE_MOBILE) - pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; + pDeviceStateCoreAudio->pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; #endif + *ppDeviceState = pDeviceStateCoreAudio; + return MA_SUCCESS; } static ma_result ma_device_start__coreaudio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_coreaudio* pDeviceStateCoreAudio = ma_device_get_backend_state__coreaudio(pDevice); + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + OSStatus status = pContextStateCoreAudio->AudioOutputUnitStart(pDeviceStateCoreAudio->audioUnitCapture); if (status != noErr) { return ma_result_from_OSStatus(status); } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + OSStatus status = pContextStateCoreAudio->AudioOutputUnitStart(pDeviceStateCoreAudio->audioUnitPlayback); if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (deviceType == ma_device_type_duplex) { + pContextStateCoreAudio->AudioOutputUnitStop(pDeviceStateCoreAudio->audioUnitCapture); } return ma_result_from_OSStatus(status); } @@ -36606,269 +37673,52 @@ static ma_result ma_device_start__coreaudio(ma_device* pDevice) static ma_result ma_device_stop__coreaudio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_coreaudio* pDeviceStateCoreAudio = ma_device_get_backend_state__coreaudio(pDevice); + ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + OSStatus status = pContextStateCoreAudio->AudioOutputUnitStop(pDeviceStateCoreAudio->audioUnitCapture); if (status != noErr) { return ma_result_from_OSStatus(status); } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + OSStatus status = pContextStateCoreAudio->AudioOutputUnitStop(pDeviceStateCoreAudio->audioUnitPlayback); if (status != noErr) { return ma_result_from_OSStatus(status); } } /* We need to wait for the callback to finish before returning. */ - ma_event_wait(&pDevice->coreaudio.stopEvent); + ma_event_wait(&pDeviceStateCoreAudio->stopEvent); return MA_SUCCESS; } -static ma_result ma_context_uninit__coreaudio(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_CoreAudio = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_coreaudio); + ma_backend_info__coreaudio, + ma_context_init__coreaudio, + ma_context_uninit__coreaudio, + ma_context_enumerate_devices__coreaudio, + ma_context_get_device_info__coreaudio, + ma_device_init__coreaudio, + ma_device_uninit__coreaudio, + ma_device_start__coreaudio, + ma_device_stop__coreaudio, + NULL, /* onDeviceRead */ + NULL, /* onDeviceWrite */ + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ +}; -#if defined(MA_APPLE_MOBILE) - if (!pContext->coreaudio.noAudioSessionDeactivate) { - if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); -#endif - -#if !defined(MA_APPLE_MOBILE) - ma_context__uninit_device_tracking__coreaudio(pContext); -#endif - - (void)pContext; - return MA_SUCCESS; -} - -#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) -static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) -{ - /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ - MA_ASSERT(category != ma_ios_session_category_default); - MA_ASSERT(category != ma_ios_session_category_none); - - switch (category) { - case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; - case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; - case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; - case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; - case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; - case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; - default: return AVAudioSessionCategoryAmbient; - } -} -#endif - -static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_APPLE_MOBILE) - ma_result result; -#endif - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_MOBILE) - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; - - MA_ASSERT(pAudioSession != NULL); - - if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { - /* - I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails - we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. - */ - #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) - options |= AVAudioSessionCategoryOptionDefaultToSpeaker; - #endif - - if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { - /* Using PlayAndRecord */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { - /* Using Playback */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { - /* Using Record */ - } else { - /* Leave as default? */ - } - } else { - if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { - #if defined(__IPHONE_12_0) - if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { - return MA_INVALID_OPERATION; /* Failed to set session category. */ - } - #else - /* Ignore the session category on version 11 and older, but post a warning. */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); - #endif - } - } - - if (!pConfig->coreaudio.noAudioSessionActivate) { - if (![pAudioSession setActive:true error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); - if (pContext->coreaudio.hCoreFoundation == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - - - pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); - if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); - pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); - - /* - It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still - defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. - The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to - AudioToolbox. - */ - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { - /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - } - - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); +ma_device_backend_vtable* ma_device_backend_coreaudio = &ma_gDeviceBackendVTable_CoreAudio; #else - pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; - pContext->coreaudio.CFRelease = (ma_proc)CFRelease; - - #if defined(MA_APPLE_DESKTOP) - pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; - pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; - pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; - pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; - pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; - #endif - - pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; - pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; - pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; - pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; - pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; - pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; - pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; - pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; - pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; - pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; - pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; -#endif - - /* Audio component. */ - { - AudioComponentDescription desc; - desc.componentType = kAudioUnitType_Output; - #if defined(MA_APPLE_DESKTOP) - desc.componentSubType = kAudioUnitSubType_HALOutput; - #else - desc.componentSubType = kAudioUnitSubType_RemoteIO; - #endif - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (pContext->coreaudio.component == NULL) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return MA_FAILED_TO_INIT_BACKEND; - } - } - -#if !defined(MA_APPLE_MOBILE) - result = ma_context__init_device_tracking__coreaudio(pContext); - if (result != MA_SUCCESS) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return result; - } -#endif - - pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; - - pCallbacks->onContextInit = ma_context_init__coreaudio; - pCallbacks->onContextUninit = ma_context_uninit__coreaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; - pCallbacks->onDeviceInit = ma_device_init__coreaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; - pCallbacks->onDeviceStart = ma_device_start__coreaudio; - pCallbacks->onDeviceStop = ma_device_stop__coreaudio; - pCallbacks->onDeviceRead = NULL; - pCallbacks->onDeviceWrite = NULL; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_coreaudio = NULL; #endif /* MA_HAS_COREAUDIO */ @@ -36952,17 +37802,136 @@ struct ma_sio_cap struct ma_sio_conf confs[MA_SIO_NCONF]; }; -typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); -typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); -typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); -typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); -typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); +typedef struct ma_sio_hdl* (* ma_sio_open_proc )(const char*, unsigned int, int); +typedef void (* ma_sio_close_proc )(struct ma_sio_hdl*); +typedef int (* ma_sio_setpar_proc )(struct ma_sio_hdl*, struct ma_sio_par*); +typedef int (* ma_sio_getpar_proc )(struct ma_sio_hdl*, struct ma_sio_par*); +typedef int (* ma_sio_getcap_proc )(struct ma_sio_hdl*, struct ma_sio_cap*); +typedef int (* ma_sio_start_proc )(struct ma_sio_hdl*); +typedef int (* ma_sio_stop_proc )(struct ma_sio_hdl*); +typedef size_t (* ma_sio_read_proc )(struct ma_sio_hdl*, void*, size_t); +typedef size_t (* ma_sio_write_proc )(struct ma_sio_hdl*, const void*, size_t); typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); +typedef struct ma_context_state_sndio +{ + ma_handle sndioSO; + ma_sio_open_proc sio_open; + ma_sio_close_proc sio_close; + ma_sio_setpar_proc sio_setpar; + ma_sio_getpar_proc sio_getpar; + ma_sio_getcap_proc sio_getcap; + ma_sio_start_proc sio_start; + ma_sio_stop_proc sio_stop; + ma_sio_read_proc sio_read; + ma_sio_write_proc sio_write; + ma_sio_initpar_proc sio_initpar; +} ma_context_state_sndio; + +typedef struct ma_device_state_sndio +{ + struct ma_sio_hdl* handlePlayback; + struct ma_sio_hdl* handleCapture; + ma_bool32 isStartedPlayback; + ma_bool32 isStartedCapture; +} ma_device_state_sndio; + + +static ma_context_state_sndio* ma_context_get_backend_state__sndio(ma_context* pContext) +{ + return (ma_context_state_sndio*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_sndio* ma_device_get_backend_state__sndio(ma_device* pDevice) +{ + return (ma_device_state_sndio*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__sndio(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "sndio"; +} + +static ma_result ma_context_init__sndio(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_sndio* pContextStateSndio; + const ma_context_config_sndio* pContextConfigSndio = (const ma_context_config_sndio*)pContextBackendConfig; + ma_context_config_sndio defaultConfigSndio; + + if (pContextConfigSndio == NULL) { + defaultConfigSndio = ma_context_config_sndio_init(); + pContextConfigSndio = &defaultConfigSndio; + } + + pContextStateSndio = (ma_context_state_sndio*)ma_calloc(sizeof(*pContextStateSndio), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateSndio == NULL) { + return MA_OUT_OF_MEMORY; + } + + #ifndef MA_NO_RUNTIME_LINKING + { + const char* libsndioNames[] = { + "libsndio.so" + }; + size_t i; + + for (i = 0; i < ma_countof(libsndioNames); ++i) { + pContextStateSndio->sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); + if (pContextStateSndio->sndioSO != NULL) { + break; + } + } + + if (pContextStateSndio->sndioSO == NULL) { + ma_free(pContextStateSndio, ma_context_get_allocation_callbacks(pContext)); + return MA_NO_BACKEND; + } + + pContextStateSndio->sio_open = (ma_sio_open_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_open"); + pContextStateSndio->sio_close = (ma_sio_close_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_close"); + pContextStateSndio->sio_setpar = (ma_sio_setpar_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_setpar"); + pContextStateSndio->sio_getpar = (ma_sio_getpar_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_getpar"); + pContextStateSndio->sio_getcap = (ma_sio_getcap_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_getcap"); + pContextStateSndio->sio_start = (ma_sio_start_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_start"); + pContextStateSndio->sio_stop = (ma_sio_stop_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_stop"); + pContextStateSndio->sio_read = (ma_sio_read_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_read"); + pContextStateSndio->sio_write = (ma_sio_write_proc )ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_write"); + pContextStateSndio->sio_initpar = (ma_sio_initpar_proc)ma_dlsym(ma_context_get_log(pContext), pContextStateSndio->sndioSO, "sio_initpar"); + } + #else + { + pContextStateSndio->sio_open = sio_open; + pContextStateSndio->sio_close = sio_close; + pContextStateSndio->sio_setpar = sio_setpar; + pContextStateSndio->sio_getpar = sio_getpar; + pContextStateSndio->sio_getcap = sio_getcap; + pContextStateSndio->sio_start = sio_start; + pContextStateSndio->sio_stop = sio_stop; + pContextStateSndio->sio_read = sio_read; + pContextStateSndio->sio_write = sio_write; + pContextStateSndio->sio_initpar = sio_initpar; + } + #endif + + *ppContextState = pContextStateSndio; + + return MA_SUCCESS; +} + +static void ma_context_uninit__sndio(ma_context* pContext) +{ + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(pContext); + + #ifndef MA_NO_RUNTIME_LINKING + { + ma_dlclose(ma_context_get_log(pContext), pContextStateSndio->sndioSO); + } + #endif +} + + static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ { ma_uint32 i; @@ -37213,17 +38182,15 @@ static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(pContext); ma_bool32 isTerminating = MA_FALSE; struct ma_sio_hdl* handle; - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */ /* Playback. */ if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); + handle = pContextStateSndio->sio_open(MA_SIO_DEVANY, MA_SIO_PLAY, 0); if (handle != NULL) { /* Supports playback. */ ma_device_info deviceInfo; @@ -37233,13 +38200,13 @@ static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_en isTerminating = !callback(ma_device_type_playback, &deviceInfo, pUserData); - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); + pContextStateSndio->sio_close(handle); } } /* Capture. */ if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); + handle = pContextStateSndio->sio_open(MA_SIO_DEVANY, MA_SIO_REC, 0); if (handle != NULL) { /* Supports capture. */ ma_device_info deviceInfo; @@ -37249,7 +38216,7 @@ static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_en isTerminating = !callback(ma_device_type_capture, &deviceInfo, pUserData); - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); + pContextStateSndio->sio_close(handle); } } @@ -37258,6 +38225,7 @@ static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_en static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(pContext); char devid[256]; struct ma_sio_hdl* handle; struct ma_sio_cap caps; @@ -37274,12 +38242,12 @@ static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_devi ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); } - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); + handle = pContextStateSndio->sio_open(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); if (handle == NULL) { return MA_NO_DEVICE; } - if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { + if (pContextStateSndio->sio_getcap(handle, &caps) == 0) { return MA_ERROR; } @@ -37349,29 +38317,15 @@ static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_devi } } - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); + pContextStateSndio->sio_close(handle); return MA_SUCCESS; } -static ma_result ma_device_uninit__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +static ma_result ma_device_init_handle__sndio(ma_device* pDevice, ma_device_descriptor* pDescriptor, ma_device_type deviceType, struct ma_sio_hdl** pHandle) { + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(ma_device_get_context(pDevice)); const char* pDeviceName; - ma_ptr handle; + struct ma_sio_hdl* handle; int openFlags = 0; struct ma_sio_cap caps; struct ma_sio_par par; @@ -37385,7 +38339,6 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; - MA_ASSERT(pConfig != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); MA_ASSERT(pDevice != NULL); @@ -37405,15 +38358,15 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic pDeviceName = pDeviceID->sndio; } - handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); + handle = pContextStateSndio->sio_open(pDeviceName, openFlags, 0); if (handle == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } /* We need to retrieve the device caps to determine the most appropriate format to use. */ - if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); + if (pContextStateSndio->sio_getcap(handle, &caps) == 0) { + pContextStateSndio->sio_close(handle); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); return MA_ERROR; } @@ -37453,11 +38406,11 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic } if (sampleRate == 0) { - sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); + sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, deviceType, format, channels); } - ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); + pContextStateSndio->sio_initpar(&par); par.msb = 0; par.le = ma_is_little_endian(); @@ -37502,19 +38455,19 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic par.rate = sampleRate; - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); + internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, ma_performance_profile_low_latency); par.round = internalPeriodSizeInFrames; par.appbufsz = par.round * pDescriptor->periodCount; - if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); + if (pContextStateSndio->sio_setpar(handle, &par) == 0) { + pContextStateSndio->sio_close(handle); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); return MA_ERROR; } - if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); + if (pContextStateSndio->sio_getpar(handle, &par) == 0) { + pContextStateSndio->sio_close(handle); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); return MA_ERROR; } @@ -37525,12 +38478,6 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic internalPeriods = par.appbufsz / par.round; internalPeriodSizeInFrames = par.round; - if (deviceType == ma_device_type_capture) { - pDevice->sndio.handleCapture = handle; - } else { - pDevice->sndio.handlePlayback = handle; - } - pDescriptor->format = internalFormat; pDescriptor->channels = internalChannels; pDescriptor->sampleRate = internalSampleRate; @@ -37538,46 +38485,85 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; pDescriptor->periodCount = internalPeriods; + *pHandle = handle; + return MA_SUCCESS; } -static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__sndio(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { - MA_ASSERT(pDevice != NULL); + ma_device_state_sndio* pDeviceStateSndio; + const ma_device_config_sndio* pDeviceConfigSndio = (ma_device_config_sndio*)pDeviceBackendConfig; + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(ma_device_get_context(pDevice)); + ma_device_config_sndio defaultConfigSndio; + ma_device_type deviceType = ma_device_get_type(pDevice); - MA_ZERO_OBJECT(&pDevice->sndio); + if (pDeviceConfigSndio == NULL) { + defaultConfigSndio = ma_device_config_sndio_init(); + pDeviceConfigSndio = &defaultConfigSndio; + } - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + pDeviceStateSndio = (ma_device_state_sndio*)ma_calloc(sizeof(*pDeviceStateSndio), ma_device_get_allocation_callbacks(pDevice)); + if (pDevice == NULL) { + return MA_OUT_OF_MEMORY; + } + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_handle__sndio(pDevice, pDescriptorCapture, ma_device_type_capture, &pDeviceStateSndio->handleCapture); if (result != MA_SUCCESS) { + ma_free(pDeviceStateSndio, ma_device_get_allocation_callbacks(pDevice)); return result; } } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_handle__sndio(pDevice, pDescriptorPlayback, ma_device_type_playback, &pDeviceStateSndio->handlePlayback); if (result != MA_SUCCESS) { + if (deviceType == ma_device_type_duplex) { + pContextStateSndio->sio_close(pDeviceStateSndio->handleCapture); + } + + ma_free(pDeviceStateSndio, ma_device_get_allocation_callbacks(pDevice)); return result; } } + *ppDeviceState = pDeviceStateSndio; + return MA_SUCCESS; } +static void ma_device_uninit__sndio(ma_device* pDevice) +{ + ma_device_state_sndio* pDeviceStateSndio = ma_device_get_backend_state__sndio(pDevice); + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStateSndio->sio_close(pDeviceStateSndio->handleCapture); + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStateSndio->sio_close(pDeviceStateSndio->handlePlayback); + } +} + static ma_result ma_device_start__sndio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_sndio* pDeviceStateSndio = ma_device_get_backend_state__sndio(pDevice); + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStateSndio->sio_start(pDeviceStateSndio->handleCapture); } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStateSndio->sio_start(pDeviceStateSndio->handlePlayback); /* <-- Doesn't actually start until data is written. */ } return MA_SUCCESS; @@ -37585,7 +38571,9 @@ static ma_result ma_device_start__sndio(ma_device* pDevice) static ma_result ma_device_stop__sndio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_sndio* pDeviceStateSndio = ma_device_get_backend_state__sndio(pDevice); + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(ma_device_get_context(pDevice)); + ma_device_type deviceType = ma_device_get_type(pDevice); /* From the documentation: @@ -37597,12 +38585,12 @@ static ma_result ma_device_stop__sndio(ma_device* pDevice) Therefore, sio_stop() performs all of the necessary draining for us. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + pContextStateSndio->sio_stop(pDeviceStateSndio->handleCapture); } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + pContextStateSndio->sio_stop(pDeviceStateSndio->handlePlayback); } return MA_SUCCESS; @@ -37610,13 +38598,15 @@ static ma_result ma_device_stop__sndio(ma_device* pDevice) static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { + ma_device_state_sndio* pDeviceStateSndio = ma_device_get_backend_state__sndio(pDevice); + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(ma_device_get_context(pDevice)); int result; if (pFramesWritten != NULL) { *pFramesWritten = 0; } - result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + result = pContextStateSndio->sio_write(pDeviceStateSndio->handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (result == 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); return MA_IO_ERROR; @@ -37631,13 +38621,15 @@ static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFram static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { + ma_device_state_sndio* pDeviceStateSndio = ma_device_get_backend_state__sndio(pDevice); + ma_context_state_sndio* pContextStateSndio = ma_context_get_backend_state__sndio(ma_device_get_context(pDevice)); int result; if (pFramesRead != NULL) { *pFramesRead = 0; } - result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + result = pContextStateSndio->sio_read(pDeviceStateSndio->handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); if (result == 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); return MA_IO_ERROR; @@ -37650,72 +38642,26 @@ static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_ return MA_SUCCESS; } -static ma_result ma_context_uninit__sndio(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_sndio = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_sndio); + ma_backend_info__sndio, + ma_context_init__sndio, + ma_context_uninit__sndio, + ma_context_enumerate_devices__sndio, + ma_context_get_device_info__sndio, + ma_device_init__sndio, + ma_device_uninit__sndio, + ma_device_start__sndio, + ma_device_stop__sndio, + ma_device_read__sndio, + ma_device_write__sndio, + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ +}; - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libsndioNames[] = { - "libsndio.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); - if (pContext->sndio.sndioSO != NULL) { - break; - } - } - - if (pContext->sndio.sndioSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar"); +ma_device_backend_vtable* ma_device_backend_sndio = &ma_gDeviceBackendVTable_sndio; #else - pContext->sndio.sio_open = sio_open; - pContext->sndio.sio_close = sio_close; - pContext->sndio.sio_setpar = sio_setpar; - pContext->sndio.sio_getpar = sio_getpar; - pContext->sndio.sio_getcap = sio_getcap; - pContext->sndio.sio_write = sio_write; - pContext->sndio.sio_read = sio_read; - pContext->sndio.sio_start = sio_start; - pContext->sndio.sio_stop = sio_stop; - pContext->sndio.sio_initpar = sio_initpar; -#endif - - pCallbacks->onContextInit = ma_context_init__sndio; - pCallbacks->onContextUninit = ma_context_uninit__sndio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; - pCallbacks->onDeviceInit = ma_device_init__sndio; - pCallbacks->onDeviceUninit = ma_device_uninit__sndio; - pCallbacks->onDeviceStart = ma_device_start__sndio; - pCallbacks->onDeviceStop = ma_device_stop__sndio; - pCallbacks->onDeviceRead = ma_device_read__sndio; - pCallbacks->onDeviceWrite = ma_device_write__sndio; - pCallbacks->onDeviceDataLoop = NULL; - - (void)pConfig; - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_sndio = NULL; #endif /* MA_HAS_SNDIO */ @@ -37745,6 +38691,65 @@ audio(4) Backend #endif #endif +typedef struct ma_context_state_audio4 +{ + int _unused; +} ma_context_state_audio4; + +typedef struct ma_device_state_audio4 +{ + int fdPlayback; + int fdCapture; +} ma_device_state_audio4; + + +static ma_context_state_audio4* ma_context_get_backend_state__audio4(ma_context* pContext) +{ + return (ma_context_state_audio4*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_audio4* ma_device_get_backend_state__audio4(ma_device* pDevice) +{ + return (ma_device_state_audio4*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__audio4(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "audio(4)"; +} + +static ma_result ma_context_init__audio4(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_audio4* pContextStateAudio4; + const ma_context_config_audio4* pContextConfigAudio4 = (const ma_context_config_audio4*)pContextBackendConfig; + ma_context_config_audio4 defaultConfigAudio4; + + if (pContextConfigAudio4 == NULL) { + defaultConfigAudio4 = ma_context_config_audio4_init(); + pContextConfigAudio4 = &defaultConfigAudio4; + } + + (void)pContextConfigAudio4; + + pContextStateAudio4 = (ma_context_state_audio4*)ma_calloc(sizeof(*pContextStateAudio4), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateAudio4 == NULL) { + return MA_OUT_OF_MEMORY; + } + + *ppContextState = pContextStateAudio4; + + return MA_SUCCESS; +} + +static void ma_context_uninit__audio4(ma_context* pContext) +{ + ma_context_state_audio4* pContextStateAudio4 = ma_context_get_backend_state__audio4(pContext); + + ma_free(pContextStateAudio4, ma_context_get_allocation_callbacks(pContext)); +} + static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) { size_t baseLen; @@ -37957,15 +38962,15 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext ma_uint32 channels; ma_uint32 sampleRate; -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) + #if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) { return MA_ERROR; } -#else + #else if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { return MA_ERROR; } -#endif + #endif if (deviceType == ma_device_type_playback) { channels = fdInfo.play.channels; @@ -38137,31 +39142,13 @@ static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_dev return result; } -static ma_result ma_device_uninit__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +static ma_result ma_device_init_fd__audio4(ma_device* pDevice, ma_device_descriptor* pDescriptor, ma_device_type deviceType, int* pFD) { const char* pDefaultDeviceNames[] = { "/dev/audio", "/dev/audio0" }; - const char* pDefaultDeviceCtlNames[] = { - "/dev/audioctl", - "/dev/audioctl0" - }; + int fd; int fdFlags = 0; size_t iDefaultDevice = (size_t)-1; @@ -38171,9 +39158,9 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; - MA_ASSERT(pConfig != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); MA_ASSERT(pDevice != NULL); + MA_ASSERT(pFD != NULL); /* The first thing to do is open the file. */ if (deviceType == ma_device_type_capture) { @@ -38214,6 +39201,10 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ { + const char* pDefaultDeviceCtlNames[] = { + "/dev/audioctl", + "/dev/audioctl0" + }; audio_info_t fdInfo; int fdInfoResult = -1; @@ -38318,7 +39309,7 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c { ma_uint32 internalPeriodSizeInBytes; - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); + internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, ma_performance_profile_low_latency); internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); if (internalPeriodSizeInBytes < 16) { @@ -38370,7 +39361,7 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c { ma_uint32 internalPeriodSizeInBytes; - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); + internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, ma_performance_profile_low_latency); /* What miniaudio calls a period, audio4 calls a block. */ internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); @@ -38408,11 +39399,7 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c return MA_FORMAT_NOT_SUPPORTED; } - if (deviceType == ma_device_type_capture) { - pDevice->audio4.fdCapture = fd; - } else { - pDevice->audio4.fdPlayback = fd; - } + *pFD = fd; pDescriptor->format = internalFormat; pDescriptor->channels = internalChannels; @@ -38424,66 +39411,94 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c return MA_SUCCESS; } -static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__audio4(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { - MA_ASSERT(pDevice != NULL); + ma_device_state_audio4* pDeviceStateAudio4; + const ma_device_config_audio4* pDeviceConfigAudio4 = (const ma_device_config_audio4*)pDeviceBackendConfig; + ma_device_config_audio4 defaultConfigAudio4; + ma_device_type deviceType = ma_device_get_type(pDevice); - MA_ZERO_OBJECT(&pDevice->audio4); - - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } - pDevice->audio4.fdCapture = -1; - pDevice->audio4.fdPlayback = -1; + if (pDeviceConfigAudio4 == NULL) { + defaultConfigAudio4 = ma_device_config_audio4_init(); + pDeviceConfigAudio4 = &defaultConfigAudio4; + } + + pDeviceStateAudio4 = (ma_device_state_audio4*)ma_calloc(sizeof(*pDeviceStateAudio4), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateAudio4 == NULL) { + return MA_OUT_OF_MEMORY; + } + + pDeviceStateAudio4->fdCapture = -1; + pDeviceStateAudio4->fdPlayback = -1; /* - The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD + The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD 8 introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as I'm aware. */ #if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 /* NetBSD 8.0+ */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + if (((deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } #else /* All other flavors. */ #endif - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__audio4(pDevice, pDescriptorCapture, ma_device_type_capture, &pDeviceStateAudio4->fdCapture); if (result != MA_SUCCESS) { return result; } } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__audio4(pDevice, pDescriptorPlayback, ma_device_type_playback, &pDeviceStateAudio4->fdPlayback); if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); + if (deviceType == ma_device_type_duplex) { + close(pDeviceStateAudio4->fdCapture); } return result; } } + *ppDeviceState = pDeviceStateAudio4; + return MA_SUCCESS; } +static void ma_device_uninit__audio4(ma_device* pDevice) +{ + ma_device_state_audio4* pDeviceStateAudio4 = ma_device_get_backend_state__audio4(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + close(pDeviceStateAudio4->fdCapture); + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + close(pDeviceStateAudio4->fdPlayback); + } +} + static ma_result ma_device_start__audio4(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_audio4* pDeviceStateAudio4 = ma_device_get_backend_state__audio4(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdCapture == -1) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + if (pDeviceStateAudio4->fdCapture == -1) { return MA_INVALID_ARGS; } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdPlayback == -1) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + if (pDeviceStateAudio4->fdPlayback == -1) { return MA_INVALID_ARGS; } } @@ -38514,27 +39529,28 @@ static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) static ma_result ma_device_stop__audio4(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_audio4* pDeviceStateAudio4 = ma_device_get_backend_state__audio4(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { ma_result result; - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); + result = ma_device_stop_fd__audio4(pDevice, pDeviceStateAudio4->fdCapture); if (result != MA_SUCCESS) { return result; } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_result result; /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ #if !defined(MA_AUDIO4_USE_NEW_API) - ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); + ioctl(pDeviceStateAudio4->fdPlayback, AUDIO_DRAIN, 0); #endif /* Here is where the device is stopped immediately. */ - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); + result = ma_device_stop_fd__audio4(pDevice, pDeviceStateAudio4->fdPlayback); if (result != MA_SUCCESS) { return result; } @@ -38545,13 +39561,14 @@ static ma_result ma_device_stop__audio4(ma_device* pDevice) static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { + ma_device_state_audio4* pDeviceStateAudio4 = ma_device_get_backend_state__audio4(pDevice); int result; if (pFramesWritten != NULL) { *pFramesWritten = 0; } - result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + result = write(pDeviceStateAudio4->fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (result < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); return ma_result_from_errno(errno); @@ -38566,13 +39583,14 @@ static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFra static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { + ma_device_state_audio4* pDeviceStateAudio4 = ma_device_get_backend_state__audio4(pDevice); int result; if (pFramesRead != NULL) { *pFramesRead = 0; } - result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + result = read(pDeviceStateAudio4->fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); if (result < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); return ma_result_from_errno(errno); @@ -38585,35 +39603,26 @@ static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma return MA_SUCCESS; } -static ma_result ma_context_uninit__audio4(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_Audio4 = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_audio4); + ma_backend_info__audio4, + ma_context_init__audio4, + ma_context_uninit__audio4, + ma_context_enumerate_devices__audio4, + ma_context_get_device_info__audio4, + ma_device_init__audio4, + ma_device_uninit__audio4, + ma_device_start__audio4, + ma_device_stop__audio4, + ma_device_read__audio4, + ma_device_write__audio4, + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ +}; - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pCallbacks->onContextInit = ma_context_init__audio4; - pCallbacks->onContextUninit = ma_context_uninit__audio4; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; - pCallbacks->onDeviceInit = ma_device_init__audio4; - pCallbacks->onDeviceUninit = ma_device_uninit__audio4; - pCallbacks->onDeviceStart = ma_device_start__audio4; - pCallbacks->onDeviceStop = ma_device_stop__audio4; - pCallbacks->onDeviceRead = ma_device_read__audio4; - pCallbacks->onDeviceWrite = ma_device_write__audio4; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_audio4 = &ma_gDeviceBackendVTable_Audio4; +#else +ma_device_backend_vtable* ma_device_backend_audio4 = NULL; #endif /* MA_HAS_AUDIO4 */ @@ -38634,7 +39643,29 @@ OSS Backend #define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" -static int ma_open_temp_device__oss() +typedef struct ma_context_state_oss +{ + int versionMajor; + int versionMinor; +} ma_context_state_oss; + +typedef struct ma_device_state_oss +{ + int fdPlayback; + int fdCapture; +} ma_device_state_oss; + +static ma_context_state_oss* ma_context_get_backend_state__oss(ma_context* pContext) +{ + return (ma_context_state_oss*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_oss* ma_device_get_backend_state__oss(ma_device* pDevice) +{ + return (ma_device_state_oss*)ma_device_get_backend_state(pDevice); +} + +static int ma_open_temp_device__oss(void) { /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ int fd = open("/dev/mixer", O_RDONLY, 0); @@ -38645,6 +39676,68 @@ static int ma_open_temp_device__oss() return -1; } +static void ma_backend_info__oss(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "OSS"; +} +static ma_result ma_context_init__oss(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_oss* pContextStateOSS; + const ma_context_config_oss* pContextConfigOSS = (const ma_context_config_oss*)pContextBackendConfig; + ma_context_config_oss defaultConfigOSS; + int fd; + int ossVersion; + int result; + + if (pContextConfigOSS == NULL) { + defaultConfigOSS = ma_context_config_oss_init(); + pContextConfigOSS = &defaultConfigOSS; + } + + (void)pContextConfigOSS; + + pContextStateOSS = (ma_context_state_oss*)ma_calloc(sizeof(*pContextStateOSS), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateOSS == NULL) { + return MA_OUT_OF_MEMORY; + } + + /* Try opening a temporary device first so we can get version information. This is closed at the end. */ + fd = ma_open_temp_device__oss(); + if (fd == -1) { + ma_free(pContextStateOSS, ma_context_get_allocation_callbacks(pContext)); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ + return MA_NO_BACKEND; + } + + /* Grab the OSS version. */ + ossVersion = 0; + result = ioctl(fd, OSS_GETVERSION, &ossVersion); + if (result == -1) { + close(fd); + ma_free(pContextStateOSS, ma_context_get_allocation_callbacks(pContext)); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); + return MA_NO_BACKEND; + } + + /* The file handle to temp device is no longer needed. Close ASAP. */ + close(fd); + + pContextStateOSS->versionMajor = ((ossVersion & 0xFF0000) >> 16); + pContextStateOSS->versionMinor = ((ossVersion & 0x00FF00) >> 8); + + *ppContextState = pContextStateOSS; + + return MA_SUCCESS; +} + +static void ma_context_uninit__oss(ma_context* pContext) +{ + ma_context_state_oss* pContextStateOSS = ma_context_get_backend_state__oss(pContext); + + ma_free(pContextStateOSS, ma_context_get_allocation_callbacks(pContext)); +} + static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) { const char* deviceName; @@ -38895,20 +39988,6 @@ static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device return MA_SUCCESS; } -static ma_result ma_device_uninit__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdPlayback); - } - - return MA_SUCCESS; -} static int ma_format_to_oss(ma_format format) { @@ -38948,7 +40027,7 @@ static ma_format ma_format_from_oss(int ossFormat) return ma_format_unknown; } -static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config_oss* pDeviceConfigOSS, ma_device_descriptor* pDescriptor, ma_device_type deviceType, int* pFD) { ma_result result; int ossResult; @@ -38961,7 +40040,8 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf int ossFragment; MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDeviceConfigOSS != NULL); + MA_ASSERT(pFD != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); pDeviceID = pDescriptor->pDeviceID; @@ -38970,7 +40050,7 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; - result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); + result = ma_context_open_device__oss(ma_device_get_context(pDevice), deviceType, pDeviceID, shareMode, &fd); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); return result; @@ -39021,7 +40101,7 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf ma_uint32 periodSizeInBytes; ma_uint32 ossFragmentSizePower; - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, ma_performance_profile_low_latency); periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); if (periodSizeInBytes < 16) { @@ -39034,7 +40114,7 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf ossFragmentSizePower += 1; } - ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); + ossFragment = (int)((pDescriptor->periodCount << 16) | ossFragmentSizePower); ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); if (ossResult == -1) { close(fd); @@ -39043,13 +40123,6 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf } } - /* Internal settings. */ - if (deviceType == ma_device_type_capture) { - pDevice->oss.fdCapture = fd; - } else { - pDevice->oss.fdPlayback = fd; - } - pDescriptor->format = ma_format_from_oss(ossFormat); pDescriptor->channels = ossChannels; pDescriptor->sampleRate = ossSampleRate; @@ -39058,43 +40131,73 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); if (pDescriptor->format == ma_format_unknown) { + close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); return MA_FORMAT_NOT_SUPPORTED; } + *pFD = fd; + return MA_SUCCESS; } -static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__oss(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); + ma_device_state_oss* pDeviceStateOSS; + const ma_device_config_oss* pDeviceConfigOSS = (const ma_device_config_oss*)pDeviceBackendConfig; + ma_device_config_oss defaultConfigOSS; + ma_device_type deviceType = ma_device_get_type(pDevice); - MA_ZERO_OBJECT(&pDevice->oss); + if (pDeviceConfigOSS == NULL) { + defaultConfigOSS = ma_device_config_oss_init(); + pDeviceConfigOSS = &defaultConfigOSS; + } - if (pConfig->deviceType == ma_device_type_loopback) { + pDeviceStateOSS = (ma_device_state_oss*)ma_calloc(sizeof(*pDeviceStateOSS), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateOSS == NULL) { + return MA_OUT_OF_MEMORY; + } + + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__oss(pDevice, pDeviceConfigOSS, pDescriptorCapture, ma_device_type_capture, &pDeviceStateOSS->fdCapture); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); return result; } } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__oss(pDevice, pDeviceConfigOSS, pDescriptorPlayback, ma_device_type_playback, &pDeviceStateOSS->fdPlayback); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); return result; } } + *ppDeviceState = pDeviceStateOSS; + return MA_SUCCESS; } +static void ma_device_uninit__oss(ma_device* pDevice) +{ + ma_device_state_oss* pDeviceStateOSS = ma_device_get_backend_state__oss(pDevice); + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + close(pDeviceStateOSS->fdCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + close(pDeviceStateOSS->fdPlayback); + } +} + /* Note on Starting and Stopping ============================= @@ -39136,6 +40239,7 @@ static ma_result ma_device_stop__oss(ma_device* pDevice) static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { + ma_device_state_oss* pDeviceStateOSS = ma_device_get_backend_state__oss(pDevice); int resultOSS; ma_uint32 deviceState; @@ -39149,7 +40253,7 @@ static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames return MA_SUCCESS; } - resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + resultOSS = write(pDeviceStateOSS->fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (resultOSS < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); return ma_result_from_errno(errno); @@ -39164,6 +40268,7 @@ static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { + ma_device_state_oss* pDeviceStateOSS = ma_device_get_backend_state__oss(pDevice); int resultOSS; ma_uint32 deviceState; @@ -39177,7 +40282,7 @@ static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_ui return MA_SUCCESS; } - resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + resultOSS = read(pDeviceStateOSS->fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); if (resultOSS < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); return ma_result_from_errno(errno); @@ -39190,61 +40295,26 @@ static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_ui return MA_SUCCESS; } -static ma_result ma_context_uninit__oss(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_OSS = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_oss); + ma_backend_info__oss, + ma_context_init__oss, + ma_context_uninit__oss, + ma_context_enumerate_devices__oss, + ma_context_get_device_info__oss, + ma_device_init__oss, + ma_device_uninit__oss, + ma_device_start__oss, + ma_device_stop__oss, + ma_device_read__oss, + ma_device_write__oss, + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ +}; - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int fd; - int ossVersion; - int result; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - /* Try opening a temporary device first so we can get version information. This is closed at the end. */ - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ - return MA_NO_BACKEND; - } - - /* Grab the OSS version. */ - ossVersion = 0; - result = ioctl(fd, OSS_GETVERSION, &ossVersion); - if (result == -1) { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); - return MA_NO_BACKEND; - } - - /* The file handle to temp device is no longer needed. Close ASAP. */ - close(fd); - - pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); - pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); - - pCallbacks->onContextInit = ma_context_init__oss; - pCallbacks->onContextUninit = ma_context_uninit__oss; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; - pCallbacks->onDeviceInit = ma_device_init__oss; - pCallbacks->onDeviceUninit = ma_device_uninit__oss; - pCallbacks->onDeviceStart = ma_device_start__oss; - pCallbacks->onDeviceStop = ma_device_stop__oss; - pCallbacks->onDeviceRead = ma_device_read__oss; - pCallbacks->onDeviceWrite = ma_device_write__oss; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_oss = &ma_gDeviceBackendVTable_OSS; +#else +ma_device_backend_vtable* ma_device_backend_oss = NULL; #endif /* MA_HAS_OSS */ @@ -39356,38 +40426,38 @@ typedef struct ma_AAudioStream_t* ma_AAudioStream; #define MA_AAUDIO_CALLBACK_RESULT_STOP 1 -typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); +typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback )(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); -typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); -typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); -typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); -typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); -typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); -typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); -typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); +typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder )(ma_AAudioStreamBuilder** ppBuilder); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete )(ma_AAudioStreamBuilder* pBuilder); +typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId )(ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); +typedef void (* MA_PFN_AAudioStreamBuilder_setDirection )(ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); +typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode )(ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); +typedef void (* MA_PFN_AAudioStreamBuilder_setFormat )(ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); +typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount )(ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); +typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate )(ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); -typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); -typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); -typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); -typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); +typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback )(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); +typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback )(ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); +typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback )(ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); +typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode )(ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); +typedef void (* MA_PFN_AAudioStreamBuilder_setUsage )(ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); +typedef void (* MA_PFN_AAudioStreamBuilder_setContentType )(ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); +typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset )(ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); +typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy )(ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream )(ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close )(ma_AAudioStream* pStream); +typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState )(ma_AAudioStream* pStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange )(ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); +typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat )(ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getChannelCount )(ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getSampleRate )(ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames )(ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback )(ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst )(ma_AAudioStream* pStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart )(ma_AAudioStream* pStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop )(ma_AAudioStream* pStream); static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) { @@ -39465,35 +40535,252 @@ static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(m return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; } +static ma_aaudio_performance_mode_t ma_to_performance_mode__aaudio(ma_aaudio_performance_mode performanceMode) +{ + switch (performanceMode) { + case ma_aaudio_performance_mode_none: return MA_AAUDIO_PERFORMANCE_MODE_NONE; + case ma_aaudio_performance_mode_power_saving: return MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING; + case ma_aaudio_performance_mode_low_latency: return MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; + default: break; + } + + return MA_AAUDIO_PERFORMANCE_MODE_NONE; +} + + +typedef struct ma_context_state_aaudio +{ + ma_handle hAAudio; /* libaaudio.so */ + MA_PFN_AAudio_createStreamBuilder AAudio_createStreamBuilder; + MA_PFN_AAudioStreamBuilder_delete AAudioStreamBuilder_delete; + MA_PFN_AAudioStreamBuilder_setDeviceId AAudioStreamBuilder_setDeviceId; + MA_PFN_AAudioStreamBuilder_setDirection AAudioStreamBuilder_setDirection; + MA_PFN_AAudioStreamBuilder_setSharingMode AAudioStreamBuilder_setSharingMode; + MA_PFN_AAudioStreamBuilder_setFormat AAudioStreamBuilder_setFormat; + MA_PFN_AAudioStreamBuilder_setChannelCount AAudioStreamBuilder_setChannelCount; + MA_PFN_AAudioStreamBuilder_setSampleRate AAudioStreamBuilder_setSampleRate; + MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames AAudioStreamBuilder_setBufferCapacityInFrames; + MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback AAudioStreamBuilder_setFramesPerDataCallback; + MA_PFN_AAudioStreamBuilder_setDataCallback AAudioStreamBuilder_setDataCallback; + MA_PFN_AAudioStreamBuilder_setErrorCallback AAudioStreamBuilder_setErrorCallback; + MA_PFN_AAudioStreamBuilder_setPerformanceMode AAudioStreamBuilder_setPerformanceMode; + MA_PFN_AAudioStreamBuilder_setUsage AAudioStreamBuilder_setUsage; + MA_PFN_AAudioStreamBuilder_setContentType AAudioStreamBuilder_setContentType; + MA_PFN_AAudioStreamBuilder_setInputPreset AAudioStreamBuilder_setInputPreset; + MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy AAudioStreamBuilder_setAllowedCapturePolicy; + MA_PFN_AAudioStreamBuilder_openStream AAudioStreamBuilder_openStream; + MA_PFN_AAudioStream_close AAudioStream_close; + MA_PFN_AAudioStream_getState AAudioStream_getState; + MA_PFN_AAudioStream_waitForStateChange AAudioStream_waitForStateChange; + MA_PFN_AAudioStream_getFormat AAudioStream_getFormat; + MA_PFN_AAudioStream_getChannelCount AAudioStream_getChannelCount; + MA_PFN_AAudioStream_getSampleRate AAudioStream_getSampleRate; + MA_PFN_AAudioStream_getBufferCapacityInFrames AAudioStream_getBufferCapacityInFrames; + MA_PFN_AAudioStream_getFramesPerDataCallback AAudioStream_getFramesPerDataCallback; + MA_PFN_AAudioStream_getFramesPerBurst AAudioStream_getFramesPerBurst; + MA_PFN_AAudioStream_requestStart AAudioStream_requestStart; + MA_PFN_AAudioStream_requestStop AAudioStream_requestStop; + ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ +} ma_context_state_aaudio; + +typedef struct ma_device_state_aaudio +{ + ma_AAudioStream* pStreamPlayback; + ma_AAudioStream* pStreamCapture; + ma_mutex rerouteLock; + ma_atomic_bool32 isTearingDown; + ma_aaudio_usage usage; + ma_aaudio_content_type contentType; + ma_aaudio_input_preset inputPreset; + ma_aaudio_allowed_capture_policy allowedCapturePolicy; + ma_bool32 noAutoStartAfterReroute; +} ma_device_state_aaudio; + + +static ma_context_state_aaudio* ma_context_get_backend_state__aaudio(ma_context* pContext) +{ + return (ma_context_state_aaudio*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_aaudio* ma_device_get_backend_state__aaudio(ma_device* pDevice) +{ + return (ma_device_state_aaudio*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__aaudio(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "AAudio"; +} + +static ma_result ma_context_init__aaudio(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_aaudio* pContextStateAAudio; + const ma_context_config_aaudio* pContextConfigAAudio = (const ma_context_config_aaudio*)pContextBackendConfig; + ma_context_config_aaudio defaultConfigAAudio; + + if (pContextConfigAAudio == NULL) { + defaultConfigAAudio = ma_context_config_aaudio_init(); + pContextConfigAAudio = &defaultConfigAAudio; + } + + (void)pContextConfigAAudio; + + pContextStateAAudio = (ma_context_state_aaudio*)ma_calloc(sizeof(*pContextStateAAudio), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateAAudio == NULL) { + return MA_OUT_OF_MEMORY; + } + + #if !defined(MA_NO_RUNTIME_LINKING) + { + size_t i; + const char* libNames[] = { + "libaaudio.so" + }; + + for (i = 0; i < ma_countof(libNames); ++i) { + pContextStateAAudio->hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); + if (pContextStateAAudio->hAAudio != NULL) { + break; + } + } + + if (pContextStateAAudio->hAAudio == NULL) { + ma_free(pContextStateAAudio, ma_context_get_allocation_callbacks(pContext)); + return MA_FAILED_TO_INIT_BACKEND; + } + + pContextStateAAudio->AAudio_createStreamBuilder = (MA_PFN_AAudio_createStreamBuilder )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudio_createStreamBuilder"); + pContextStateAAudio->AAudioStreamBuilder_delete = (MA_PFN_AAudioStreamBuilder_delete )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_delete"); + pContextStateAAudio->AAudioStreamBuilder_setDeviceId = (MA_PFN_AAudioStreamBuilder_setDeviceId )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setDeviceId"); + pContextStateAAudio->AAudioStreamBuilder_setDirection = (MA_PFN_AAudioStreamBuilder_setDirection )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setDirection"); + pContextStateAAudio->AAudioStreamBuilder_setSharingMode = (MA_PFN_AAudioStreamBuilder_setSharingMode )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setSharingMode"); + pContextStateAAudio->AAudioStreamBuilder_setFormat = (MA_PFN_AAudioStreamBuilder_setFormat )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setFormat"); + pContextStateAAudio->AAudioStreamBuilder_setChannelCount = (MA_PFN_AAudioStreamBuilder_setChannelCount )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setChannelCount"); + pContextStateAAudio->AAudioStreamBuilder_setSampleRate = (MA_PFN_AAudioStreamBuilder_setSampleRate )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setSampleRate"); + pContextStateAAudio->AAudioStreamBuilder_setBufferCapacityInFrames = (MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); + pContextStateAAudio->AAudioStreamBuilder_setFramesPerDataCallback = (MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); + pContextStateAAudio->AAudioStreamBuilder_setDataCallback = (MA_PFN_AAudioStreamBuilder_setDataCallback )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setDataCallback"); + pContextStateAAudio->AAudioStreamBuilder_setErrorCallback = (MA_PFN_AAudioStreamBuilder_setErrorCallback )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setErrorCallback"); + pContextStateAAudio->AAudioStreamBuilder_setPerformanceMode = (MA_PFN_AAudioStreamBuilder_setPerformanceMode )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setPerformanceMode"); + pContextStateAAudio->AAudioStreamBuilder_setUsage = (MA_PFN_AAudioStreamBuilder_setUsage )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setUsage"); + pContextStateAAudio->AAudioStreamBuilder_setContentType = (MA_PFN_AAudioStreamBuilder_setContentType )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setContentType"); + pContextStateAAudio->AAudioStreamBuilder_setInputPreset = (MA_PFN_AAudioStreamBuilder_setInputPreset )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setInputPreset"); + pContextStateAAudio->AAudioStreamBuilder_setAllowedCapturePolicy = (MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); + pContextStateAAudio->AAudioStreamBuilder_openStream = (MA_PFN_AAudioStreamBuilder_openStream )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStreamBuilder_openStream"); + pContextStateAAudio->AAudioStream_close = (MA_PFN_AAudioStream_close )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_close"); + pContextStateAAudio->AAudioStream_getState = (MA_PFN_AAudioStream_getState )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_getState"); + pContextStateAAudio->AAudioStream_waitForStateChange = (MA_PFN_AAudioStream_waitForStateChange )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_waitForStateChange"); + pContextStateAAudio->AAudioStream_getFormat = (MA_PFN_AAudioStream_getFormat )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_getFormat"); + pContextStateAAudio->AAudioStream_getChannelCount = (MA_PFN_AAudioStream_getChannelCount )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_getChannelCount"); + pContextStateAAudio->AAudioStream_getSampleRate = (MA_PFN_AAudioStream_getSampleRate )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_getSampleRate"); + pContextStateAAudio->AAudioStream_getBufferCapacityInFrames = (MA_PFN_AAudioStream_getBufferCapacityInFrames )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_getBufferCapacityInFrames"); + pContextStateAAudio->AAudioStream_getFramesPerDataCallback = (MA_PFN_AAudioStream_getFramesPerDataCallback )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_getFramesPerDataCallback"); + pContextStateAAudio->AAudioStream_getFramesPerBurst = (MA_PFN_AAudioStream_getFramesPerBurst )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_getFramesPerBurst"); + pContextStateAAudio->AAudioStream_requestStart = (MA_PFN_AAudioStream_requestStart )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_requestStart"); + pContextStateAAudio->AAudioStream_requestStop = (MA_PFN_AAudioStream_requestStop )ma_dlsym(ma_context_get_log(pContext), pContextStateAAudio->hAAudio, "AAudioStream_requestStop"); + } + #else + { + pContextStateAAudio->AAudio_createStreamBuilder = AAudio_createStreamBuilder; + pContextStateAAudio->AAudioStreamBuilder_delete = AAudioStreamBuilder_delete; + pContextStateAAudio->AAudioStreamBuilder_setDeviceId = AAudioStreamBuilder_setDeviceId; + pContextStateAAudio->AAudioStreamBuilder_setDirection = AAudioStreamBuilder_setDirection; + pContextStateAAudio->AAudioStreamBuilder_setSharingMode = AAudioStreamBuilder_setSharingMode; + pContextStateAAudio->AAudioStreamBuilder_setFormat = AAudioStreamBuilder_setFormat; + pContextStateAAudio->AAudioStreamBuilder_setChannelCount = AAudioStreamBuilder_setChannelCount; + pContextStateAAudio->AAudioStreamBuilder_setSampleRate = AAudioStreamBuilder_setSampleRate; + pContextStateAAudio->AAudioStreamBuilder_setBufferCapacityInFrames = AAudioStreamBuilder_setBufferCapacityInFrames; + pContextStateAAudio->AAudioStreamBuilder_setFramesPerDataCallback = AAudioStreamBuilder_setFramesPerDataCallback; + pContextStateAAudio->AAudioStreamBuilder_setDataCallback = AAudioStreamBuilder_setDataCallback; + pContextStateAAudio->AAudioStreamBuilder_setErrorCallback = AAudioStreamBuilder_setErrorCallback; + pContextStateAAudio->AAudioStreamBuilder_setPerformanceMode = AAudioStreamBuilder_setPerformanceMode; + pContextStateAAudio->AAudioStreamBuilder_setUsage = AAudioStreamBuilder_setUsage; + pContextStateAAudio->AAudioStreamBuilder_setContentType = AAudioStreamBuilder_setContentType; + pContextStateAAudio->AAudioStreamBuilder_setInputPreset = AAudioStreamBuilder_setInputPreset; + #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29 + pContextStateAAudio->AAudioStreamBuilder_setAllowedCapturePolicy = AAudioStreamBuilder_setAllowedCapturePolicy; + #endif + pContextStateAAudio->AAudioStreamBuilder_openStream = AAudioStreamBuilder_openStream; + pContextStateAAudio->AAudioStream_close = AAudioStream_close; + pContextStateAAudio->AAudioStream_getState = AAudioStream_getState; + pContextStateAAudio->AAudioStream_waitForStateChange = AAudioStream_waitForStateChange; + pContextStateAAudio->AAudioStream_getFormat = AAudioStream_getFormat; + pContextStateAAudio->AAudioStream_getChannelCount = AAudioStream_getChannelCount; + pContextStateAAudio->AAudioStream_getSampleRate = AAudioStream_getSampleRate; + pContextStateAAudio->AAudioStream_getBufferCapacityInFrames = AAudioStream_getBufferCapacityInFrames; + pContextStateAAudio->AAudioStream_getFramesPerDataCallback = AAudioStream_getFramesPerDataCallback; + pContextStateAAudio->AAudioStream_getFramesPerBurst = AAudioStream_getFramesPerBurst; + pContextStateAAudio->AAudioStream_requestStart = AAudioStream_requestStart; + pContextStateAAudio->AAudioStream_requestStop = AAudioStream_requestStop; + } + #endif + + /* We need a job thread so we can deal with rerouting. */ + { + ma_result result; + ma_device_job_thread_config jobThreadConfig; + + jobThreadConfig = ma_device_job_thread_config_init(); + + result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContextStateAAudio->jobThread); + if (result != MA_SUCCESS) { + ma_free(pContextStateAAudio, ma_context_get_allocation_callbacks(pContext)); + ma_dlclose(ma_context_get_log(pContext), pContextStateAAudio->hAAudio); + return result; + } + } + + *ppContextState = pContextStateAAudio; + + return MA_SUCCESS; +} + +static void ma_context_uninit__aaudio(ma_context* pContext) +{ + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(pContext); + + ma_device_job_thread_uninit(&pContextStateAAudio->jobThread, &pContext->allocationCallbacks); + + #if !defined(MA_NO_RUNTIME_LINKING) + { + ma_dlclose(ma_context_get_log(pContext), pContextStateAAudio->hAAudio); + } + #endif + + ma_free(pContextStateAAudio, ma_context_get_allocation_callbacks(pContext)); +} + static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) { + ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_aaudio* pDeviceStateAAudio = ma_device_get_backend_state__aaudio(pDevice); + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(ma_device_get_context(pDevice)); ma_result result; ma_job job; - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - (void)error; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, pContextStateAAudio->AAudioStream_getState(pStream)); /* When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely. */ - if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) { + if (ma_atomic_bool32_get(&pDeviceStateAAudio->isTearingDown)) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Tearing down device.\n"); } else { job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); job.data.device.aaudio.reroute.pDevice = pDevice; - if (pStream == pDevice->aaudio.pStreamCapture) { + if (pStream == pDeviceStateAAudio->pStreamCapture) { job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; } else { job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; } - result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); + result = ma_device_job_thread_post(&pContextStateAAudio->jobThread, &job); if (result != MA_SUCCESS) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); return; @@ -39532,41 +40819,42 @@ static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; } -static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) +static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_config_aaudio* pDeviceConfigAAudio, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) { + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(pContext); ma_AAudioStreamBuilder* pBuilder; ma_aaudio_result_t resultAA; /* Safety. */ *ppBuilder = NULL; - resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); + resultAA = pContextStateAAudio->AAudio_createStreamBuilder(&pBuilder); if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } if (pDeviceID != NULL) { - ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); + pContextStateAAudio->AAudioStreamBuilder_setDeviceId(pBuilder, pDeviceID->aaudio); } - ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); - ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); + pContextStateAAudio->AAudioStreamBuilder_setDirection(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); + pContextStateAAudio->AAudioStreamBuilder_setSharingMode(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ if (pDescriptor != NULL) { - MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ + MA_ASSERT(pDeviceConfigAAudio != NULL); if (pDescriptor->sampleRate != 0) { - ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); + pContextStateAAudio->AAudioStreamBuilder_setSampleRate(pBuilder, pDescriptor->sampleRate); } if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); + pContextStateAAudio->AAudioStreamBuilder_setChannelCount(pBuilder, pDescriptor->channels); } if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); + pContextStateAAudio->AAudioStreamBuilder_setFormat(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); } @@ -39577,7 +40865,7 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* can still be set if it's explicitly requested via the aaudio.allowSetBufferCapacity variable in the device config. */ - if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) { + if ((!pDeviceConfigAAudio->enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pDeviceConfigAAudio->allowSetBufferCapacity) { /* AAudio is annoying when it comes to its buffer calculation stuff because it doesn't let you retrieve the actual sample rate until after you've opened the stream. But you need to configure @@ -39585,44 +40873,42 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. */ - ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; + ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, ma_performance_profile_low_latency) * pDescriptor->periodCount; - ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); - ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); + pContextStateAAudio->AAudioStreamBuilder_setBufferCapacityInFrames(pBuilder, bufferCapacityInFrames); + pContextStateAAudio->AAudioStreamBuilder_setFramesPerDataCallback(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); } if (deviceType == ma_device_type_capture) { - if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { - ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); + if (pDeviceConfigAAudio->inputPreset != ma_aaudio_input_preset_default && pContextStateAAudio->AAudioStreamBuilder_setInputPreset != NULL) { + pContextStateAAudio->AAudioStreamBuilder_setInputPreset(pBuilder, ma_to_input_preset__aaudio(pDeviceConfigAAudio->inputPreset)); } - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); + pContextStateAAudio->AAudioStreamBuilder_setDataCallback(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); } else { - if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { - ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); + if (pDeviceConfigAAudio->usage != ma_aaudio_usage_default && pContextStateAAudio->AAudioStreamBuilder_setUsage != NULL) { + pContextStateAAudio->AAudioStreamBuilder_setUsage(pBuilder, ma_to_usage__aaudio(pDeviceConfigAAudio->usage)); } - if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { - ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); + if (pDeviceConfigAAudio->contentType != ma_aaudio_content_type_default && pContextStateAAudio->AAudioStreamBuilder_setContentType != NULL) { + pContextStateAAudio->AAudioStreamBuilder_setContentType(pBuilder, ma_to_content_type__aaudio(pDeviceConfigAAudio->contentType)); } - if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { - ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy)); + if (pDeviceConfigAAudio->allowedCapturePolicy != ma_aaudio_allow_capture_default && pContextStateAAudio->AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { + pContextStateAAudio->AAudioStreamBuilder_setAllowedCapturePolicy(pBuilder, ma_to_allowed_capture_policy__aaudio(pDeviceConfigAAudio->allowedCapturePolicy)); } - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); + pContextStateAAudio->AAudioStreamBuilder_setDataCallback(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); } - /* - If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path). - Since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let's use it. - Beware though, with a conservative performance profile, AAudio will indeed take the legacy path. - */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); + /* If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path). Beware though, with a conservative performance profile, AAudio will indeed take the legacy path. */ + if (pDeviceConfigAAudio->performanceMode != ma_aaudio_performance_mode_default) { + pContextStateAAudio->AAudioStreamBuilder_setPerformanceMode(pBuilder, ma_to_performance_mode__aaudio(pDeviceConfigAAudio->performanceMode)); + } /* We need to set an error callback to detect device changes. */ if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ - ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); + pContextStateAAudio->AAudioStreamBuilder_setErrorCallback(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); } } @@ -39633,33 +40919,35 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) { + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(pContext); ma_result result; - result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); - ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); + result = ma_result_from_aaudio(pContextStateAAudio->AAudioStreamBuilder_openStream(pBuilder, ppStream)); + pContextStateAAudio->AAudioStreamBuilder_delete(pBuilder); return result; } static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) { + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(pContext); ma_result result; ma_AAudioStreamBuilder* pBuilder; *ppStream = NULL; - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); + result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, NULL, pDeviceID, deviceType, shareMode, NULL, NULL, &pBuilder); if (result != MA_SUCCESS) { return result; } /* Let's give AAudio a hint to avoid the legacy path (AudioStreamLegacy). */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); + pContextStateAAudio->AAudioStreamBuilder_setPerformanceMode(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); } -static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) +static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config_aaudio* pDeviceConfigAAudio, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) { ma_result result; ma_AAudioStreamBuilder* pBuilder; @@ -39670,21 +40958,23 @@ static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_conf *ppStream = NULL; - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); + result = ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_device_get_context(pDevice), pDeviceConfigAAudio, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pDevice, &pBuilder); if (result != MA_SUCCESS) { return result; } - return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); + return ma_open_stream_and_close_builder__aaudio(ma_device_get_context(pDevice), pBuilder, ppStream); } static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) { + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(pContext); + if (pStream == NULL) { return MA_INVALID_ARGS; } - return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); + return ma_result_from_aaudio(pContextStateAAudio->AAudioStream_close(pStream)); } static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) @@ -39702,8 +40992,9 @@ static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_t static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) { + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(pContext); ma_aaudio_stream_state_t actualNewState; - ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ + ma_aaudio_result_t resultAA = pContextStateAAudio->AAudioStream_waitForStateChange(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } @@ -39754,13 +41045,14 @@ static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_e static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) { - MA_ASSERT(pContext != NULL); + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(pContext); + MA_ASSERT(pStream != NULL); MA_ASSERT(pDeviceInfo != NULL); pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = pContextStateAAudio->AAudioStream_getChannelCount(pStream); + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = pContextStateAAudio->AAudioStream_getSampleRate(pStream); pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; pDeviceInfo->nativeDataFormatCount += 1; } @@ -39812,67 +41104,48 @@ static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_dev static ma_result ma_close_streams__aaudio(ma_device* pDevice) { + ma_device_state_aaudio* pDeviceStateAAudio = ma_device_get_backend_state__aaudio(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); + MA_ASSERT(pDevice != NULL); /* When rerouting, streams may have been closed and never re-opened. Hence the extra checks below. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - pDevice->aaudio.pStreamCapture = NULL; + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(ma_device_get_context(pDevice), pDeviceStateAAudio->pStreamCapture); + pDeviceStateAAudio->pStreamCapture = NULL; } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - pDevice->aaudio.pStreamPlayback = NULL; + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(ma_device_get_context(pDevice), pDeviceStateAAudio->pStreamPlayback); + pDeviceStateAAudio->pStreamPlayback = NULL; } return MA_SUCCESS; } -static ma_result ma_device_uninit__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - Note: Closing the streams may cause a timeout error, which would then trigger rerouting in our error callback. - We must not schedule a reroute when device is getting destroyed. - */ - ma_atomic_bool32_set(&pDevice->aaudio.isTearingDown, MA_TRUE); - - /* Wait for any rerouting to finish before attempting to close the streams. */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { - ma_close_streams__aaudio(pDevice); - } - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - /* Destroy rerouting lock. */ - ma_mutex_uninit(&pDevice->aaudio.rerouteLock); - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) +static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config_aaudio* pDeviceConfigAAudio, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) { + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(ma_device_get_context(pDevice)); ma_result result; int32_t bufferCapacityInFrames; int32_t framesPerDataCallback; ma_AAudioStream* pStream; - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDeviceConfigAAudio != NULL); MA_ASSERT(pDescriptor != NULL); *ppStream = NULL; /* Safety. */ /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ - result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); + result = ma_open_stream__aaudio(pDevice, pDeviceConfigAAudio, deviceType, pDescriptor, &pStream); if (result != MA_SUCCESS) { return result; /* Failed to open the AAudio stream. */ } /* Now extract the internal configuration. */ - pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; - pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); + pDescriptor->format = (pContextStateAAudio->AAudioStream_getFormat(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; + pDescriptor->channels = pContextStateAAudio->AAudioStream_getChannelCount(pStream); + pDescriptor->sampleRate = pContextStateAAudio->AAudioStream_getSampleRate(pStream); /* For the channel map we need to be sure we don't overflow any buffers. */ if (pDescriptor->channels <= MA_MAX_CHANNELS) { @@ -39881,8 +41154,8 @@ static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_dev ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ } - bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); - framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); + bufferCapacityInFrames = pContextStateAAudio->AAudioStream_getBufferCapacityInFrames(pStream); + framesPerDataCallback = pContextStateAAudio->AAudioStream_getFramesPerDataCallback(pStream); if (framesPerDataCallback > 0) { pDescriptor->periodSizeInFrames = framesPerDataCallback; @@ -39897,31 +41170,32 @@ static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_dev return MA_SUCCESS; } -static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, ma_device_state_aaudio* pDeviceStateAAudio, const ma_device_config_aaudio* pDeviceConfigAAudio, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { + ma_device_type deviceType = ma_device_get_type(pDevice); ma_result result; MA_ASSERT(pDevice != NULL); - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } - pDevice->aaudio.usage = pConfig->aaudio.usage; - pDevice->aaudio.contentType = pConfig->aaudio.contentType; - pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; - pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy; - pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; + pDeviceStateAAudio->usage = pDeviceConfigAAudio->usage; + pDeviceStateAAudio->contentType = pDeviceConfigAAudio->contentType; + pDeviceStateAAudio->inputPreset = pDeviceConfigAAudio->inputPreset; + pDeviceStateAAudio->allowedCapturePolicy = pDeviceConfigAAudio->allowedCapturePolicy; + pDeviceStateAAudio->noAutoStartAfterReroute = pDeviceConfigAAudio->noAutoStartAfterReroute; - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + result = ma_device_init_by_type__aaudio(pDevice, pDeviceConfigAAudio, ma_device_type_capture, pDescriptorCapture, &pDeviceStateAAudio->pStreamCapture); if (result != MA_SUCCESS) { return result; } } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + result = ma_device_init_by_type__aaudio(pDevice, pDeviceConfigAAudio, ma_device_type_playback, pDescriptorPlayback, &pDeviceStateAAudio->pStreamPlayback); if (result != MA_SUCCESS) { return result; } @@ -39930,27 +41204,59 @@ static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_dev return MA_SUCCESS; } -static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__aaudio(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { + ma_device_state_aaudio* pDeviceStateAAudio; + const ma_device_config_aaudio* pDeviceConfigAAudio = (const ma_device_config_aaudio*)pDeviceBackendConfig; ma_result result; - MA_ASSERT(pDevice != NULL); + pDeviceStateAAudio = (ma_device_state_aaudio*)ma_calloc(sizeof(*pDeviceStateAAudio), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateAAudio == NULL) { + return MA_OUT_OF_MEMORY; + } - result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture); + result = ma_device_init_streams__aaudio(pDevice, pDeviceStateAAudio, pDeviceConfigAAudio, pDescriptorPlayback, pDescriptorCapture); if (result != MA_SUCCESS) { + ma_free(pDeviceStateAAudio, ma_device_get_allocation_callbacks(pDevice)); return result; } - result = ma_mutex_init(&pDevice->aaudio.rerouteLock); + result = ma_mutex_init(&pDeviceStateAAudio->rerouteLock); if (result != MA_SUCCESS) { + ma_free(pDeviceStateAAudio, ma_device_get_allocation_callbacks(pDevice)); return result; } + *ppDeviceState = pDeviceStateAAudio; + return MA_SUCCESS; } +static void ma_device_uninit__aaudio(ma_device* pDevice) +{ + ma_device_state_aaudio* pDeviceStateAAudio = ma_device_get_backend_state__aaudio(pDevice); + + /* + Note: Closing the streams may cause a timeout error, which would then trigger rerouting in our error callback. + We must not schedule a reroute when device is getting destroyed. + */ + ma_atomic_bool32_set(&pDeviceStateAAudio->isTearingDown, MA_TRUE); + + /* Wait for any rerouting to finish before attempting to close the streams. */ + ma_mutex_lock(&pDeviceStateAAudio->rerouteLock); + { + ma_close_streams__aaudio(pDevice); + } + ma_mutex_unlock(&pDeviceStateAAudio->rerouteLock); + ma_mutex_uninit(&pDeviceStateAAudio->rerouteLock); + + ma_free(pDeviceStateAAudio, ma_device_get_allocation_callbacks(pDevice)); +} + static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) { + ma_device_state_aaudio* pDeviceStateAAudio = ma_device_get_backend_state__aaudio(pDevice); + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(ma_device_get_context(pDevice)); ma_aaudio_result_t resultAA; ma_aaudio_stream_state_t currentState; @@ -39960,7 +41266,7 @@ static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStr return MA_INVALID_ARGS; } - resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); + resultAA = pContextStateAAudio->AAudioStream_requestStart(pStream); if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } @@ -39968,7 +41274,7 @@ static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStr /* Do we actually need to wait for the device to transition into its started state? */ /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + currentState = pContextStateAAudio->AAudioStream_getState(pStream); if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { ma_result result; @@ -39976,7 +41282,7 @@ static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStr return MA_ERROR; /* Expecting the stream to be a starting or started state. */ } - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); + result = ma_wait_for_simple_state_transition__aaudio(ma_device_get_context(pDevice), pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); if (result != MA_SUCCESS) { return result; } @@ -39987,6 +41293,8 @@ static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStr static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) { + ma_device_state_aaudio* pDeviceStateAAudio = ma_device_get_backend_state__aaudio(pDevice); + ma_context_state_aaudio* pContextStateAAudio = ma_context_get_backend_state__aaudio(ma_device_get_context(pDevice)); ma_aaudio_result_t resultAA; ma_aaudio_stream_state_t currentState; @@ -40003,18 +41311,18 @@ static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStre This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + currentState = pContextStateAAudio->AAudioStream_getState(pStream); if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ } - resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); + resultAA = pContextStateAAudio->AAudioStream_requestStop(pStream); if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + currentState = pContextStateAAudio->AAudioStream_getState(pStream); if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { ma_result result; @@ -40022,7 +41330,7 @@ static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStre return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ } - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); + result = ma_wait_for_simple_state_transition__aaudio(ma_device_get_context(pDevice), pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); if (result != MA_SUCCESS) { return result; } @@ -40033,20 +41341,21 @@ static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStre static ma_result ma_device_start__aaudio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_aaudio* pDeviceStateAAudio = ma_device_get_backend_state__aaudio(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_start_stream__aaudio(pDevice, pDeviceStateAAudio->pStreamCapture); if (result != MA_SUCCESS) { return result; } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_result result = ma_device_start_stream__aaudio(pDevice, pDeviceStateAAudio->pStreamPlayback); if (result != MA_SUCCESS) { - if (pDevice->type == ma_device_type_duplex) { - ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + if (deviceType == ma_device_type_duplex) { + ma_device_stop_stream__aaudio(pDevice, pDeviceStateAAudio->pStreamCapture); } return result; } @@ -40057,29 +41366,31 @@ static ma_result ma_device_start__aaudio(ma_device* pDevice) static ma_result ma_device_stop__aaudio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_aaudio* pDeviceStateAAudio = ma_device_get_backend_state__aaudio(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + ma_result result = ma_device_stop_stream__aaudio(pDevice, pDeviceStateAAudio->pStreamCapture); if (result != MA_SUCCESS) { return result; } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + ma_result result = ma_device_stop_stream__aaudio(pDevice, pDeviceStateAAudio->pStreamPlayback); if (result != MA_SUCCESS) { return result; } } - ma_device__on_notification_stopped(pDevice); + ma_device_post_notification_stopped(pDevice); return MA_SUCCESS; } static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) { + ma_device_state_aaudio* pDeviceStateAAudio = ma_device_get_backend_state__aaudio(pDevice); const ma_int32 maxAttempts = 4; /* Reasonable retry limit. */ ma_result result; @@ -40090,8 +41401,12 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev /* We got disconnected! Retry a few times, until we find a connected device! */ iAttempt = 0; while (iAttempt++ < maxAttempts) { + ma_device_config_aaudio deviceConfigAAudio; + ma_device_descriptor descriptorPlayback; + ma_device_descriptor descriptorCapture; + /* Device tearing down? No need to reroute! */ - if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) { + if (ma_atomic_bool32_get(&pDeviceStateAAudio->isTearingDown)) { result = MA_SUCCESS; /* Caller should continue as normal. */ break; } @@ -40100,55 +41415,34 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev ma_close_streams__aaudio(pDevice); /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ - ma_device_config deviceConfig; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - deviceConfig = ma_device_config_init(deviceType); - deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.playback.shareMode = pDevice->playback.shareMode; - deviceConfig.playback.format = pDevice->playback.format; - deviceConfig.playback.channels = pDevice->playback.channels; - deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.capture.shareMode = pDevice->capture.shareMode; - deviceConfig.capture.format = pDevice->capture.format; - deviceConfig.capture.channels = pDevice->capture.channels; - deviceConfig.sampleRate = pDevice->sampleRate; - deviceConfig.aaudio.usage = pDevice->aaudio.usage; - deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; - deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; - deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy; - deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; - deviceConfig.periods = 1; - - /* Try to get an accurate period size. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - } else { - deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - } + deviceConfigAAudio = ma_device_config_aaudio_init(); + deviceConfigAAudio.usage = pDeviceStateAAudio->usage; + deviceConfigAAudio.contentType = pDeviceStateAAudio->contentType; + deviceConfigAAudio.inputPreset = pDeviceStateAAudio->inputPreset; + deviceConfigAAudio.allowedCapturePolicy = pDeviceStateAAudio->allowedCapturePolicy; + deviceConfigAAudio.noAutoStartAfterReroute = pDeviceStateAAudio->noAutoStartAfterReroute; if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; - descriptorCapture.shareMode = deviceConfig.capture.shareMode; - descriptorCapture.format = deviceConfig.capture.format; - descriptorCapture.channels = deviceConfig.capture.channels; - descriptorCapture.sampleRate = deviceConfig.sampleRate; - descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorCapture.periodCount = deviceConfig.periods; + descriptorCapture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ + descriptorCapture.shareMode = pDevice->capture.shareMode; + descriptorCapture.format = pDevice->capture.format; + descriptorCapture.channels = pDevice->capture.channels; + descriptorCapture.sampleRate = pDevice->sampleRate; + descriptorCapture.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; + descriptorCapture.periodCount = 1; } if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; - descriptorPlayback.shareMode = deviceConfig.playback.shareMode; - descriptorPlayback.format = deviceConfig.playback.format; - descriptorPlayback.channels = deviceConfig.playback.channels; - descriptorPlayback.sampleRate = deviceConfig.sampleRate; - descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorPlayback.periodCount = deviceConfig.periods; + descriptorPlayback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ + descriptorPlayback.shareMode = pDevice->playback.shareMode; + descriptorPlayback.format = pDevice->playback.format; + descriptorPlayback.channels = pDevice->playback.channels; + descriptorPlayback.sampleRate = pDevice->sampleRate; + descriptorPlayback.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; + descriptorPlayback.periodCount = 1; } - result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); + result = ma_device_init_streams__aaudio(pDevice, pDeviceStateAAudio, &deviceConfigAAudio, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); /* Reroute failed! */ @@ -40164,11 +41458,11 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev } /* We'll only ever do this in response to a reroute. */ - ma_device__on_notification_rerouted(pDevice); + ma_device_post_notification_rerouted(pDevice); /* If the device is started, start the streams. Maybe make this configurable? */ if (ma_device_get_state(pDevice) == ma_device_state_started) { - if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { + if (pDeviceStateAAudio->noAutoStartAfterReroute == MA_FALSE) { result = ma_device_start__aaudio(pDevice); if (result != MA_SUCCESS) { if (iAttempt < maxAttempts) { @@ -40191,177 +41485,20 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev return result; } -static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream = NULL; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(type != ma_device_type_duplex); - MA_ASSERT(pDeviceInfo != NULL); - - if (type == ma_device_type_capture) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; - pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - if (type == ma_device_type_playback) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; - pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - - /* Safety. Should never happen. */ - if (pStream == NULL) { - return MA_INVALID_OPERATION; - } - - pDeviceInfo->nativeDataFormatCount = 0; - ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__aaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_aaudio); - - ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); - - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libNames[] = { - "libaaudio.so" - }; - - for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); - if (pContext->aaudio.hAAudio != NULL) { - break; - } - } - - if (pContext->aaudio.hAAudio == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); -#else - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)AAudio_createStreamBuilder; - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)AAudioStreamBuilder_delete; - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)AAudioStreamBuilder_setDeviceId; - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)AAudioStreamBuilder_setDirection; - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)AAudioStreamBuilder_setSharingMode; - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)AAudioStreamBuilder_setFormat; - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)AAudioStreamBuilder_setChannelCount; - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)AAudioStreamBuilder_setSampleRate; - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames; - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback; - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)AAudioStreamBuilder_setDataCallback; - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)AAudioStreamBuilder_setErrorCallback; - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)AAudioStreamBuilder_setPerformanceMode; - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)AAudioStreamBuilder_setUsage; - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)AAudioStreamBuilder_setContentType; - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)AAudioStreamBuilder_setInputPreset; - #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29 - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy; - #endif - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)AAudioStreamBuilder_openStream; - pContext->aaudio.AAudioStream_close = (ma_proc)AAudioStream_close; - pContext->aaudio.AAudioStream_getState = (ma_proc)AAudioStream_getState; - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)AAudioStream_waitForStateChange; - pContext->aaudio.AAudioStream_getFormat = (ma_proc)AAudioStream_getFormat; - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)AAudioStream_getChannelCount; - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)AAudioStream_getSampleRate; - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)AAudioStream_getBufferCapacityInFrames; - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)AAudioStream_getFramesPerDataCallback; - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)AAudioStream_getFramesPerBurst; - pContext->aaudio.AAudioStream_requestStart = (ma_proc)AAudioStream_requestStart; - pContext->aaudio.AAudioStream_requestStop = (ma_proc)AAudioStream_requestStop; -#endif - - pCallbacks->onContextInit = ma_context_init__aaudio; - pCallbacks->onContextUninit = ma_context_uninit__aaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; - pCallbacks->onDeviceInit = ma_device_init__aaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; - pCallbacks->onDeviceStart = ma_device_start__aaudio; - pCallbacks->onDeviceStop = ma_device_stop__aaudio; - pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; - - - /* We need a job thread so we can deal with rerouting. */ - { - ma_result result; - ma_device_job_thread_config jobThreadConfig; - - jobThreadConfig = ma_device_job_thread_config_init(); - - result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - return result; - } - } - - - (void)pConfig; - return MA_SUCCESS; -} - static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) { ma_result result = MA_SUCCESS; ma_device* pDevice; + ma_device_state_aaudio* pDeviceStateAAudio; MA_ASSERT(pJob != NULL); pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; MA_ASSERT(pDevice != NULL); - ma_mutex_lock(&pDevice->aaudio.rerouteLock); + pDeviceStateAAudio = ma_device_get_backend_state__aaudio(pDevice); + + ma_mutex_lock(&pDeviceStateAAudio->rerouteLock); { /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); @@ -40374,16 +41511,37 @@ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) ma_device_stop(pDevice); } } - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); + ma_mutex_unlock(&pDeviceStateAAudio->rerouteLock); return result; } + +static ma_device_backend_vtable ma_gDeviceBackendVTable_AAudio = +{ + ma_backend_info__aaudio, + ma_context_init__aaudio, + ma_context_uninit__aaudio, + ma_context_enumerate_devices__aaudio, + ma_context_get_device_info__aaudio, + ma_device_init__aaudio, + ma_device_uninit__aaudio, + ma_device_start__aaudio, + ma_device_stop__aaudio, + NULL, /* onDeviceRead */ + NULL, /* onDeviceWrite */ + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ +}; + +ma_device_backend_vtable* ma_device_backend_aaudio = &ma_gDeviceBackendVTable_AAudio; #else /* Getting here means there is no AAudio backend so we need a no-op job implementation. */ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) { return ma_job_process__noop(pJob); } + +ma_device_backend_vtable* ma_device_backend_aaudio = NULL; #endif /* AAudio */ @@ -40616,6 +41774,249 @@ static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordi } +typedef struct ma_context_state_opensl +{ + ma_handle libOpenSLES; + SLInterfaceID SL_IID_ENGINE; + SLInterfaceID SL_IID_AUDIOIODEVICECAPABILITIES; + SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + SLInterfaceID SL_IID_RECORD; + SLInterfaceID SL_IID_PLAY; + SLInterfaceID SL_IID_OUTPUTMIX; + SLInterfaceID SL_IID_ANDROIDCONFIGURATION; + ma_slCreateEngine_proc slCreateEngine; +} ma_context_state_opensl; + +typedef struct ma_device_state_opensl +{ + SLObjectItf pOutputMixObj; + SLOutputMixItf pOutputMix; + SLObjectItf pAudioPlayerObj; + SLPlayItf pAudioPlayer; + SLObjectItf pAudioRecorderObj; + SLRecordItf pAudioRecorder; + SLAndroidSimpleBufferQueueItf pBufferQueuePlayback; + SLAndroidSimpleBufferQueueItf pBufferQueueCapture; + ma_bool32 isDrainingCapture; + ma_bool32 isDrainingPlayback; + ma_uint32 currentBufferIndexPlayback; + ma_uint32 currentBufferIndexCapture; + ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ + ma_uint8* pBufferCapture; +} ma_device_state_opensl; + +static ma_context_state_opensl* ma_context_get_backend_state__opensl(ma_context* pContext) +{ + return (ma_context_state_opensl*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_opensl* ma_device_get_backend_state__opensl(ma_device* pDevice) +{ + return (ma_device_state_opensl*)ma_device_get_backend_state(pDevice); +} + + +static void ma_backend_info__opensl(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "OpenSL|ES"; +} + + +static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, ma_context_state_opensl* pContextStateOpenSL, const char* pName, SLInterfaceID* pHandle) +{ + /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ + SLInterfaceID* p = (SLInterfaceID*)ma_dlsym(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES, pName); + if (p == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); + return MA_NO_BACKEND; + } + + *pHandle = *p; + return MA_SUCCESS; +} + +static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext, ma_context_state_opensl* pContextStateOpenSL) +{ + g_maOpenSLInitCounter += 1; + if (g_maOpenSLInitCounter == 1) { + SLresult resultSL; + + resultSL = pContextStateOpenSL->slCreateEngine(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + g_maOpenSLInitCounter -= 1; + return ma_result_from_OpenSL(resultSL); + } + + (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); + + resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, pContextStateOpenSL->SL_IID_ENGINE, &g_maEngineSL); + if (resultSL != SL_RESULT_SUCCESS) { + (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); + g_maOpenSLInitCounter -= 1; + return ma_result_from_OpenSL(resultSL); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_init__opensl(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_opensl* pContextStateOpenSL; + const ma_context_config_opensl* pContextConfigAAudio = (const ma_context_config_opensl*)pContextBackendConfig; + ma_context_config_opensl defaultConfigAAudio; + ma_result result; + + if (pContextConfigAAudio == NULL) { + defaultConfigAAudio = ma_context_config_opensl_init(); + pContextConfigAAudio = &defaultConfigAAudio; + } + + (void)pContextBackendConfig; + + pContextStateOpenSL = (ma_context_state_opensl*)ma_calloc(sizeof(*pContextStateOpenSL), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateOpenSL == NULL) { + return MA_OUT_OF_MEMORY; + } + + #if !defined(MA_NO_RUNTIME_LINKING) + { + /* + Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One + report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime + and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any + references to the symbols and will hopefully skip the checks. + */ + size_t i; + const char* libOpenSLESNames[] = { + "libOpenSLES.so" + }; + + for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { + pContextStateOpenSL->libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); + if (pContextStateOpenSL->libOpenSLES != NULL) { + break; + } + } + + if (pContextStateOpenSL->libOpenSLES == NULL) { + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + return MA_NO_BACKEND; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, pContextStateOpenSL, "SL_IID_ENGINE", &pContextStateOpenSL->SL_IID_ENGINE); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, pContextStateOpenSL, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContextStateOpenSL->SL_IID_AUDIOIODEVICECAPABILITIES); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, pContextStateOpenSL, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContextStateOpenSL->SL_IID_ANDROIDSIMPLEBUFFERQUEUE); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, pContextStateOpenSL, "SL_IID_RECORD", &pContextStateOpenSL->SL_IID_RECORD); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, pContextStateOpenSL, "SL_IID_PLAY", &pContextStateOpenSL->SL_IID_PLAY); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, pContextStateOpenSL, "SL_IID_OUTPUTMIX", &pContextStateOpenSL->SL_IID_OUTPUTMIX); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, pContextStateOpenSL, "SL_IID_ANDROIDCONFIGURATION", &pContextStateOpenSL->SL_IID_ANDROIDCONFIGURATION); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + return result; + } + + pContextStateOpenSL->slCreateEngine = (ma_slCreateEngine_proc)ma_dlsym(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES, "slCreateEngine"); + if (pContextStateOpenSL->slCreateEngine == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); + return MA_NO_BACKEND; + } + } + #else + { + pContextStateOpenSL->SL_IID_ENGINE = SL_IID_ENGINE; + pContextStateOpenSL->SL_IID_AUDIOIODEVICECAPABILITIES = SL_IID_AUDIOIODEVICECAPABILITIES; + pContextStateOpenSL->SL_IID_ANDROIDSIMPLEBUFFERQUEUE = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + pContextStateOpenSL->SL_IID_RECORD = SL_IID_RECORD; + pContextStateOpenSL->SL_IID_PLAY = SL_IID_PLAY; + pContextStateOpenSL->SL_IID_OUTPUTMIX = SL_IID_OUTPUTMIX; + pContextStateOpenSL->SL_IID_ANDROIDCONFIGURATION = SL_IID_ANDROIDCONFIGURATION; + pContextStateOpenSL->slCreateEngine = slCreateEngine; + } + #endif + + + /* Initialize global data first if applicable. */ + ma_spinlock_lock(&g_maOpenSLSpinlock); + { + result = ma_context_init_engine_nolock__opensl(pContext, pContextStateOpenSL); + } + ma_spinlock_unlock(&g_maOpenSLSpinlock); + + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContextStateOpenSL->libOpenSLES); + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); + return result; + } + + *ppContextState = pContextStateOpenSL; + + return MA_SUCCESS; +} + +static void ma_context_uninit__opensl(ma_context* pContext) +{ + ma_context_state_opensl* pContextStateOpenSL = ma_context_get_backend_state__opensl(pContext); + + /* Uninit global data. */ + ma_spinlock_lock(&g_maOpenSLSpinlock); + { + MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ + + g_maOpenSLInitCounter -= 1; + if (g_maOpenSLInitCounter == 0) { + (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); + } + } + ma_spinlock_unlock(&g_maOpenSLSpinlock); + + ma_free(pContextStateOpenSL, ma_context_get_allocation_callbacks(pContext)); +} + + + static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_bool32 cbResult; @@ -40634,13 +42035,14 @@ static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_e This is currently untested, so for now we are just returning default devices. */ #if 0 && !defined(MA_ANDROID) + ma_context_state_opensl* pContextStateOpenSL = ma_context_get_backend_state__opensl(pContext); ma_bool32 isTerminated = MA_FALSE; SLuint32 pDeviceIDs[128]; SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, pContextStateOpenSL->SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); if (resultSL != SL_RESULT_SUCCESS) { /* The interface may not be supported so just report a default device. */ goto return_default_device; @@ -40780,8 +42182,10 @@ static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_dev This is currently untested, so for now we are just returning default devices. */ #if 0 && !defined(MA_ANDROID) + ma_context_state_opensl* pContextStateOpenSL = ma_context_get_backend_state__opensl(pContext); + SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, pContextStateOpenSL->SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); if (resultSL != SL_RESULT_SUCCESS) { /* The interface may not be supported so just report a default device. */ goto return_default_device; @@ -40855,6 +42259,7 @@ return_detailed_info: static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_opensl* pDeviceStateOpenSL = ma_device_get_backend_state__opensl(pDevice); size_t periodSizeInBytes; ma_uint8* pBuffer; SLresult resultSL; @@ -40875,26 +42280,27 @@ static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBuff } /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingCapture) { + if (pDeviceStateOpenSL->isDrainingCapture) { return; } periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); + pBuffer = pDeviceStateOpenSL->pBufferCapture + (pDeviceStateOpenSL->currentBufferIndexCapture * periodSizeInBytes); ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); + resultSL = MA_OPENSL_BUFFERQUEUE(pDeviceStateOpenSL->pBufferQueueCapture)->Enqueue(pDeviceStateOpenSL->pBufferQueueCapture, pBuffer, periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { return; } - pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; + pDeviceStateOpenSL->currentBufferIndexCapture = (pDeviceStateOpenSL->currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; } static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_opensl* pDeviceStateOpenSL = ma_device_get_backend_state__opensl(pDevice); size_t periodSizeInBytes; ma_uint8* pBuffer; SLresult resultSL; @@ -40909,54 +42315,24 @@ static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBuf } /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingPlayback) { + if (pDeviceStateOpenSL->isDrainingPlayback) { return; } periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); + pBuffer = pDeviceStateOpenSL->pBufferPlayback + (pDeviceStateOpenSL->currentBufferIndexPlayback * periodSizeInBytes); ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); + resultSL = MA_OPENSL_BUFFERQUEUE(pDeviceStateOpenSL->pBufferQueuePlayback)->Enqueue(pDeviceStateOpenSL->pBufferQueuePlayback, pBuffer, periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { return; } - pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; + pDeviceStateOpenSL->currentBufferIndexPlayback = (pDeviceStateOpenSL->currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; } #endif -static ma_result ma_device_uninit__opensl(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioRecorderObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); - } - - ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioPlayerObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); - } - if (pDevice->opensl.pOutputMixObj) { - MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); - } - - ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} #if defined(MA_ANDROID) && __ANDROID_API__ >= 21 typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; @@ -41063,8 +42439,17 @@ static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pD return MA_SUCCESS; } -static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) + +static void ma_device_uninit__opensl(ma_device* pDevice); + +static ma_result ma_device_init__opensl(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { + ma_device_state_opensl* pDeviceStateOpenSL; + ma_context_state_opensl* pContextStateOpenSL = ma_context_get_backend_state__opensl(ma_device_get_context(pDevice)); + ma_device_config_opensl* pDeviceConfigOpenSL = (ma_device_config_opensl*)pDeviceBackendConfig; + ma_device_config_opensl defaultConfigOpenSL; + ma_device_type deviceType = ma_device_get_type(pDevice); + #ifdef MA_ANDROID SLDataLocator_AndroidSimpleBufferQueue queue; SLresult resultSL; @@ -41076,12 +42461,17 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf }; #endif + if (pDeviceConfigOpenSL == NULL) { + defaultConfigOpenSL = ma_device_config_opensl_init(); + pDeviceConfigOpenSL = &defaultConfigOpenSL; + } + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ if (g_maOpenSLInitCounter == 0) { return MA_INVALID_OPERATION; } - if (pConfig->deviceType == ma_device_type_loopback) { + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } @@ -41091,22 +42481,24 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf queues). */ #ifdef MA_ANDROID - itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; + itfIDs[0] = pContextStateOpenSL->SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + itfIDs[1] = pContextStateOpenSL->SL_IID_ANDROIDCONFIGURATION; /* No exclusive mode with OpenSL|ES. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + if (((deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } /* Now we can start initializing the device properly. */ - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->opensl); + pDeviceStateOpenSL = (ma_device_state_opensl*)ma_calloc(sizeof(*pDeviceStateOpenSL), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateOpenSL == NULL) { + return MA_OUT_OF_MEMORY; + } queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { ma_SLDataFormat_PCM pcm; SLDataLocator_IODevice locatorDevice; SLDataSource source; @@ -41128,7 +42520,7 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf sink.pLocator = &queue; sink.pFormat = (SLDataFormat_PCM*)&pcm; - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDeviceStateOpenSL->pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ pcm.formatType = SL_DATAFORMAT_PCM; @@ -41137,7 +42529,7 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf pcm.bitsPerSample = 16; pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ pcm.channelMask = 0; - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDeviceStateOpenSL->pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); } if (resultSL != SL_RESULT_SUCCESS) { @@ -41148,10 +42540,10 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf /* Set the recording preset before realizing the player. */ - if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); + if (pDeviceConfigOpenSL->recordingPreset != ma_opensl_recording_preset_default) { + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioRecorderObj)->GetInterface(pDeviceStateOpenSL->pAudioRecorderObj, pContextStateOpenSL->SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); if (resultSL == SL_RESULT_SUCCESS) { - SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); + SLint32 recordingPreset = ma_to_recording_preset__opensl(pDeviceConfigOpenSL->recordingPreset); resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); if (resultSL != SL_RESULT_SUCCESS) { /* Failed to set the configuration. Just keep going. */ @@ -41159,28 +42551,28 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf } } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioRecorderObj)->Realize((SLObjectItf)pDeviceStateOpenSL->pAudioRecorderObj, SL_BOOLEAN_FALSE); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); return ma_result_from_OpenSL(resultSL); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioRecorderObj)->GetInterface((SLObjectItf)pDeviceStateOpenSL->pAudioRecorderObj, pContextStateOpenSL->SL_IID_RECORD, &pDeviceStateOpenSL->pAudioRecorder); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); return ma_result_from_OpenSL(resultSL); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioRecorderObj)->GetInterface(pDeviceStateOpenSL->pAudioRecorderObj, pContextStateOpenSL->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDeviceStateOpenSL->pBufferQueueCapture); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); return ma_result_from_OpenSL(resultSL); } - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); + resultSL = MA_OPENSL_BUFFERQUEUE(pDeviceStateOpenSL->pBufferQueueCapture)->RegisterCallback(pDeviceStateOpenSL->pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); @@ -41191,20 +42583,20 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); /* Buffer. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexCapture = 0; + pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, ma_performance_profile_low_latency); + pDeviceStateOpenSL->currentBufferIndexCapture = 0; bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; - pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferCapture == NULL) { + pDeviceStateOpenSL->pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateOpenSL->pBufferCapture == NULL) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); return MA_OUT_OF_MEMORY; } - MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); + MA_ZERO_MEMORY(pDeviceStateOpenSL->pBufferCapture, bufferSizeInBytes); } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_SLDataFormat_PCM pcm; SLDataSource source; SLDataLocator_OutputMix outmixLocator; @@ -41213,21 +42605,21 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); - resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); + resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDeviceStateOpenSL->pOutputMixObj, 0, NULL, NULL); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); return ma_result_from_OpenSL(resultSL); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pOutputMixObj)->Realize(pDeviceStateOpenSL->pOutputMixObj, SL_BOOLEAN_FALSE); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); return ma_result_from_OpenSL(resultSL); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pOutputMixObj)->GetInterface(pDeviceStateOpenSL->pOutputMixObj, pContextStateOpenSL->SL_IID_OUTPUTMIX, &pDeviceStateOpenSL->pOutputMix); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); @@ -41237,7 +42629,7 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf /* Set the output device. */ if (pDescriptorPlayback->pDeviceID != NULL) { SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; - MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); + MA_OPENSL_OUTPUTMIX(pDeviceStateOpenSL->pOutputMix)->ReRoute((SLOutputMixItf)pDeviceStateOpenSL->pOutputMix, 1, &deviceID_OpenSL); } queue.numBuffers = pDescriptorPlayback->periodCount; @@ -41246,12 +42638,12 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf source.pFormat = (SLDataFormat_PCM*)&pcm; outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; - outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; + outmixLocator.outputMix = (SLObjectItf)pDeviceStateOpenSL->pOutputMixObj; sink.pLocator = &outmixLocator; sink.pFormat = NULL; - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDeviceStateOpenSL->pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ pcm.formatType = SL_DATAFORMAT_PCM; @@ -41260,7 +42652,7 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf pcm.bitsPerSample = 16; pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDeviceStateOpenSL->pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); } if (resultSL != SL_RESULT_SUCCESS) { @@ -41271,10 +42663,10 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf /* Set the stream type before realizing the player. */ - if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); + if (pDeviceConfigOpenSL->streamType != ma_opensl_stream_type_default) { + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioPlayerObj)->GetInterface(pDeviceStateOpenSL->pAudioPlayerObj, pContextStateOpenSL->SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); if (resultSL == SL_RESULT_SUCCESS) { - SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); + SLint32 streamType = ma_to_stream_type__opensl(pDeviceConfigOpenSL->streamType); resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); if (resultSL != SL_RESULT_SUCCESS) { /* Failed to set the configuration. Just keep going. */ @@ -41282,28 +42674,28 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf } } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioPlayerObj)->Realize(pDeviceStateOpenSL->pAudioPlayerObj, SL_BOOLEAN_FALSE); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); return ma_result_from_OpenSL(resultSL); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioPlayerObj)->GetInterface(pDeviceStateOpenSL->pAudioPlayerObj, pContextStateOpenSL->SL_IID_PLAY, &pDeviceStateOpenSL->pAudioPlayer); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); return ma_result_from_OpenSL(resultSL); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); + resultSL = MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioPlayerObj)->GetInterface(pDeviceStateOpenSL->pAudioPlayerObj, pContextStateOpenSL->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDeviceStateOpenSL->pBufferQueuePlayback); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); return ma_result_from_OpenSL(resultSL); } - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); + resultSL = MA_OPENSL_BUFFERQUEUE(pDeviceStateOpenSL->pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDeviceStateOpenSL->pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); @@ -41314,27 +42706,65 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); /* Buffer. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexPlayback = 0; + pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, ma_performance_profile_low_latency); + pDeviceStateOpenSL->currentBufferIndexPlayback = 0; bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; - pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferPlayback == NULL) { + pDeviceStateOpenSL->pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateOpenSL->pBufferPlayback == NULL) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); return MA_OUT_OF_MEMORY; } - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); + MA_ZERO_MEMORY(pDeviceStateOpenSL->pBufferPlayback, bufferSizeInBytes); } + *ppDeviceState = pDeviceStateOpenSL; + return MA_SUCCESS; #else return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ #endif } +static void ma_device_uninit__opensl(ma_device* pDevice) +{ + ma_device_state_opensl* pDeviceStateOpenSL = ma_device_get_backend_state__opensl(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); + + MA_ASSERT(pDevice != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ + if (g_maOpenSLInitCounter == 0) { + return; + } + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + if (pDeviceStateOpenSL->pAudioRecorderObj) { + MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioRecorderObj)->Destroy(pDeviceStateOpenSL->pAudioRecorderObj); + } + + ma_free(pDeviceStateOpenSL->pBufferCapture, ma_device_get_allocation_callbacks(pDevice)); + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + if (pDeviceStateOpenSL->pAudioPlayerObj) { + MA_OPENSL_OBJ(pDeviceStateOpenSL->pAudioPlayerObj)->Destroy(pDeviceStateOpenSL->pAudioPlayerObj); + } + if (pDeviceStateOpenSL->pOutputMixObj) { + MA_OPENSL_OBJ(pDeviceStateOpenSL->pOutputMixObj)->Destroy(pDeviceStateOpenSL->pOutputMixObj); + } + + ma_free(pDeviceStateOpenSL->pBufferPlayback, ma_device_get_allocation_callbacks(pDevice)); + } + + ma_free(pDeviceStateOpenSL, ma_device_get_allocation_callbacks(pDevice)); +} + static ma_result ma_device_start__opensl(ma_device* pDevice) { + ma_device_state_opensl* pDeviceStateOpenSL = ma_device_get_backend_state__opensl(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); SLresult resultSL; size_t periodSizeInBytes; ma_uint32 iPeriod; @@ -41346,8 +42776,8 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) return MA_INVALID_OPERATION; } - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + resultSL = MA_OPENSL_RECORD(pDeviceStateOpenSL->pAudioRecorder)->SetRecordState((SLRecordItf)pDeviceStateOpenSL->pAudioRecorder, SL_RECORDSTATE_RECORDING); if (resultSL != SL_RESULT_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); return ma_result_from_OpenSL(resultSL); @@ -41355,34 +42785,34 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); + resultSL = MA_OPENSL_BUFFERQUEUE(pDeviceStateOpenSL->pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDeviceStateOpenSL->pBufferQueueCapture, pDeviceStateOpenSL->pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); + MA_OPENSL_RECORD(pDeviceStateOpenSL->pAudioRecorder)->SetRecordState((SLRecordItf)pDeviceStateOpenSL->pAudioRecorder, SL_RECORDSTATE_STOPPED); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); return ma_result_from_OpenSL(resultSL); } } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + resultSL = MA_OPENSL_PLAY(pDeviceStateOpenSL->pAudioPlayer)->SetPlayState((SLPlayItf)pDeviceStateOpenSL->pAudioPlayer, SL_PLAYSTATE_PLAYING); if (resultSL != SL_RESULT_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); return ma_result_from_OpenSL(resultSL); } /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ - if (pDevice->type == ma_device_type_duplex) { - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + if (deviceType == ma_device_type_duplex) { + MA_ZERO_MEMORY(pDeviceStateOpenSL->pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); } else { - ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); + ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDeviceStateOpenSL->pBufferPlayback); } periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); + resultSL = MA_OPENSL_BUFFERQUEUE(pDeviceStateOpenSL->pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDeviceStateOpenSL->pBufferQueuePlayback, pDeviceStateOpenSL->pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); + MA_OPENSL_PLAY(pDeviceStateOpenSL->pAudioPlayer)->SetPlayState((SLPlayItf)pDeviceStateOpenSL->pAudioPlayer, SL_PLAYSTATE_STOPPED); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); return ma_result_from_OpenSL(resultSL); } @@ -41394,16 +42824,17 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) { + ma_device_state_opensl* pDeviceStateOpenSL = ma_device_get_backend_state__opensl(pDevice); SLAndroidSimpleBufferQueueItf pBufferQueue; MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); - if (pDevice->type == ma_device_type_capture) { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; - pDevice->opensl.isDrainingCapture = MA_TRUE; + if (deviceType == ma_device_type_capture) { + pBufferQueue = pDeviceStateOpenSL->pBufferQueueCapture; + pDeviceStateOpenSL->isDrainingCapture = MA_TRUE; } else { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; - pDevice->opensl.isDrainingPlayback = MA_TRUE; + pBufferQueue = pDeviceStateOpenSL->pBufferQueuePlayback; + pDeviceStateOpenSL->isDrainingPlayback = MA_TRUE; } for (;;) { @@ -41417,10 +42848,10 @@ static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type devi ma_sleep(10); } - if (pDevice->type == ma_device_type_capture) { - pDevice->opensl.isDrainingCapture = MA_FALSE; + if (deviceType == ma_device_type_capture) { + pDeviceStateOpenSL->isDrainingCapture = MA_FALSE; } else { - pDevice->opensl.isDrainingPlayback = MA_FALSE; + pDeviceStateOpenSL->isDrainingPlayback = MA_FALSE; } return MA_SUCCESS; @@ -41428,6 +42859,8 @@ static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type devi static ma_result ma_device_stop__opensl(ma_device* pDevice) { + ma_device_state_opensl* pDeviceStateOpenSL = ma_device_get_backend_state__opensl(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); SLresult resultSL; MA_ASSERT(pDevice != NULL); @@ -41437,217 +42870,56 @@ static ma_result ma_device_stop__opensl(ma_device* pDevice) return MA_INVALID_OPERATION; } - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { ma_device_drain__opensl(pDevice, ma_device_type_capture); - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); + resultSL = MA_OPENSL_RECORD(pDeviceStateOpenSL->pAudioRecorder)->SetRecordState((SLRecordItf)pDeviceStateOpenSL->pAudioRecorder, SL_RECORDSTATE_STOPPED); if (resultSL != SL_RESULT_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); return ma_result_from_OpenSL(resultSL); } - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); + MA_OPENSL_BUFFERQUEUE(pDeviceStateOpenSL->pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDeviceStateOpenSL->pBufferQueueCapture); } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_device_drain__opensl(pDevice, ma_device_type_playback); - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); + resultSL = MA_OPENSL_PLAY(pDeviceStateOpenSL->pAudioPlayer)->SetPlayState((SLPlayItf)pDeviceStateOpenSL->pAudioPlayer, SL_PLAYSTATE_STOPPED); if (resultSL != SL_RESULT_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); return ma_result_from_OpenSL(resultSL); } - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); + MA_OPENSL_BUFFERQUEUE(pDeviceStateOpenSL->pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDeviceStateOpenSL->pBufferQueuePlayback); } /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ - ma_device__on_notification_stopped(pDevice); + ma_device_post_notification_stopped(pDevice); return MA_SUCCESS; } - -static ma_result ma_context_uninit__opensl(ma_context* pContext) +static ma_device_backend_vtable ma_gDeviceBackendVTable_OpenSL = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_opensl); - (void)pContext; + ma_backend_info__opensl, + ma_context_init__opensl, + ma_context_uninit__opensl, + ma_context_enumerate_devices__opensl, + ma_context_get_device_info__opensl, + ma_device_init__opensl, + ma_device_uninit__opensl, + ma_device_start__opensl, + ma_device_stop__opensl, + NULL, /* onDeviceRead */ + NULL, /* onDeviceWrite */ + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ +}; - /* Uninit global data. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ - - g_maOpenSLInitCounter -= 1; - if (g_maOpenSLInitCounter == 0) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - } - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - return MA_SUCCESS; -} - -static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) -{ - /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ - ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); - if (p == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); - return MA_NO_BACKEND; - } - - *pHandle = *p; - return MA_SUCCESS; -} - -static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) -{ - g_maOpenSLInitCounter += 1; - if (g_maOpenSLInitCounter == 1) { - SLresult resultSL; - - resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - - (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); - - resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); - if (resultSL != SL_RESULT_SUCCESS) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; - -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libOpenSLESNames[] = { - "libOpenSLES.so" - }; -#endif - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#if !defined(MA_NO_RUNTIME_LINKING) - /* - Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One - report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime - and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any - references to the symbols and will hopefully skip the checks. - */ - for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { - pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); - if (pContext->opensl.libOpenSLES != NULL) { - break; - } - } - - if (pContext->opensl.libOpenSLES == NULL) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); - return MA_NO_BACKEND; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); - if (pContext->opensl.slCreateEngine == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); - return MA_NO_BACKEND; - } +ma_device_backend_vtable* ma_device_backend_opensl = &ma_gDeviceBackendVTable_OpenSL; #else - pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; - pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; - pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; - pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; - pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; - pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; - pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; -#endif - - - /* Initialize global data first if applicable. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - result = ma_context_init_engine_nolock__opensl(pContext); - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__opensl; - pCallbacks->onContextUninit = ma_context_uninit__opensl; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; - pCallbacks->onDeviceInit = ma_device_init__opensl; - pCallbacks->onDeviceUninit = ma_device_uninit__opensl; - pCallbacks->onDeviceStart = ma_device_start__opensl; - pCallbacks->onDeviceStop = ma_device_stop__opensl; - pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_opensl = NULL; #endif /* OpenSL|ES */ @@ -41686,6 +42958,48 @@ TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by def #define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback" #endif + +typedef struct ma_context_state_webaudio +{ + int _unused; +} ma_context_state_webaudio; + +typedef struct ma_device_state_webaudio +{ + #if defined(MA_USE_AUDIO_WORKLETS) + EMSCRIPTEN_WEBAUDIO_T audioContext; + EMSCRIPTEN_WEBAUDIO_T audioWorklet; + #endif + float* pIntermediaryBuffer; + void* pStackBuffer; + ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ + int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ +} ma_device_state_webaudio; + +static ma_context_state_webaudio* ma_context_get_backend_state__webaudio(ma_context* pContext) +{ + return (ma_context_state_webaudio*)ma_context_get_backend_state(pContext); +} + +static ma_device_state_webaudio* ma_device_get_backend_state__webaudio(ma_device* pDevice) +{ + return (ma_device_state_webaudio*)ma_device_get_backend_state(pDevice); +} + + +#if defined(MA_EMSCRIPTEN) +#ifdef __cplusplus +extern "C" { +#endif +void EMSCRIPTEN_KEEPALIVE ma_device_post_notification_unlocked_emscripten(ma_device* pDevice) +{ + return ma_device_post_notification_unlocked(pDevice); +} +#ifdef __cplusplus +} +#endif +#endif + static ma_bool32 ma_is_capture_supported__webaudio() { return EM_ASM_INT({ @@ -41719,6 +43033,159 @@ void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_dev } #endif +static void ma_backend_info__webaudio(ma_device_backend_info* pBackendInfo) +{ + MA_ASSERT(pBackendInfo != NULL); + pBackendInfo->pName = "Web Audio"; +} + +static ma_result ma_context_init__webaudio(ma_context* pContext, const void* pContextBackendConfig, void** ppContextState) +{ + ma_context_state_webaudio* pContextStateWebAudio; + const ma_context_config_webaudio* pContextConfigWebAudio = (const ma_context_config_webaudio*)pContextBackendConfig; + ma_context_config_webaudio defaultConfigWebAudio; + int resultFromJS; + + if (pContextConfigWebAudio == NULL) { + defaultConfigWebAudio = ma_context_config_webaudio_init(); + pContextConfigWebAudio = &defaultConfigWebAudio; + } + + pContextStateWebAudio = (ma_context_state_webaudio*)ma_calloc(sizeof(*pContextStateWebAudio), ma_context_get_allocation_callbacks(pContext)); + if (pContextStateWebAudio == NULL) { + return MA_OUT_OF_MEMORY; + } + + /* Here is where our global JavaScript object is initialized. */ + resultFromJS = EM_ASM_INT({ + if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { + return 0; /* Web Audio not supported. */ + } + + if (typeof(window.miniaudio) === 'undefined') { + window.miniaudio = { + referenceCount: 0 + }; + + /* Device types. */ + window.miniaudio.device_type = {}; + window.miniaudio.device_type.playback = $0; + window.miniaudio.device_type.capture = $1; + window.miniaudio.device_type.duplex = $2; + + /* Device states. */ + window.miniaudio.device_state = {}; + window.miniaudio.device_state.stopped = $3; + window.miniaudio.device_state.started = $4; + + /* Device cache for mapping devices to indexes for JavaScript/C interop. */ + let miniaudio = window.miniaudio; + miniaudio.devices = []; + + miniaudio.track_device = function(device) { + /* Try inserting into a free slot first. */ + for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { + if (miniaudio.devices[iDevice] == null) { + miniaudio.devices[iDevice] = device; + return iDevice; + } + } + + /* Getting here means there is no empty slots in the array so we just push to the end. */ + miniaudio.devices.push(device); + return miniaudio.devices.length - 1; + }; + + miniaudio.untrack_device_by_index = function(deviceIndex) { + /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ + miniaudio.devices[deviceIndex] = null; + + /* Trim the array if possible. */ + while (miniaudio.devices.length > 0) { + if (miniaudio.devices[miniaudio.devices.length-1] == null) { + miniaudio.devices.pop(); + } else { + break; + } + } + }; + + miniaudio.untrack_device = function(device) { + for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { + if (miniaudio.devices[iDevice] == device) { + return miniaudio.untrack_device_by_index(iDevice); + } + } + }; + + miniaudio.get_device_by_index = function(deviceIndex) { + return miniaudio.devices[deviceIndex]; + }; + + miniaudio.unlock_event_types = (function(){ + return ['touchend', 'click']; + })(); + + miniaudio.unlock = function() { + for(var i = 0; i < miniaudio.devices.length; ++i) { + var device = miniaudio.devices[i]; + if (device != null && + device.webaudio != null && + device.state === miniaudio.device_state.started) { + + device.webaudio.resume().then(() => { + _ma_device_post_notification_unlocked_emscripten(device.pDevice); + }, + (error) => {console.error("Failed to resume audiocontext", error); + }); + } + } + miniaudio.unlock_event_types.map(function(event_type) { + document.removeEventListener(event_type, miniaudio.unlock, true); + }); + }; + + miniaudio.unlock_event_types.map(function(event_type) { + document.addEventListener(event_type, miniaudio.unlock, true); + }); + } + + window.miniaudio.referenceCount += 1; + + return 1; + }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); + + if (resultFromJS != 1) { + ma_free(pContextStateWebAudio, ma_context_get_allocation_callbacks(pContext)); + return MA_FAILED_TO_INIT_BACKEND; + } + + *ppContextState = pContextStateWebAudio; + + return MA_SUCCESS; +} + +static void ma_context_uninit__webaudio(ma_context* pContext) +{ + ma_context_state_webaudio* pContextStateWebAudio = ma_context_get_backend_state__webaudio(pContext); + + /* Remove the global miniaudio object from window if there are no more references to it. */ + EM_ASM({ + if (typeof(window.miniaudio) !== 'undefined') { + miniaudio.unlock_event_types.map(function(event_type) { + document.removeEventListener(event_type, miniaudio.unlock, true); + }); + + window.miniaudio.referenceCount -= 1; + if (window.miniaudio.referenceCount === 0) { + delete window.miniaudio; + } + } + }); + + ma_free(pContextStateWebAudio, ma_context_get_allocation_callbacks(pContext)); +} + static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_bool32 cbResult = MA_TRUE; @@ -41796,67 +43263,9 @@ static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_d return MA_SUCCESS; } -static ma_result ma_device_uninit__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - #if defined(MA_USE_AUDIO_WORKLETS) - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - - emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); - } - #else - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - /* Make sure all nodes are disconnected and marked for collection. */ - if (device.scriptNode !== undefined) { - device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ - device.scriptNode.disconnect(); - device.scriptNode = undefined; - } - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - /* - Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want - to clear the callback before closing. - */ - device.webaudio.close(); - device.webaudio = undefined; - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - } - #endif - - /* Clean up the device on the JS side. */ - EM_ASM({ - window.miniaudio.untrack_device_by_index($0); - }, pDevice->webaudio.deviceIndex); - - ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - - return MA_SUCCESS; -} #if !defined(MA_USE_AUDIO_WORKLETS) -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate) { /* There have been reports of the default buffer size being too small on some browsers. If we're using @@ -41870,11 +43279,7 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(co if (pDescriptor->periodSizeInFrames == 0) { if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); - } + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ } else { periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); } @@ -41900,7 +43305,7 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(co typedef struct { ma_device* pDevice; - const ma_device_config* pConfig; + ma_device_state_webaudio* pDeviceStateWebAudio; ma_device_descriptor* pDescriptorPlayback; ma_device_descriptor* pDescriptorCapture; } ma_audio_worklet_thread_initialized_data; @@ -41908,6 +43313,8 @@ typedef struct static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; + ma_device_state_webaudio* pDeviceStateWebAudio = ma_device_get_backend_state__webaudio(pDevice); + ma_device_type deviceType = ma_device_get_type(pDevice); ma_uint32 frameCount; (void)paramCount; @@ -41921,7 +43328,7 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio for further processing. */ - if (pDevice->type == ma_device_type_playback) { + if (deviceType == ma_device_type_playback) { frameCount = pDevice->playback.internalPeriodSizeInFrames; } else { frameCount = pDevice->capture.internalPeriodSizeInFrames; @@ -41940,24 +43347,24 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const /* Input data needs to be interleaved before we hand it to the client. */ for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; + pDeviceStateWebAudio->pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; } } - ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); + ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDeviceStateWebAudio->pIntermediaryBuffer); } if (outputCount > 0) { /* If it's a capture-only device, we'll need to output silence. */ - if (pDevice->type == ma_device_type_capture) { + if (deviceType == ma_device_type_capture) { MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); } else { - ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); + ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDeviceStateWebAudio->pIntermediaryBuffer); /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; + pOutputs[0].data[frameCount*iChannel + iFrame] = pDeviceStateWebAudio->pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; } } } @@ -41970,14 +43377,15 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) { ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; + ma_device_type deviceType = ma_device_get_type(pParameters->pDevice); EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; int channels = 0; size_t intermediaryBufferSizeInFrames; int sampleRate; if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + pParameters->pDeviceStateWebAudio->initResult = MA_ERROR; + ma_free(pParameters, ma_device_get_allocation_callbacks(pParameters->pDevice)); return; } @@ -41991,13 +43399,13 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have proper control over the channel count. In the capture case, we'll have to output silence to its output node. */ - if (pParameters->pConfig->deviceType == ma_device_type_capture) { + if (deviceType == ma_device_type_capture) { channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); audioWorkletOptions.numberOfInputs = 1; } else { channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); - if (pParameters->pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_duplex) { audioWorkletOptions.numberOfInputs = 1; } else { audioWorkletOptions.numberOfInputs = 0; @@ -42022,17 +43430,17 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a } #endif - pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); - if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { - pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; + pParameters->pDeviceStateWebAudio->pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); + if (pParameters->pDeviceStateWebAudio->pIntermediaryBuffer == NULL) { + pParameters->pDeviceStateWebAudio->initResult = MA_OUT_OF_MEMORY; ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); return; } - pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); + pParameters->pDeviceStateWebAudio->audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); /* With the audio worklet initialized we can now attach it to the graph. */ - if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { ma_result attachmentResult = (ma_result)EM_ASM_INT({ var getUserMediaResult = 0; var audioWorklet = emscriptenGetAudioObject($0); @@ -42051,29 +43459,29 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a }); return getUserMediaResult; - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); + }, pParameters->pDeviceStateWebAudio->audioWorklet, audioContext); if (attachmentResult != MA_SUCCESS) { ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); - emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = attachmentResult; + emscripten_destroy_web_audio_node(pParameters->pDeviceStateWebAudio->audioWorklet); + pParameters->pDeviceStateWebAudio->initResult = attachmentResult; ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); return; } } /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ - if (pParameters->pConfig->deviceType == ma_device_type_playback) { + if (deviceType == ma_device_type_playback) { ma_result attachmentResult = (ma_result)EM_ASM_INT({ var audioWorklet = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); audioWorklet.connect(audioContext.destination); return 0; /* 0 = MA_SUCCESS */ - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); + }, pParameters->pDeviceStateWebAudio->audioWorklet, audioContext); if (attachmentResult != MA_SUCCESS) { ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); - pParameters->pDevice->webaudio.initResult = attachmentResult; + pParameters->pDeviceStateWebAudio->initResult = attachmentResult; ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); return; } @@ -42101,8 +43509,8 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a } /* At this point we're done and we can return. */ - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = MA_SUCCESS; + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDeviceStateWebAudio->audioWorklet); + pParameters->pDeviceStateWebAudio->initResult = MA_SUCCESS; ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); } @@ -42114,7 +43522,7 @@ static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T MA_ASSERT(pParameters != NULL); if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; + pParameters->pDeviceStateWebAudio->initResult = MA_ERROR; return; } @@ -42125,18 +43533,33 @@ static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T } #endif -static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__webaudio(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState) { - if (pConfig->deviceType == ma_device_type_loopback) { + ma_device_state_webaudio* pDeviceStateWebAudio; + const ma_device_config_webaudio* pDeviceConfigWebAudio = (const ma_device_config_webaudio*)pDeviceBackendConfig; + ma_device_config_webaudio defaultConfigWebAudio; + ma_device_type deviceType = ma_device_get_type(pDevice); + + if (pDeviceConfigWebAudio == NULL) { + defaultConfigWebAudio = ma_device_config_webaudio_init(); + pDeviceConfigWebAudio = &defaultConfigWebAudio; + } + + if (deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* No exclusive mode with Web Audio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + if (((deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } + pDeviceStateWebAudio = (ma_device_state_webaudio*)ma_calloc(sizeof(*pDeviceStateWebAudio), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateWebAudio == NULL) { + return MA_OUT_OF_MEMORY; + } + /* With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so it might be worthwhile to look into that as well. @@ -42147,10 +43570,14 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co ma_audio_worklet_thread_initialized_data* pInitParameters; void* pStackBuffer; - if (pConfig->performanceProfile == ma_performance_profile_conservative) { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; - } else { + /* */ if (pDeviceConfigWebAudio->latencyHint == ma_webaudio_latency_hint_interactive) { audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; + } else if (pDeviceConfigWebAudio->latencyHint == ma_webaudio_latency_hint_playback) { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; + } else if (pDeviceConfigWebAudio->latencyHint == ma_webaudio_latency_hint_balanced) { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_BALANCED; + } else { + /* Leave the latency hint unset. */ } /* @@ -42159,14 +43586,14 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co if the device type is anything other than playback, we'll leave the sample rate as-is and let the browser pick the appropriate rate for us. */ - if (pConfig->deviceType == ma_device_type_playback) { + if (deviceType == ma_device_type_playback) { audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; } else { audioContextAttributes.sampleRate = 0; } /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ - pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); + pDeviceStateWebAudio->audioContext = emscripten_create_audio_context(&audioContextAttributes); /* With the context created we can now create the worklet. We can only have a single worklet per audio @@ -42178,52 +43605,53 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to allocate this on the heap to keep it simple. */ - pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); + pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, ma_device_get_allocation_callbacks(pDevice)); if (pStackBuffer == NULL) { - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + emscripten_destroy_audio_context(pDeviceStateWebAudio->audioContext); + ma_free(pDeviceStateWebAudio, ma_device_get_allocation_callbacks(pDevice)); return MA_OUT_OF_MEMORY; } /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ - pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); + pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), ma_device_get_allocation_callbacks(pDevice)); if (pInitParameters == NULL) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + ma_aligned_free(pStackBuffer, ma_device_get_allocation_callbacks(pDevice)); + emscripten_destroy_audio_context(pDeviceStateWebAudio->audioContext); + ma_free(pDeviceStateWebAudio, ma_device_get_allocation_callbacks(pDevice)); return MA_OUT_OF_MEMORY; } - pInitParameters->pDevice = pDevice; - pInitParameters->pConfig = pConfig; - pInitParameters->pDescriptorPlayback = pDescriptorPlayback; - pInitParameters->pDescriptorCapture = pDescriptorCapture; + pInitParameters->pDevice = pDevice; + pInitParameters->pDeviceStateWebAudio = pDeviceStateWebAudio; + pInitParameters->pDescriptorPlayback = pDescriptorPlayback; + pInitParameters->pDescriptorCapture = pDescriptorCapture; /* We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of the Emscripten WebAudio stuff is asynchronous. */ - pDevice->webaudio.initResult = MA_BUSY; + pDeviceStateWebAudio->initResult = MA_BUSY; { - emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); + emscripten_start_wasm_audio_worklet_thread_async(pDeviceStateWebAudio->audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); } - while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ + while (pDeviceStateWebAudio->initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ - if (pDevice->webaudio.initResult != MA_SUCCESS) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return pDevice->webaudio.initResult; + if (pDeviceStateWebAudio->initResult != MA_SUCCESS) { + ma_aligned_free(pStackBuffer, ma_device_get_allocation_callbacks(pDevice)); + emscripten_destroy_audio_context(pDeviceStateWebAudio->audioContext); + ma_free(pDeviceStateWebAudio, ma_device_get_allocation_callbacks(pDevice)); + return pDeviceStateWebAudio->initResult; } /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ - pDevice->webaudio.deviceIndex = EM_ASM_INT({ + pDeviceStateWebAudio->deviceIndex = EM_ASM_INT({ return window.miniaudio.track_device({ webaudio: emscriptenGetAudioObject($0), state: 1, /* 1 = ma_device_state_stopped */ pDevice: $1 }); - }, pDevice->webaudio.audioContext, pDevice); - - return MA_SUCCESS; + }, pDeviceStateWebAudio->audioContext, pDevice); } #else { @@ -42234,7 +43662,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co ma_uint32 periodSizeInFrames; /* The channel count will depend on the device type. If it's a capture, use its, otherwise use the playback side. */ - if (pConfig->deviceType == ma_device_type_capture) { + if (deviceType == ma_device_type_capture) { channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; } else { channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; @@ -42244,22 +43672,22 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's native rate. For this reason we're leaving the sample rate untouched for capture devices. */ - if (pConfig->deviceType == ma_device_type_playback) { + if (deviceType == ma_device_type_playback) { sampleRate = pDescriptorPlayback->sampleRate; } else { sampleRate = 0; /* Let the browser decide when capturing. */ } /* The period size needs to be a power of 2. */ - if (pConfig->deviceType == ma_device_type_capture) { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); + if (deviceType == ma_device_type_capture) { + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate); } else { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate); } /* We need an intermediary buffer for doing interleaving and deinterleaving. */ - pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); - if (pDevice->webaudio.pIntermediaryBuffer == NULL) { + pDeviceStateWebAudio->pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), ma_device_get_allocation_callbacks(pDevice)); + if (pDeviceStateWebAudio->pIntermediaryBuffer == NULL) { return MA_OUT_OF_MEMORY; } @@ -42359,13 +43787,14 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co device.pDevice = pDevice; return window.miniaudio.track_device(device); - }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); + }, deviceType, channels, sampleRate, periodSizeInFrames, pDeviceStateWebAudio->pIntermediaryBuffer, pDevice); if (deviceIndex < 0) { + ma_free(pDeviceStateWebAudio, ma_device_get_allocation_callbacks(pDevice)); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } - pDevice->webaudio.deviceIndex = deviceIndex; + pDeviceStateWebAudio->deviceIndex = deviceIndex; /* Grab the sample rate from the audio context directly. */ sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); @@ -42387,28 +43816,88 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; pDescriptorPlayback->periodCount = 1; } - - return MA_SUCCESS; } #endif + + *ppDeviceState = pDeviceStateWebAudio; + + return MA_SUCCESS; +} + +static void ma_device_uninit__webaudio(ma_device* pDevice) +{ + ma_device_state_webaudio* pDeviceStateWebAudio = ma_device_get_backend_state__webaudio(pDevice); + + #if defined(MA_USE_AUDIO_WORKLETS) + { + EM_ASM({ + var device = window.miniaudio.get_device_by_index($0); + + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + + device.pDevice = undefined; + }, pDeviceStateWebAudio->deviceIndex); + + emscripten_destroy_web_audio_node(pDeviceStateWebAudio->audioWorklet); + emscripten_destroy_audio_context(pDeviceStateWebAudio->audioContext); + ma_aligned_free(pDeviceStateWebAudio->pStackBuffer, ma_device_get_allocation_callbacks(pDevice)); + } + #else + { + EM_ASM({ + var device = window.miniaudio.get_device_by_index($0); + + /* Make sure all nodes are disconnected and marked for collection. */ + if (device.scriptNode !== undefined) { + device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ + device.scriptNode.disconnect(); + device.scriptNode = undefined; + } + + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + + /* + Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want + to clear the callback before closing. + */ + device.webaudio.close(); + device.webaudio = undefined; + device.pDevice = undefined; + }, pDeviceStateWebAudio->deviceIndex); + } + #endif + + /* Clean up the device on the JS side. */ + EM_ASM({ + window.miniaudio.untrack_device_by_index($0); + }, pDeviceStateWebAudio->deviceIndex); + + ma_free(pDeviceStateWebAudio->pIntermediaryBuffer, ma_device_get_allocation_callbacks(pDevice)); + ma_free(pDeviceStateWebAudio, ma_device_get_allocation_callbacks(pDevice)); } static ma_result ma_device_start__webaudio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_webaudio* pDeviceStateWebAudio = ma_device_get_backend_state__webaudio(pDevice); EM_ASM({ var device = window.miniaudio.get_device_by_index($0); device.webaudio.resume(); device.state = window.miniaudio.device_state.started; - }, pDevice->webaudio.deviceIndex); + }, pDeviceStateWebAudio->deviceIndex); return MA_SUCCESS; } static ma_result ma_device_stop__webaudio(ma_device* pDevice) { - MA_ASSERT(pDevice != NULL); + ma_device_state_webaudio* pDeviceStateWebAudio = ma_device_get_backend_state__webaudio(pDevice); /* From the WebAudio API documentation for AudioContext.suspend(): @@ -42423,162 +43912,35 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) var device = window.miniaudio.get_device_by_index($0); device.webaudio.suspend(); device.state = window.miniaudio.device_state.stopped; - }, pDevice->webaudio.deviceIndex); + }, pDeviceStateWebAudio->deviceIndex); - ma_device__on_notification_stopped(pDevice); + ma_device_post_notification_stopped(pDevice); return MA_SUCCESS; } -static ma_result ma_context_uninit__webaudio(ma_context* pContext) + + +static ma_device_backend_vtable ma_gDeviceBackendVTable_WebAudio = { - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_webaudio); + ma_backend_info__webaudio, + ma_context_init__webaudio, + ma_context_uninit__webaudio, + ma_context_enumerate_devices__webaudio, + ma_context_get_device_info__webaudio, + ma_device_init__webaudio, + ma_device_uninit__webaudio, + ma_device_start__webaudio, + ma_device_stop__webaudio, + NULL, /* onDeviceRead */ + NULL, /* onDeviceWrite */ + NULL, /* onDeviceLoop */ + NULL /* onDeviceWakeup */ +}; - (void)pContext; /* Unused. */ - - /* Remove the global miniaudio object from window if there are no more references to it. */ - EM_ASM({ - if (typeof(window.miniaudio) !== 'undefined') { - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - - window.miniaudio.referenceCount -= 1; - if (window.miniaudio.referenceCount === 0) { - delete window.miniaudio; - } - } - }); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int resultFromJS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; /* Unused. */ - - /* Here is where our global JavaScript object is initialized. */ - resultFromJS = EM_ASM_INT({ - if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { - return 0; /* Web Audio not supported. */ - } - - if (typeof(window.miniaudio) === 'undefined') { - window.miniaudio = { - referenceCount: 0 - }; - - /* Device types. */ - window.miniaudio.device_type = {}; - window.miniaudio.device_type.playback = $0; - window.miniaudio.device_type.capture = $1; - window.miniaudio.device_type.duplex = $2; - - /* Device states. */ - window.miniaudio.device_state = {}; - window.miniaudio.device_state.stopped = $3; - window.miniaudio.device_state.started = $4; - - /* Device cache for mapping devices to indexes for JavaScript/C interop. */ - let miniaudio = window.miniaudio; - miniaudio.devices = []; - - miniaudio.track_device = function(device) { - /* Try inserting into a free slot first. */ - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == null) { - miniaudio.devices[iDevice] = device; - return iDevice; - } - } - - /* Getting here means there is no empty slots in the array so we just push to the end. */ - miniaudio.devices.push(device); - return miniaudio.devices.length - 1; - }; - - miniaudio.untrack_device_by_index = function(deviceIndex) { - /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ - miniaudio.devices[deviceIndex] = null; - - /* Trim the array if possible. */ - while (miniaudio.devices.length > 0) { - if (miniaudio.devices[miniaudio.devices.length-1] == null) { - miniaudio.devices.pop(); - } else { - break; - } - } - }; - - miniaudio.untrack_device = function(device) { - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == device) { - return miniaudio.untrack_device_by_index(iDevice); - } - } - }; - - miniaudio.get_device_by_index = function(deviceIndex) { - return miniaudio.devices[deviceIndex]; - }; - - miniaudio.unlock_event_types = (function(){ - return ['touchend', 'click']; - })(); - - miniaudio.unlock = function() { - for(var i = 0; i < miniaudio.devices.length; ++i) { - var device = miniaudio.devices[i]; - if (device != null && - device.webaudio != null && - device.state === miniaudio.device_state.started) { - - device.webaudio.resume().then(() => { - _ma_device__on_notification_unlocked(device.pDevice); - }, - (error) => {console.error("Failed to resume audiocontext", error); - }); - } - } - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - }; - - miniaudio.unlock_event_types.map(function(event_type) { - document.addEventListener(event_type, miniaudio.unlock, true); - }); - } - - window.miniaudio.referenceCount += 1; - - return 1; - }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); - - if (resultFromJS != 1) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pCallbacks->onContextInit = ma_context_init__webaudio; - pCallbacks->onContextUninit = ma_context_uninit__webaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; - pCallbacks->onDeviceInit = ma_device_init__webaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; - pCallbacks->onDeviceStart = ma_device_start__webaudio; - pCallbacks->onDeviceStop = ma_device_stop__webaudio; - pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ - - return MA_SUCCESS; -} +ma_device_backend_vtable* ma_device_backend_webaudio = &ma_gDeviceBackendVTable_WebAudio; +#else +ma_device_backend_vtable* ma_device_backend_webaudio = NULL; #endif /* MA_HAS_WEBAUDIO */ @@ -42614,14 +43976,13 @@ static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) ma_bool32 hasWrite; MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->pVTable != NULL); - hasRead = pContext->pVTable->onDeviceRead != NULL && (pContext->pVTable->onHasRead == NULL || pContext->pVTable->onHasRead (pContext->pVTableUserData, pContext) == MA_TRUE); - hasWrite = pContext->pVTable->onDeviceWrite != NULL && (pContext->pVTable->onHasWrite == NULL || pContext->pVTable->onHasWrite(pContext->pVTableUserData, pContext) == MA_TRUE); + hasRead = pContext->pVTable->onDeviceRead != NULL; + hasWrite = pContext->pVTable->onDeviceWrite != NULL; if (hasRead == MA_FALSE && hasWrite == MA_FALSE) { - ma_bool32 hasDataLoop = pContext->pVTable->onDeviceDataLoop != NULL && (pContext->pVTable->onHasDataLoop == NULL || pContext->pVTable->onHasDataLoop(pContext->pVTableUserData, pContext) == MA_TRUE); - - if (hasDataLoop == MA_FALSE) { + if (pContext->pVTable->onDeviceLoop == NULL) { return MA_TRUE; } else { return MA_FALSE; @@ -42710,10 +44071,10 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d /* Make sure the old converter is uninitialized first. */ if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); + ma_data_converter_uninit(&pDevice->capture.converter, ma_device_get_allocation_callbacks(pDevice)); } - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); + result = ma_data_converter_init(&converterConfig, ma_device_get_allocation_callbacks(pDevice), &pDevice->capture.converter); if (result != MA_SUCCESS) { return result; } @@ -42740,10 +44101,10 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d /* Make sure the old converter is uninitialized first. */ if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); + ma_data_converter_uninit(&pDevice->playback.converter, ma_device_get_allocation_callbacks(pDevice)); } - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); + result = ma_data_converter_init(&converterConfig, ma_device_get_allocation_callbacks(pDevice), &pDevice->playback.converter); if (result != MA_SUCCESS) { return result; } @@ -42787,15 +44148,15 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); if (newInputCacheSizeInBytes > MA_SIZE_MAX) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->playback.pInputCache, ma_device_get_allocation_callbacks(pDevice)); pDevice->playback.pInputCache = NULL; pDevice->playback.inputCacheCap = 0; return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ } - pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); + pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, ma_device_get_allocation_callbacks(pDevice)); if (pNewInputCache == NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->playback.pInputCache, ma_device_get_allocation_callbacks(pDevice)); pDevice->playback.pInputCache = NULL; pDevice->playback.inputCacheCap = 0; return MA_OUT_OF_MEMORY; @@ -42805,7 +44166,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d pDevice->playback.inputCacheCap = newInputCacheCap; } else { /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->playback.pInputCache, ma_device_get_allocation_callbacks(pDevice)); pDevice->playback.pInputCache = NULL; pDevice->playback.inputCacheCap = 0; } @@ -42906,8 +44267,8 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); /* If the device has a start callback, start it now. */ - if (pDevice->pContext->pVTable->onDeviceStart != NULL && (pDevice->pContext->pVTable->onHasStart == NULL || pDevice->pContext->pVTable->onHasStart(pDevice->pContext->pVTableUserData, pDevice->pContext) == MA_TRUE)) { - startResult = pDevice->pContext->pVTable->onDeviceStart(pDevice->pContext->pVTableUserData, pDevice); + if (pDevice->pContext->pVTable->onDeviceStart != NULL) { + startResult = pDevice->pContext->pVTable->onDeviceStart(pDevice); } else { startResult = MA_SUCCESS; } @@ -42926,18 +44287,18 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ ma_event_signal(&pDevice->startEvent); - ma_device__on_notification_started(pDevice); + ma_device_post_notification_started(pDevice); - if (pDevice->pContext->pVTable->onDeviceDataLoop != NULL && (pDevice->pContext->pVTable->onHasDataLoop == NULL || pDevice->pContext->pVTable->onHasDataLoop(pDevice->pContext->pVTableUserData, pDevice->pContext) == MA_TRUE)) { - pDevice->pContext->pVTable->onDeviceDataLoop(pDevice->pContext->pVTableUserData, pDevice); + if (pDevice->pContext->pVTable->onDeviceLoop != NULL) { + pDevice->pContext->pVTable->onDeviceLoop(pDevice); } else { /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ ma_device_audio_thread__default_read_write(pDevice); } /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ - if (pDevice->pContext->pVTable->onDeviceStop != NULL && (pDevice->pContext->pVTable->onHasStop == NULL || pDevice->pContext->pVTable->onHasStop(pDevice->pContext->pVTableUserData, pDevice->pContext) == MA_TRUE)) { - stopResult = pDevice->pContext->pVTable->onDeviceStop(pDevice->pContext->pVTableUserData, pDevice); + if (pDevice->pContext->pVTable->onDeviceStop != NULL) { + stopResult = pDevice->pContext->pVTable->onDeviceStop(pDevice); } else { stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ } @@ -42948,7 +44309,7 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) stopped due to the device being physically unplugged or disabled via an OS setting. */ if (stopResult == MA_SUCCESS) { - ma_device__on_notification_stopped(pDevice); + ma_device_post_notification_stopped(pDevice); } /* If we stopped because the device has been uninitialized, abort now. */ @@ -42971,16 +44332,6 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) } -/* Helper for determining whether or not the given device is initialized. */ -static ma_bool32 ma_device__is_initialized(ma_device* pDevice) -{ - if (pDevice == NULL) { - return MA_FALSE; - } - - return ma_device_get_state(pDevice) != ma_device_state_uninitialized; -} - #ifdef MA_WIN32 static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) @@ -43239,24 +44590,18 @@ MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* -MA_API void ma_device_data_callback_invoke(ma_device_data_callback* pDeviceDataCallback, void* pFramesOutInBackendFormat, const void* pFramesInInBackendFormat, ma_uint32 frameCount) +MA_API ma_device_backend_config ma_device_backend_config_init(ma_device_backend_vtable* pVTable, const void* pConfig) { - if (pDeviceDataCallback == NULL) { - return; - } + ma_device_backend_config config; - MA_ASSERT(pDeviceDataCallback->pInternal != NULL); + config.pVTable = pVTable; + config.pConfig = pConfig; - /* - For now `ma_device_data_callback` is just a wrapper around `ma_device`, but later on I want to update - this so that it's entirely decoupled from `ma_device`. - */ - ma_device_handle_backend_data_callback((ma_device*)pDeviceDataCallback->pInternal, pFramesOutInBackendFormat, pFramesInInBackendFormat, frameCount); + return config; } - -static const void* ma_find_device_backend_config(const ma_device_backend_spec* pBackends, size_t count, const ma_device_backend_vtable* pVTable) +static const void* ma_find_device_backend_config(const ma_device_backend_config* pBackends, size_t count, ma_device_backend_vtable* pVTable) { size_t iBackend; @@ -43282,24 +44627,96 @@ MA_API ma_context_config ma_context_config_init(void) return config; } -MA_API const void* ma_context_config_find_custom_backend_config(const ma_context_config* pConfig, const ma_device_backend_vtable* pVTable) +MA_API const void* ma_context_config_find_backend_config(const ma_context_config* pConfig, const ma_device_backend_config* pBackendConfigs, size_t backendConfigCount, ma_device_backend_vtable* pVTable) { + const void* pBackendConfig; + if (pVTable == NULL || pConfig == NULL) { return NULL; } - return ma_find_device_backend_config(pConfig->custom.pBackends, pConfig->custom.count, pVTable); + pBackendConfig = ma_find_device_backend_config(pBackendConfigs, backendConfigCount, pVTable); + if (pBackendConfig != NULL) { + return pBackendConfig; + } + + /* Getting here means we couldn't find a config pointer. It might have been set to null for stock backend. */ + if (pVTable != NULL) { + if (pVTable == ma_device_backend_wasapi) { + return &pConfig->wasapi; + } + if (pVTable == ma_device_backend_dsound) { + return &pConfig->dsound; + } + if (pVTable == ma_device_backend_winmm) { + return &pConfig->winmm; + } + if (pVTable == ma_device_backend_coreaudio) { + return &pConfig->coreaudio; + } + if (pVTable == ma_device_backend_sndio) { + return &pConfig->sndio; + } + if (pVTable == ma_device_backend_audio4) { + return &pConfig->audio4; + } + if (pVTable == ma_device_backend_oss) { + return &pConfig->oss; + } + if (pVTable == ma_device_backend_pulseaudio) { + return &pConfig->pulseaudio; + } + if (pVTable == ma_device_backend_alsa) { + return &pConfig->alsa; + } + if (pVTable == ma_device_backend_jack) { + return &pConfig->jack; + } + if (pVTable == ma_device_backend_aaudio) { + return &pConfig->aaudio; + } + if (pVTable == ma_device_backend_opensl) { + return &pConfig->opensl; + } + if (pVTable == ma_device_backend_webaudio) { + return &pConfig->webaudio; + } + if (pVTable == ma_device_backend_null) { + return &pConfig->null_backend; + } + } + + return NULL; } -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) +/* This is the list of default device backends in priority order. */ +#define MA_STOCK_DEVICE_BACKENDS \ +{ \ + { ma_device_backend_wasapi, NULL }, \ + { ma_device_backend_dsound, NULL }, \ + { ma_device_backend_winmm, NULL }, \ + { ma_device_backend_coreaudio, NULL }, \ + { ma_device_backend_sndio, NULL }, \ + { ma_device_backend_audio4, NULL }, \ + { ma_device_backend_oss, NULL }, \ + { ma_device_backend_pulseaudio, NULL }, \ + { ma_device_backend_alsa, NULL }, \ + { ma_device_backend_jack, NULL }, \ + { ma_device_backend_aaudio, NULL }, \ + { ma_device_backend_opensl, NULL }, \ + { ma_device_backend_webaudio, NULL }, \ + { ma_device_backend_null, NULL } \ +} + + +MA_API ma_result ma_context_init(const ma_device_backend_config* pBackends, ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) { ma_result result; ma_context_config defaultConfig; - ma_backend defaultBackends[ma_backend_null+1]; ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; + const ma_device_backend_config pStockBackends[] = MA_STOCK_DEVICE_BACKENDS; + ma_uint32 stockBackendCount = ma_countof(pStockBackends); if (pContext == NULL) { return MA_INVALID_ARGS; @@ -43341,211 +44758,68 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC return result; } - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; + /* If NULL was passed in for the backend list we'll want to iterate over our default list. */ + if (pBackends == NULL) { + pBackends = pStockBackends; + backendCount = stockBackendCount; } - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } + for (iBackend = 0; iBackend < backendCount; iBackend += 1) { + ma_device_backend_config backend = pBackends[iBackend]; + ma_device_backend_info backendInfo; /* For logging the name. */ + void* pContextState = NULL; - MA_ASSERT(pBackendsToIterate != NULL); - - for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { - ma_backend backend = pBackendsToIterate[iBackend]; - - /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ - MA_ZERO_OBJECT(&pContext->callbacks); - pContext->pVTable = NULL; - pContext->pVTableUserData = NULL; - - /* For stock backends we can just map the backend enum to the appropriate vtable. */ - - /* These backends are using the new callback system. */ - switch (backend) { - #ifdef MA_HAS_WASAPI - case ma_backend_wasapi: - { - pContext->callbacks.onContextInit = ma_context_init__wasapi; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_DSOUND - case ma_backend_dsound: - { - pContext->callbacks.onContextInit = ma_context_init__dsound; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_WINMM - case ma_backend_winmm: - { - pContext->callbacks.onContextInit = ma_context_init__winmm; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_COREAUDIO - case ma_backend_coreaudio: - { - pContext->callbacks.onContextInit = ma_context_init__coreaudio; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_SNDIO - case ma_backend_sndio: - { - pContext->callbacks.onContextInit = ma_context_init__sndio; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_AUDIO4 - case ma_backend_audio4: - { - pContext->callbacks.onContextInit = ma_context_init__audio4; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_OSS - case ma_backend_oss: - { - pContext->callbacks.onContextInit = ma_context_init__oss; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_PULSEAUDIO - case ma_backend_pulseaudio: - { - pContext->callbacks.onContextInit = ma_context_init__pulse; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_ALSA - case ma_backend_alsa: - { - pContext->callbacks.onContextInit = ma_context_init__alsa; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_JACK - case ma_backend_jack: - { - pContext->callbacks.onContextInit = ma_context_init__jack; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_AAUDIO - case ma_backend_aaudio: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__aaudio; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } - } break; - #endif - #ifdef MA_HAS_OPENSL - case ma_backend_opensl: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__opensl; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } - } break; - #endif - #ifdef MA_HAS_WEBAUDIO - case ma_backend_webaudio: - { - pContext->callbacks.onContextInit = ma_context_init__webaudio; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - #ifdef MA_HAS_CUSTOM - case ma_backend_custom: - { - /* Custom backends are handled differently. */ - } break; - #endif - #ifdef MA_HAS_NULL - case ma_backend_null: - { - pContext->callbacks.onContextInit = ma_context_init__null; - pContext->pVTable = &ma_gDeviceVTable_Compat; - } break; - #endif - - default: break; + /* + If the vtable is NULL just skip it. This is *not* an error - it's possible for a vtable to be null when + a backend has been disabled. + */ + if (backend.pVTable == NULL) { + continue; } - /* Special case for custom backends. */ - if (backend == ma_backend_custom) { - /* It's a custom backend. We need to iterate over each vtable and use the first one that works. */ - if (pConfig->custom.pBackends != NULL && pConfig->custom.count > 0) { - size_t iCustomBackend; - for (iCustomBackend = 0; iCustomBackend < pConfig->custom.count; iCustomBackend += 1) { - pContext->pVTable = pConfig->custom.pBackends[iCustomBackend].pVTable; - pContext->pVTableUserData = pConfig->custom.pBackends[iCustomBackend].pUserData; - - if (pContext->pVTable != NULL) { - MA_ASSERT(pContext->pVTable->onContextInit != NULL); /* onContextInit() must always be specified. */ - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize custom backend %d...\n", (int)iCustomBackend); - - result = pContext->pVTable->onContextInit(pContext->pVTableUserData, pContext, pConfig); - if (result == MA_SUCCESS) { - break; - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize custom backend %d.\n", (int)iCustomBackend); - } - } - } - } else { - /* No custom backend vtables defined. */ - result = MA_NO_BACKEND; - } - } else { - /* It's not a custom backend. */ - if (pContext->pVTable != NULL) { - MA_ASSERT(pContext->pVTable->onContextInit != NULL); /* onContextInit() must always be specified. */ - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); - result = pContext->pVTable->onContextInit(pContext->pVTableUserData, pContext, pConfig); - } else { - /* Getting here means the vtable is not set which means the backend is not enabled. */ - result = MA_BACKEND_NOT_ENABLED; - } + /* Grab the info we have any. We're going to make this callback mandatory. */ + if (backend.pVTable->onBackendInfo == NULL) { + continue; /* No backend info callback. We need this for logging. */ } - + backend.pVTable->onBackendInfo(&backendInfo); - /* If this iteration was successful, return. */ - if (result == MA_SUCCESS) { - result = ma_mutex_init(&pContext->deviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); - } + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...", backendInfo.pName); - result = ma_mutex_init(&pContext->deviceInfoLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); - } - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); - - pContext->backend = backend; - return result; - } else { - if (result == MA_BACKEND_NOT_ENABLED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend)); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); - } + if (backend.pVTable->onContextInit == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend. No onContextInit callback.", backendInfo.pName); + continue; } + + result = backend.pVTable->onContextInit(pContext, ma_context_config_find_backend_config(pConfig, pBackends, backendCount, backend.pVTable), &pContextState); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend. %s.", backendInfo.pName, ma_result_description(result)); + continue; + } + + /* Getting here means initialization was successful. */ + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Successfully initialized %s backend.", backendInfo.pName); + + pContext->pVTable = backend.pVTable; + pContext->pBackendState = pContextState; + + result = ma_mutex_init(&pContext->deviceEnumLock); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); + } + + result = ma_mutex_init(&pContext->deviceInfoLock); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); + } + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); + + return result; } /* If we get here it means an error occurred. */ @@ -43560,7 +44834,8 @@ MA_API ma_result ma_context_uninit(ma_context* pContext) } if (pContext->pVTable->onContextUninit != NULL) { - pContext->pVTable->onContextUninit(pContext->pVTableUserData, pContext); + pContext->pVTable->onContextUninit(pContext); + pContext->pBackendState = NULL; } ma_mutex_uninit(&pContext->deviceEnumLock); @@ -43590,6 +44865,33 @@ MA_API ma_log* ma_context_get_log(ma_context* pContext) return pContext->pLog; } +MA_API const ma_allocation_callbacks* ma_context_get_allocation_callbacks(ma_context* pContext) +{ + if (pContext == NULL) { + return NULL; + } + + return &pContext->allocationCallbacks; +} + +MA_API ma_thread_priority ma_context_get_thread_priority(ma_context* pContext) +{ + if (pContext == NULL) { + return ma_thread_priority_default; + } + + return pContext->threadPriority; +} + +MA_API void* ma_context_get_backend_state(ma_context* pContext) +{ + if (pContext == NULL) { + return NULL; + } + + return pContext->pBackendState; +} + MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { @@ -43605,7 +44907,7 @@ MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devi ma_mutex_lock(&pContext->deviceEnumLock); { - result = pContext->pVTable->onContextEnumerateDevices(pContext->pVTableUserData, pContext, callback, pUserData); + result = pContext->pVTable->onContextEnumerateDevices(pContext, callback, pUserData); } ma_mutex_unlock(&pContext->deviceEnumLock); @@ -43688,7 +44990,7 @@ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** p pContext->captureDeviceInfoCount = 0; /* Now enumerate over available devices. */ - result = pContext->pVTable->onContextEnumerateDevices(pContext->pVTableUserData, pContext, ma_context_get_devices__enum_callback, pContext); + result = pContext->pVTable->onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, pContext); if (result == MA_SUCCESS) { /* Playback devices. */ if (ppPlaybackDeviceInfos != NULL) { @@ -43740,7 +45042,7 @@ MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type ma_mutex_lock(&pContext->deviceInfoLock); { - result = pContext->pVTable->onContextGetDeviceInfo(pContext->pVTableUserData, pContext, deviceType, pDeviceID, &deviceInfo); + result = pContext->pVTable->onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); } ma_mutex_unlock(&pContext->deviceInfoLock); @@ -43768,13 +45070,66 @@ MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) return config; } -MA_API const void* ma_device_config_find_custom_backend_config(const ma_device_config* pConfig, const ma_device_backend_vtable* pVTable) +MA_API const void* ma_device_config_find_backend_config(const ma_device_config* pConfig, ma_device_backend_vtable* pVTable) { + const void* pBackendConfig; + if (pVTable == NULL || pConfig == NULL) { return NULL; } - return ma_find_device_backend_config(pConfig->custom.pBackends, pConfig->custom.count, pVTable); + pBackendConfig = ma_find_device_backend_config(pConfig->pBackendConfigs, pConfig->backendConfigCount, pVTable); + if (pBackendConfig != NULL) { + return pBackendConfig; + } + + /* Getting here means there was no config pointer. Check stock backends. */ + if (pVTable != NULL) { + if (pVTable == ma_device_backend_wasapi) { + return &pConfig->wasapi; + } + if (pVTable == ma_device_backend_dsound) { + return &pConfig->dsound; + } + if (pVTable == ma_device_backend_winmm) { + return &pConfig->winmm; + } + if (pVTable == ma_device_backend_coreaudio) { + return &pConfig->coreaudio; + } + if (pVTable == ma_device_backend_sndio) { + return &pConfig->sndio; + } + if (pVTable == ma_device_backend_audio4) { + return &pConfig->audio4; + } + if (pVTable == ma_device_backend_oss) { + return &pConfig->oss; + } + if (pVTable == ma_device_backend_pulseaudio) { + return &pConfig->pulseaudio; + } + if (pVTable == ma_device_backend_alsa) { + return &pConfig->alsa; + } + if (pVTable == ma_device_backend_jack) { + return &pConfig->jack; + } + if (pVTable == ma_device_backend_aaudio) { + return &pConfig->aaudio; + } + if (pVTable == ma_device_backend_opensl) { + return &pConfig->opensl; + } + if (pVTable == ma_device_backend_webaudio) { + return &pConfig->webaudio; + } + if (pVTable == ma_device_backend_null) { + return &pConfig->null_backend; + } + } + + return NULL; } @@ -43825,9 +45180,6 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } } - /* The data callback object is for bridging the data callback between the backend and the device. */ - pDevice->dataCallback.pInternal = pDevice; - pDevice->pContext = pContext; pDevice->pUserData = pConfig->pUserData; pDevice->onData = pConfig->dataCallback; @@ -43940,7 +45292,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } - result = pContext->pVTable->onDeviceInit(pContext->pVTableUserData, pDevice, pConfig, &descriptorPlayback, &descriptorCapture); + result = pContext->pVTable->onDeviceInit(pDevice, ma_device_config_find_backend_config(pConfig, pContext->pVTable), &descriptorPlayback, &descriptorCapture, &pDevice->pBackendState); if (result != MA_SUCCESS) { ma_event_uninit(&pDevice->startEvent); ma_event_uninit(&pDevice->wakeupEvent); @@ -44019,6 +45371,19 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } + /* + If the device is duplex we may need an intermediary ring buffer. Note that this needs to be done + after ma_device_post_init(). + */ + if (pConfig->deviceType == ma_device_type_duplex) { + result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, ma_device_get_allocation_callbacks(pDevice), &pDevice->duplexRB); + if (result != MA_SUCCESS) { + ma_device_uninit(pDevice); + return result; + } + } + + /* Some backends don't require the worker thread. */ if (!ma_context_is_backend_asynchronous(pContext)) { /* The worker thread. */ @@ -44032,23 +45397,11 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_event_wait(&pDevice->stopEvent); MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); } else { - /* - If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done - after ma_device_post_init(). - */ - if (ma_context_is_backend_asynchronous(pContext)) { - if (pConfig->deviceType == ma_device_type_duplex) { - result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - } - } - + /* No worker thread created. Just put the device straight into a stopped state. */ ma_device__set_state(pDevice, ma_device_state_stopped); } + /* Log device information. */ { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); @@ -44106,15 +45459,14 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC return MA_SUCCESS; } -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) +MA_API ma_result ma_device_init_ex(const ma_device_backend_config* pBackends, ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) { ma_result result; ma_context* pContext; - ma_backend defaultBackends[ma_backend_null+1]; ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; ma_allocation_callbacks allocationCallbacks; + const ma_device_backend_config pStockBackends[] = MA_STOCK_DEVICE_BACKENDS; + ma_uint32 stockBackendCount = ma_countof(pStockBackends); if (pConfig == NULL) { return MA_INVALID_ARGS; @@ -44134,48 +45486,44 @@ MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backen return MA_OUT_OF_MEMORY; } - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); + if (pBackends == NULL) { + pBackends = pStockBackends; + backendCount = stockBackendCount; } result = MA_NO_BACKEND; - for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { + for (iBackend = 0; iBackend < backendCount; iBackend += 1) { /* This is a hack for iOS. If the context config is null, there's a good chance the `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this case, set the session category based on the device type. */ - #if defined(MA_APPLE_MOBILE) - ma_context_config contextConfig; + #if defined(MA_APPLE_MOBILE) + { + ma_context_config contextConfig; - if (pContextConfig == NULL) { - contextConfig = ma_context_config_init(); - switch (pConfig->deviceType) { - case ma_device_type_duplex: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; - } break; - case ma_device_type_capture: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; - } break; - case ma_device_type_playback: - default: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; - } break; + if (pContextConfig == NULL) { + contextConfig = ma_context_config_init(); + switch (pConfig->deviceType) { + case ma_device_type_duplex: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; + } break; + case ma_device_type_capture: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; + } break; + case ma_device_type_playback: + default: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; + } break; + } + + pContextConfig = &contextConfig; } - - pContextConfig = &contextConfig; } - #endif + #endif - result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); + result = ma_context_init(&pBackends[iBackend], 1, pContextConfig, pContext); if (result == MA_SUCCESS) { result = ma_device_init(pContext, pConfig, pDevice); if (result == MA_SUCCESS) { @@ -44197,7 +45545,7 @@ MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backen MA_API void ma_device_uninit(ma_device* pDevice) { - if (!ma_device__is_initialized(pDevice)) { + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { return; } @@ -44229,7 +45577,8 @@ MA_API void ma_device_uninit(ma_device* pDevice) } if (pDevice->pContext->pVTable->onDeviceUninit != NULL) { - pDevice->pContext->pVTable->onDeviceUninit(pDevice->pContext->pVTableUserData, pDevice); + pDevice->pContext->pVTable->onDeviceUninit(pDevice); + pDevice->pBackendState = NULL; } @@ -44245,21 +45594,21 @@ MA_API void ma_device_uninit(ma_device* pDevice) } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); + ma_data_converter_uninit(&pDevice->capture.converter, ma_device_get_allocation_callbacks(pDevice)); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); + ma_data_converter_uninit(&pDevice->playback.converter, ma_device_get_allocation_callbacks(pDevice)); } if (pDevice->playback.pInputCache != NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->playback.pInputCache, ma_device_get_allocation_callbacks(pDevice)); } if (pDevice->capture.pIntermediaryBuffer != NULL) { - ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->capture.pIntermediaryBuffer, ma_device_get_allocation_callbacks(pDevice)); } if (pDevice->playback.pIntermediaryBuffer != NULL) { - ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->playback.pIntermediaryBuffer, ma_device_get_allocation_callbacks(pDevice)); } if (pDevice->isOwnerOfContext) { @@ -44286,6 +45635,27 @@ MA_API ma_log* ma_device_get_log(ma_device* pDevice) return ma_context_get_log(ma_device_get_context(pDevice)); } +MA_API ma_device_type ma_device_get_type(ma_device* pDevice) +{ + return pDevice->type; +} + +MA_API const ma_allocation_callbacks* ma_device_get_allocation_callbacks(ma_device* pDevice) +{ + return ma_context_get_allocation_callbacks(ma_device_get_context(pDevice)); +} + +MA_API void* ma_device_get_backend_state(ma_device* pDevice) +{ + if (pDevice == NULL) { + return NULL; + } + + return pDevice->pBackendState; +} + + + MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) { if (pDeviceInfo == NULL) { @@ -44298,12 +45668,6 @@ MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_ return MA_INVALID_ARGS; } - /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ - if (pDevice->pContext->pVTable->onDeviceGetInfo != NULL && (pDevice->pContext->pVTable->onHasDeviceGetInfo == NULL || pDevice->pContext->pVTable->onHasDeviceGetInfo(pDevice->pContext->pVTableUserData, pDevice->pContext) == MA_TRUE)) { - return pDevice->pContext->pVTable->onDeviceGetInfo(pDevice->pContext->pVTableUserData, pDevice, type, pDeviceInfo); - } - - /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ if (type == ma_device_type_playback) { return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); } else { @@ -44395,15 +45759,15 @@ MA_API ma_result ma_device_start(ma_device* pDevice) /* Asynchronous backends need to be handled differently. */ if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->pContext->pVTable->onDeviceStart != NULL && (pDevice->pContext->pVTable->onHasStart == NULL || pDevice->pContext->pVTable->onHasStart(pDevice->pContext->pVTableUserData, pDevice->pContext) == MA_TRUE)) { - result = pDevice->pContext->pVTable->onDeviceStart(pDevice->pContext->pVTableUserData, pDevice); + if (pDevice->pContext->pVTable->onDeviceStart != NULL) { + result = pDevice->pContext->pVTable->onDeviceStart(pDevice); } else { result = MA_INVALID_OPERATION; } if (result == MA_SUCCESS) { ma_device__set_state(pDevice, ma_device_state_started); - ma_device__on_notification_started(pDevice); + ma_device_post_notification_started(pDevice); } } else { /* @@ -44465,8 +45829,8 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) /* Asynchronous backends need to be handled differently. */ if (ma_context_is_backend_asynchronous(pDevice->pContext)) { /* Asynchronous backends must have a stop operation. */ - if (pDevice->pContext->pVTable->onDeviceStop != NULL && (pDevice->pContext->pVTable->onHasStop == NULL || pDevice->pContext->pVTable->onHasStop(pDevice->pContext->pVTableUserData, pDevice->pContext) == MA_TRUE)) { - result = pDevice->pContext->pVTable->onDeviceStop(pDevice->pContext->pVTableUserData, pDevice); + if (pDevice->pContext->pVTable->onDeviceStop != NULL) { + result = pDevice->pContext->pVTable->onDeviceStop(pDevice); } else { result = MA_INVALID_OPERATION; } @@ -44481,8 +45845,8 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) */ MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); - if (pDevice->pContext->pVTable->onDeviceDataLoopWakeup != NULL) { - pDevice->pContext->pVTable->onDeviceDataLoopWakeup(pDevice->pContext->pVTableUserData, pDevice); + if (pDevice->pContext->pVTable->onDeviceWakeup != NULL) { + pDevice->pContext->pVTable->onDeviceWakeup(pDevice); } /* @@ -44630,6 +45994,58 @@ MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void return MA_SUCCESS; } + +static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) +{ + ma_device_notification notification; + + MA_ZERO_OBJECT(¬ification); + notification.pDevice = pDevice; + notification.type = type; + + return notification; +} + +MA_API void ma_device_post_notification(ma_device_notification notification) +{ + if (notification.pDevice == NULL || notification.pDevice->onNotification == NULL) { + return; + } + + notification.pDevice->onNotification(¬ification); +} + +MA_API void ma_device_post_notification_started(ma_device* pDevice) +{ + ma_device_post_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); +} + +MA_API void ma_device_post_notification_stopped(ma_device* pDevice) +{ + ma_device_post_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); +} + +MA_API void ma_device_post_notification_rerouted(ma_device* pDevice) +{ + ma_device_post_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); +} + +MA_API void ma_device_post_notification_interruption_began(ma_device* pDevice) +{ + ma_device_post_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); +} + +MA_API void ma_device_post_notification_interruption_ended(ma_device* pDevice) +{ + ma_device_post_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); +} + +MA_API void ma_device_post_notification_unlocked(ma_device* pDevice) +{ + ma_device_post_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); +} + + MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { if (pDescriptor == NULL) { diff --git a/tests/android/MiniaudioTester/app/src/main/cpp/native-lib.cpp b/tests/android/MiniaudioTester/app/src/main/cpp/native-lib.cpp index 3865f2e3..d668571e 100644 --- a/tests/android/MiniaudioTester/app/src/main/cpp/native-lib.cpp +++ b/tests/android/MiniaudioTester/app/src/main/cpp/native-lib.cpp @@ -75,7 +75,7 @@ Java_io_miniaud_miniaudiotester_MainActivity_PlayAudio(JNIEnv *env, jobject, jlo /* If we don't have a device, create one. */ if (!pAudioState->hasDevice) { ma_context_config contextConfig = ma_context_config_init(); - ma_backend pBackends[1]; + ma_device_backend_config pBackends[1]; size_t backendCount; if (backend == BACKEND_AUTO) { @@ -83,9 +83,9 @@ Java_io_miniaud_miniaudiotester_MainActivity_PlayAudio(JNIEnv *env, jobject, jlo } else { backendCount = 1; if (backend == BACKEND_AAUDIO) { - pBackends[0] = ma_backend_aaudio; + pBackends[0] = ma_device_backend_config_init(ma_device_backend_aaudio, nullptr); } else if (backend == BACKEND_OPENSL) { - pBackends[0] = ma_backend_opensl; + pBackends[0] = ma_device_backend_config_init(ma_device_backend_opensl, nullptr); } else { backendCount = 0; } diff --git a/tests/deviceio/deviceio.c b/tests/deviceio/deviceio.c index 5590abf5..28eaf96f 100644 --- a/tests/deviceio/deviceio.c +++ b/tests/deviceio/deviceio.c @@ -112,7 +112,7 @@ ma_bool32 try_parse_mode(const char* arg, ma_device_type* pDeviceType) return MA_FALSE; } -ma_bool32 try_parse_backend(const char* arg, ma_backend* pBackends, ma_uint32 backendCap, ma_uint32* pBackendCount) +ma_bool32 try_parse_backend(const char* arg, ma_device_backend_config* pBackends, ma_uint32 backendCap, ma_uint32* pBackendCount) { ma_uint32 backendCount; @@ -126,59 +126,59 @@ ma_bool32 try_parse_backend(const char* arg, ma_backend* pBackends, ma_uint32 ba } if (strcmp(arg, "wasapi") == 0) { - pBackends[backendCount++] = ma_backend_wasapi; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_wasapi, NULL); goto done; } if (strcmp(arg, "dsound") == 0 || strcmp(arg, "directsound") == 0) { - pBackends[backendCount++] = ma_backend_dsound; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_dsound, NULL); goto done; } if (strcmp(arg, "winmm") == 0) { - pBackends[backendCount++] = ma_backend_winmm; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_winmm, NULL); goto done; } if (strcmp(arg, "coreaudio") == 0) { - pBackends[backendCount++] = ma_backend_coreaudio; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_coreaudio, NULL); goto done; } if (strcmp(arg, "sndio") == 0) { - pBackends[backendCount++] = ma_backend_sndio; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_sndio, NULL); goto done; } if (strcmp(arg, "audio4") == 0) { - pBackends[backendCount++] = ma_backend_audio4; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_audio4, NULL); goto done; } if (strcmp(arg, "oss") == 0) { - pBackends[backendCount++] = ma_backend_oss; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_oss, NULL); goto done; } if (strcmp(arg, "pulseaudio") == 0 || strcmp(arg, "pulse") == 0) { - pBackends[backendCount++] = ma_backend_pulseaudio; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_pulseaudio, NULL); goto done; } if (strcmp(arg, "alsa") == 0) { - pBackends[backendCount++] = ma_backend_alsa; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_alsa, NULL); goto done; } if (strcmp(arg, "jack") == 0) { - pBackends[backendCount++] = ma_backend_jack; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_jack, NULL); goto done; } if (strcmp(arg, "aaudio") == 0) { - pBackends[backendCount++] = ma_backend_aaudio; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_aaudio, NULL); goto done; } if (strcmp(arg, "opensl") == 0) { - pBackends[backendCount++] = ma_backend_opensl; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_opensl, NULL); goto done; } if (strcmp(arg, "webaudio") == 0) { - pBackends[backendCount++] = ma_backend_webaudio; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_webaudio, NULL); goto done; } if (strcmp(arg, "null") == 0) { - pBackends[backendCount++] = ma_backend_null; + pBackends[backendCount++] = ma_device_backend_config_init(ma_device_backend_null, NULL); goto done; } @@ -237,6 +237,26 @@ ma_bool32 try_parse_noise(const char* arg, ma_noise_type* pNoiseType) return MA_FALSE; } +void print_enabled_backends() +{ + const ma_device_backend_config pStockBackends[] = MA_STOCK_DEVICE_BACKENDS; + ma_uint32 stockBackendCount = ma_countof(pStockBackends); + ma_uint32 iEnabledStockBackend; + + printf("Enabled Backends:\n"); + + for (iEnabledStockBackend = 0; iEnabledStockBackend < stockBackendCount; iEnabledStockBackend += 1) { + ma_device_backend_config backend = pStockBackends[iEnabledStockBackend]; + if (backend.pVTable != NULL && backend.pVTable->onBackendInfo != NULL) { + ma_device_backend_info backendInfo; + backend.pVTable->onBackendInfo(&backendInfo); + printf(" %s\n", backendInfo.pName); + } + } + + printf("\n"); +} + ma_result print_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo) { ma_result result; @@ -399,10 +419,7 @@ int main(int argc, char** argv) { int iarg; ma_result result; - ma_backend enabledBackends[MA_BACKEND_COUNT]; - size_t enabledBackendCount; - size_t iEnabledBackend; - ma_backend backends[MA_BACKEND_COUNT]; + ma_device_backend_config backends[256]; ma_uint32 backendCount = 0; ma_context_config contextConfig; ma_device_type deviceType = ma_device_type_playback; @@ -455,18 +472,7 @@ int main(int argc, char** argv) } /* Here we'll quickly print the available backends. */ - printf("Enabled Backends:\n"); - result = ma_get_enabled_backends(enabledBackends, ma_countof(enabledBackends), &enabledBackendCount); - if (result != MA_SUCCESS) { - printf("Failed to retrieve available backends.\n"); - return -1; - } - - for (iEnabledBackend = 0; iEnabledBackend < enabledBackendCount; iEnabledBackend += 1) { - printf(" %s\n", ma_get_backend_name(enabledBackends[iEnabledBackend])); - } - printf("\n"); - + print_enabled_backends(); /* Initialize the context first. If no backends were passed into the command line we just use defaults. */ contextConfig = ma_context_config_init();