Commit b819a080 authored by David Reid's avatar David Reid

Rearrange some threading code to decouple it from device IO.

parent 2efe1ab8
...@@ -5204,6 +5204,19 @@ ma_device_get_master_volume() ...@@ -5204,6 +5204,19 @@ ma_device_get_master_volume()
MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB); MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB);
/*
Retrieves a friendly name for a backend.
*/
MA_API const char* ma_get_backend_name(ma_backend backend);
/*
Determines whether or not loopback mode is support by a backend.
*/
MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend);
#endif /* MA_NO_DEVICE_IO */
/* /*
Creates a mutex. Creates a mutex.
...@@ -5227,20 +5240,6 @@ Unlocks a mutex. ...@@ -5227,20 +5240,6 @@ Unlocks a mutex.
MA_API void ma_mutex_unlock(ma_mutex* pMutex); MA_API void ma_mutex_unlock(ma_mutex* pMutex);
/*
Retrieves a friendly name for a backend.
*/
MA_API const char* ma_get_backend_name(ma_backend backend);
/*
Determines whether or not loopback mode is support by a backend.
*/
MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend);
#endif /* MA_NO_DEVICE_IO */
/************************************************************************************************************************************************************ /************************************************************************************************************************************************************
Utiltities Utiltities
...@@ -7665,162 +7664,37 @@ MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateO ...@@ -7665,162 +7664,37 @@ MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateO
#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 #define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
#endif #endif
/************************************************************************************************************************************************************
*************************************************************************************************************************************************************
DEVICE I/O
==========
*************************************************************************************************************************************************************
************************************************************************************************************************************************************/
#ifndef MA_NO_DEVICE_IO
#ifdef MA_WIN32
#include <objbase.h>
#include <mmreg.h>
#include <mmsystem.h>
#endif
#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
#include <mach/mach_time.h> /* For mach_absolute_time() */
#endif
#ifdef MA_POSIX
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <dlfcn.h>
#endif
/*
Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply
disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
*/
/*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
/* Disable run-time linking on certain backends. */
#ifndef MA_NO_RUNTIME_LINKING
#if defined(MA_ANDROID) || defined(MA_EMSCRIPTEN)
#define MA_NO_RUNTIME_LINKING
#endif
#endif
/*
Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not
certain unused functions and variables can be excluded from the build to avoid warnings.
*/
#ifdef MA_ENABLE_WASAPI
#define MA_HAS_WASAPI /* Every compiler should support WASAPI */
#endif
#ifdef MA_ENABLE_DSOUND
#define MA_HAS_DSOUND /* Every compiler should support DirectSound. */
#endif
#ifdef MA_ENABLE_WINMM
#define MA_HAS_WINMM /* Every compiler I'm aware of supports WinMM. */
#endif
#ifdef MA_ENABLE_ALSA
#define MA_HAS_ALSA
#ifdef MA_NO_RUNTIME_LINKING
#ifdef __has_include
#if !__has_include(<alsa/asoundlib.h>)
#undef MA_HAS_ALSA
#endif
#endif
#endif
#endif
#ifdef MA_ENABLE_PULSEAUDIO
#define MA_HAS_PULSEAUDIO
#ifdef MA_NO_RUNTIME_LINKING
#ifdef __has_include
#if !__has_include(<pulse/pulseaudio.h>)
#undef MA_HAS_PULSEAUDIO
#endif
#endif
#endif
#endif
#ifdef MA_ENABLE_JACK
#define MA_HAS_JACK
#ifdef MA_NO_RUNTIME_LINKING
#ifdef __has_include
#if !__has_include(<jack/jack.h>)
#undef MA_HAS_JACK
#endif
#endif
#endif
#endif
#ifdef MA_ENABLE_COREAUDIO
#define MA_HAS_COREAUDIO
#endif
#ifdef MA_ENABLE_SNDIO
#define MA_HAS_SNDIO
#endif
#ifdef MA_ENABLE_AUDIO4
#define MA_HAS_AUDIO4
#endif
#ifdef MA_ENABLE_OSS
#define MA_HAS_OSS
#endif
#ifdef MA_ENABLE_AAUDIO
#define MA_HAS_AAUDIO
#endif
#ifdef MA_ENABLE_OPENSL
#define MA_HAS_OPENSL
#endif
#ifdef MA_ENABLE_WEBAUDIO
#define MA_HAS_WEBAUDIO
#endif
#ifdef MA_ENABLE_NULL
#define MA_HAS_NULL /* Everything supports the null backend. */
#endif
MA_API const char* ma_get_backend_name(ma_backend backend) #if defined(MA_WIN32)
static ma_result ma_result_from_GetLastError(DWORD error)
{ {
switch (backend) switch (error)
{ {
case ma_backend_wasapi: return "WASAPI"; case ERROR_SUCCESS: return MA_SUCCESS;
case ma_backend_dsound: return "DirectSound"; case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST;
case ma_backend_winmm: return "WinMM"; case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES;
case ma_backend_coreaudio: return "Core Audio"; case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY;
case ma_backend_sndio: return "sndio"; case ERROR_DISK_FULL: return MA_NO_SPACE;
case ma_backend_audio4: return "audio(4)"; case ERROR_HANDLE_EOF: return MA_END_OF_FILE;
case ma_backend_oss: return "OSS"; case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK;
case ma_backend_pulseaudio: return "PulseAudio"; case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS;
case ma_backend_alsa: return "ALSA"; case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED;
case ma_backend_jack: return "JACK"; case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
case ma_backend_aaudio: return "AAudio"; case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
case ma_backend_opensl: return "OpenSL|ES"; default: break;
case ma_backend_webaudio: return "Web Audio";
case ma_backend_null: return "Null";
default: return "Unknown";
} }
}
MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) return MA_ERROR;
{
switch (backend)
{
case ma_backend_wasapi: return MA_TRUE;
case ma_backend_dsound: return MA_FALSE;
case ma_backend_winmm: return MA_FALSE;
case ma_backend_coreaudio: return MA_FALSE;
case ma_backend_sndio: return MA_FALSE;
case ma_backend_audio4: return MA_FALSE;
case ma_backend_oss: return MA_FALSE;
case ma_backend_pulseaudio: return MA_FALSE;
case ma_backend_alsa: return MA_FALSE;
case ma_backend_jack: return MA_FALSE;
case ma_backend_aaudio: return MA_FALSE;
case ma_backend_opensl: return MA_FALSE;
case ma_backend_webaudio: return MA_FALSE;
case ma_backend_null: return MA_FALSE;
default: return MA_FALSE;
}
} }
#endif /* MA_WIN32 */
/*******************************************************************************
Threading
*******************************************************************************/
#ifdef MA_WIN32 #ifdef MA_WIN32
#define MA_THREADCALL WINAPI #define MA_THREADCALL WINAPI
typedef unsigned long ma_thread_result; typedef unsigned long ma_thread_result;
...@@ -7831,1165 +7705,1295 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) ...@@ -7831,1165 +7705,1295 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
#ifdef MA_WIN32 #ifdef MA_WIN32
static ma_result ma_result_from_GetLastError(DWORD error) static int ma_thread_priority_to_win32(ma_thread_priority priority)
{ {
switch (error) switch (priority) {
{ case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
case ERROR_SUCCESS: return MA_SUCCESS; case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
case ERROR_DISK_FULL: return MA_NO_SPACE; case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
case ERROR_HANDLE_EOF: return MA_END_OF_FILE; case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; default: return THREAD_PRIORITY_NORMAL;
case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS;
case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED;
case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
default: break;
} }
}
return MA_ERROR; static ma_result ma_thread_create__win32(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
{
pThread->win32.hThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL);
if (pThread->win32.hThread == NULL) {
return ma_result_from_GetLastError(GetLastError());
}
SetThreadPriority((HANDLE)pThread->win32.hThread, ma_thread_priority_to_win32(pContext->threadPriority));
return MA_SUCCESS;
} }
/* WASAPI error codes. */ static void ma_thread_wait__win32(ma_thread* pThread)
#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) {
#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) WaitForSingleObject(pThread->win32.hThread, INFINITE);
#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) }
#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004)
#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005)
#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006)
#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007)
#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008)
#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009)
#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A)
#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B)
#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C)
#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D)
#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E)
#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F)
#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010)
#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011)
#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012)
#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)
#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014)
#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015)
#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016)
#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017)
#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018)
#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019)
#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020)
#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021)
#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)
#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023)
#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024)
#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025)
#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026)
#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027)
#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028)
#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029)
#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030)
#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040)
#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001)
#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002)
#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003)
#define MA_DS_OK ((HRESULT)0)
#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A)
#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A)
#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E)
#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/
#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032)
#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/
#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046)
#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/
#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064)
#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/
#define MA_DSERR_NODRIVER ((HRESULT)0x88780078)
#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082)
#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/
#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096)
#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0)
#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA)
#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/
#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/
#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4)
#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE)
#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8)
#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2)
#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161)
#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC)
static ma_result ma_result_from_HRESULT(HRESULT hr) static void ma_sleep__win32(ma_uint32 milliseconds)
{ {
switch (hr) Sleep((DWORD)milliseconds);
{ }
case NOERROR: return MA_SUCCESS;
/*case S_OK: return MA_SUCCESS;*/
case E_POINTER: return MA_INVALID_ARGS;
case E_UNEXPECTED: return MA_ERROR;
case E_NOTIMPL: return MA_NOT_IMPLEMENTED;
case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY;
case E_INVALIDARG: return MA_INVALID_ARGS;
case E_NOINTERFACE: return MA_API_NOT_FOUND;
case E_HANDLE: return MA_INVALID_ARGS;
case E_ABORT: return MA_ERROR;
case E_FAIL: return MA_ERROR;
case E_ACCESSDENIED: return MA_ACCESS_DENIED;
/* WASAPI */ static ma_result ma_mutex_init__win32(ma_mutex* pMutex)
case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; {
case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; *pMutex = CreateEventW(NULL, FALSE, TRUE, NULL);
case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; if (*pMutex == NULL) {
case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; return ma_result_from_GetLastError(GetLastError());
case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; }
case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG;
case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED;
case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY;
case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST;
case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED;
case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED;
case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED;
case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR;
case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR;
case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY;
case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA;
case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE;
case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS;
case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR;
/* DirectSound */ return MA_SUCCESS;
/*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ }
case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS;
case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE;
case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION;
/*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */
case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION;
/*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */
case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION;
/*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */
case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED;
/*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */
case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND;
case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
case MA_DSERR_NOAGGREGATION: return MA_ERROR;
case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE;
case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED;
case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
/*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */
/*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */
case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE;
case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION;
case MA_DSERR_SENDLOOP: return MA_DEADLOCK;
case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS;
case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE;
case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE;
default: return MA_ERROR; static void ma_mutex_uninit__win32(ma_mutex* pMutex)
} {
CloseHandle((HANDLE)*pMutex);
} }
typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); static void ma_mutex_lock__win32(ma_mutex* pMutex)
typedef void (WINAPI * MA_PFN_CoUninitialize)(void); {
typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); WaitForSingleObject((HANDLE)*pMutex, INFINITE);
typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv); }
typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); static void ma_mutex_unlock__win32(ma_mutex* pMutex)
typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); {
SetEvent((HANDLE)*pMutex);
}
/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
#endif
static ma_result ma_event_init__win32(ma_context* pContext, ma_event* pEvent)
{
(void)pContext;
#define MA_STATE_UNINITIALIZED 0 pEvent->win32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
#define MA_STATE_STOPPED 1 /* The device's default state after initialization. */ if (pEvent->win32.hEvent == NULL) {
#define MA_STATE_STARTED 2 /* The worker thread is in it's main loop waiting for the driver to request or deliver audio data. */ return ma_result_from_GetLastError(GetLastError());
#define MA_STATE_STARTING 3 /* Transitioning from a stopped state to started. */ }
#define MA_STATE_STOPPING 4 /* Transitioning from a started state to stopped. */
#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" return MA_SUCCESS;
#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" }
static void ma_event_uninit__win32(ma_event* pEvent)
{
CloseHandle(pEvent->win32.hEvent);
}
MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) static ma_bool32 ma_event_wait__win32(ma_event* pEvent)
{ {
switch (logLevel) return WaitForSingleObject(pEvent->win32.hEvent, INFINITE) == WAIT_OBJECT_0;
{
case MA_LOG_LEVEL_VERBOSE: return "";
case MA_LOG_LEVEL_INFO: return "INFO";
case MA_LOG_LEVEL_WARNING: return "WARNING";
case MA_LOG_LEVEL_ERROR: return "ERROR";
default: return "ERROR";
}
} }
/* Posts a log message. */ static ma_bool32 ma_event_signal__win32(ma_event* pEvent)
static void ma_post_log_message(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
{ {
if (pContext == NULL) { return SetEvent(pEvent->win32.hEvent);
if (pDevice != NULL) { }
pContext = pDevice->pContext;
}
}
/* All logs must be output when debug output is enabled. */
#if defined(MA_DEBUG_OUTPUT)
printf("%s: %s\n", ma_log_level_to_string(logLevel), message);
#endif
if (pContext == NULL) { static ma_result ma_semaphore_init__win32(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore)
return; {
(void)pContext;
pSemaphore->win32.hSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);
if (pSemaphore->win32.hSemaphore == NULL) {
return ma_result_from_GetLastError(GetLastError());
} }
#if defined(MA_LOG_LEVEL) return MA_SUCCESS;
if (logLevel <= MA_LOG_LEVEL) { }
ma_log_proc onLog;
onLog = pContext->logCallback; static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
if (onLog) { {
onLog(pContext, pDevice, logLevel, message); CloseHandle((HANDLE)pSemaphore->win32.hSemaphore);
}
}
#endif
} }
/* static ma_bool32 ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a
logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf().
*/
#if defined(_MSC_VER) && _MSC_VER < 1900
int ma_vscprintf(const char* format, va_list args)
{ {
#if _MSC_VER > 1200 return WaitForSingleObject((HANDLE)pSemaphore->win32.hSemaphore, INFINITE) == WAIT_OBJECT_0;
return _vscprintf(format, args); }
#else
int result;
char* pTempBuffer = NULL;
size_t tempBufferCap = 1024;
if (format == NULL) { static ma_bool32 ma_semaphore_release__win32(ma_semaphore* pSemaphore)
errno = EINVAL; {
return -1; return ReleaseSemaphore((HANDLE)pSemaphore->win32.hSemaphore, 1, NULL) != 0;
} }
#endif
for (;;) {
char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, NULL); /* TODO: Add support for custom memory allocators? */
if (pNewTempBuffer == NULL) {
ma_free(pTempBuffer, NULL);
errno = ENOMEM;
return -1; /* Out of memory. */
}
pTempBuffer = pNewTempBuffer; #ifdef MA_POSIX
#include <sched.h>
#include <sys/time.h>
result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); typedef int (* ma_pthread_create_proc)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
ma_free(pTempBuffer, NULL); typedef int (* ma_pthread_join_proc)(pthread_t thread, void **retval);
typedef int (* ma_pthread_mutex_init_proc)(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr);
typedef int (* ma_pthread_mutex_destroy_proc)(pthread_mutex_t *__mutex);
typedef int (* ma_pthread_mutex_lock_proc)(pthread_mutex_t *__mutex);
typedef int (* ma_pthread_mutex_unlock_proc)(pthread_mutex_t *__mutex);
typedef int (* ma_pthread_cond_init_proc)(pthread_cond_t *__restrict __cond, const pthread_condattr_t *__restrict __cond_attr);
typedef int (* ma_pthread_cond_destroy_proc)(pthread_cond_t *__cond);
typedef int (* ma_pthread_cond_signal_proc)(pthread_cond_t *__cond);
typedef int (* ma_pthread_cond_wait_proc)(pthread_cond_t *__restrict __cond, pthread_mutex_t *__restrict __mutex);
typedef int (* ma_pthread_attr_init_proc)(pthread_attr_t *attr);
typedef int (* ma_pthread_attr_destroy_proc)(pthread_attr_t *attr);
typedef int (* ma_pthread_attr_setschedpolicy_proc)(pthread_attr_t *attr, int policy);
typedef int (* ma_pthread_attr_getschedparam_proc)(const pthread_attr_t *attr, struct sched_param *param);
typedef int (* ma_pthread_attr_setschedparam_proc)(pthread_attr_t *attr, const struct sched_param *param);
if (result != -1) { static ma_result ma_thread_create__posix(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
break; /* Got it. */ {
} int result;
pthread_attr_t* pAttr = NULL;
/* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ #if !defined(__EMSCRIPTEN__)
tempBufferCap *= 2; /* Try setting the thread priority. It's not critical if anything fails here. */
pthread_attr_t attr;
if (((ma_pthread_attr_init_proc)pContext->posix.pthread_attr_init)(&attr) == 0) {
int scheduler = -1;
if (pContext->threadPriority == ma_thread_priority_idle) {
#ifdef SCHED_IDLE
if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_IDLE) == 0) {
scheduler = SCHED_IDLE;
} }
return result;
#endif #endif
} } else if (pContext->threadPriority == ma_thread_priority_realtime) {
#ifdef SCHED_FIFO
if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_FIFO) == 0) {
scheduler = SCHED_FIFO;
}
#endif
#ifdef MA_LINUX
} else {
scheduler = sched_getscheduler(0);
#endif #endif
/* Posts a formatted log message. */
static void ma_post_log_messagev(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* pFormat, va_list args)
{
#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__)
{
char pFormattedMessage[1024];
vsnprintf(pFormattedMessage, sizeof(pFormattedMessage), pFormat, args);
ma_post_log_message(pContext, pDevice, logLevel, pFormattedMessage);
} }
#else
{
/*
Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll
need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing
a fixed sized stack allocated buffer.
*/
#if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */
int formattedLen;
va_list args2;
#if _MSC_VER >= 1800
va_copy(args2, args);
#else
args2 = args;
#endif
formattedLen = ma_vscprintf(pFormat, args2);
va_end(args2);
if (formattedLen > 0) { if (scheduler != -1) {
char* pFormattedMessage = NULL; int priorityMin = sched_get_priority_min(scheduler);
ma_allocation_callbacks* pAllocationCallbacks = NULL; int priorityMax = sched_get_priority_max(scheduler);
int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
/* Make sure we have a context so we can allocate memory. */ struct sched_param sched;
if (pContext == NULL) { if (((ma_pthread_attr_getschedparam_proc)pContext->posix.pthread_attr_getschedparam)(&attr, &sched) == 0) {
if (pDevice != NULL) { if (pContext->threadPriority == ma_thread_priority_idle) {
pContext = pDevice->pContext; sched.sched_priority = priorityMin;
} else if (pContext->threadPriority == ma_thread_priority_realtime) {
sched.sched_priority = priorityMax;
} else {
sched.sched_priority += ((int)pContext->threadPriority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
if (sched.sched_priority < priorityMin) {
sched.sched_priority = priorityMin;
} }
if (sched.sched_priority > priorityMax) {
sched.sched_priority = priorityMax;
} }
if (pContext != NULL) {
pAllocationCallbacks = &pContext->allocationCallbacks;
} }
pFormattedMessage = (char*)ma_malloc(formattedLen + 1, pAllocationCallbacks); if (((ma_pthread_attr_setschedparam_proc)pContext->posix.pthread_attr_setschedparam)(&attr, &sched) == 0) {
if (pFormattedMessage != NULL) { pAttr = &attr;
/* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ }
#if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ }
vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); }
#else
vsprintf(pFormattedMessage, pFormat, args);
#endif
ma_post_log_message(pContext, pDevice, logLevel, pFormattedMessage); ((ma_pthread_attr_destroy_proc)pContext->posix.pthread_attr_destroy)(&attr);
ma_free(pFormattedMessage, pAllocationCallbacks);
} }
#endif
result = ((ma_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, pAttr, entryProc, pData);
if (result != 0) {
return ma_result_from_errno(result);
} }
return MA_SUCCESS;
}
static void ma_thread_wait__posix(ma_thread* pThread)
{
((ma_pthread_join_proc)pThread->pContext->posix.pthread_join)(pThread->posix.thread, NULL);
}
#if !defined(MA_EMSCRIPTEN)
static void ma_sleep__posix(ma_uint32 milliseconds)
{
#ifdef MA_EMSCRIPTEN
(void)milliseconds;
MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */
#else
#if _POSIX_C_SOURCE >= 199309L
struct timespec ts;
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = milliseconds % 1000 * 1000000;
nanosleep(&ts, NULL);
#else #else
/* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ struct timeval tv;
(void)pContext; tv.tv_sec = milliseconds / 1000;
(void)pDevice; tv.tv_usec = milliseconds % 1000 * 1000;
(void)logLevel; select(0, NULL, NULL, NULL, &tv);
(void)pFormat;
(void)args;
#endif #endif
}
#endif #endif
} }
#endif /* MA_EMSCRIPTEN */
MA_API void ma_post_log_messagef(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* pFormat, ...)
static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
{ {
va_list args; int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);
va_start(args, pFormat); if (result != 0) {
{ return ma_result_from_errno(result);
ma_post_log_messagev(pContext, pDevice, logLevel, pFormat, args);
} }
va_end(args);
return MA_SUCCESS;
} }
/* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */ static void ma_mutex_uninit__posix(ma_mutex* pMutex)
static ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
{ {
ma_post_log_message(pContext, pDevice, logLevel, message); pthread_mutex_destroy((pthread_mutex_t*)pMutex);
return resultCode;
} }
static ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode) static void ma_mutex_lock__posix(ma_mutex* pMutex)
{ {
return ma_context_post_error(NULL, pDevice, logLevel, message, resultCode); pthread_mutex_lock((pthread_mutex_t*)pMutex);
} }
static void ma_mutex_unlock__posix(ma_mutex* pMutex)
{
pthread_mutex_unlock((pthread_mutex_t*)pMutex);
}
/*******************************************************************************
Timing
*******************************************************************************/
#ifdef MA_WIN32
static LARGE_INTEGER g_ma_TimerFrequency = {{0}};
static void ma_timer_init(ma_timer* pTimer)
{
LARGE_INTEGER counter;
if (g_ma_TimerFrequency.QuadPart == 0) { static ma_result ma_event_init__posix(ma_context* pContext, ma_event* pEvent)
QueryPerformanceFrequency(&g_ma_TimerFrequency); {
} int result;
QueryPerformanceCounter(&counter); result = ((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pEvent->posix.mutex, NULL);
pTimer->counter = counter.QuadPart; if (result != 0) {
return ma_result_from_errno(result);
} }
static double ma_timer_get_time_in_seconds(ma_timer* pTimer) result = ((ma_pthread_cond_init_proc)pContext->posix.pthread_cond_init)(&pEvent->posix.condition, NULL);
{ if (result != 0) {
LARGE_INTEGER counter; ((ma_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex);
if (!QueryPerformanceCounter(&counter)) { return ma_result_from_errno(result);
return 0;
} }
return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; pEvent->posix.value = 0;
} return MA_SUCCESS;
#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) }
static ma_uint64 g_ma_TimerFrequency = 0;
static void ma_timer_init(ma_timer* pTimer)
{
mach_timebase_info_data_t baseTime;
mach_timebase_info(&baseTime);
g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
pTimer->counter = mach_absolute_time(); static void ma_event_uninit__posix(ma_event* pEvent)
} {
((ma_pthread_cond_destroy_proc)pEvent->pContext->posix.pthread_cond_destroy)(&pEvent->posix.condition);
((ma_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex);
}
static double ma_timer_get_time_in_seconds(ma_timer* pTimer) static ma_bool32 ma_event_wait__posix(ma_event* pEvent)
{
((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
{ {
ma_uint64 newTimeCounter = mach_absolute_time(); while (pEvent->posix.value == 0) {
ma_uint64 oldTimeCounter = pTimer->counter; ((ma_pthread_cond_wait_proc)pEvent->pContext->posix.pthread_cond_wait)(&pEvent->posix.condition, &pEvent->posix.mutex);
return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
} }
#elif defined(MA_EMSCRIPTEN) pEvent->posix.value = 0; /* Auto-reset. */
static MA_INLINE void ma_timer_init(ma_timer* pTimer)
{
pTimer->counterD = emscripten_get_now();
} }
((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) return MA_TRUE;
{ }
return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
}
#else
#if _POSIX_C_SOURCE >= 199309L
#if defined(CLOCK_MONOTONIC)
#define MA_CLOCK_ID CLOCK_MONOTONIC
#else
#define MA_CLOCK_ID CLOCK_REALTIME
#endif
static void ma_timer_init(ma_timer* pTimer)
{
struct timespec newTime;
clock_gettime(MA_CLOCK_ID, &newTime);
pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
}
static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
ma_uint64 newTimeCounter;
ma_uint64 oldTimeCounter;
struct timespec newTime;
clock_gettime(MA_CLOCK_ID, &newTime);
newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
oldTimeCounter = pTimer->counter;
return (newTimeCounter - oldTimeCounter) / 1000000000.0; static ma_bool32 ma_event_signal__posix(ma_event* pEvent)
} {
#else ((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
static void ma_timer_init(ma_timer* pTimer)
{ {
struct timeval newTime; pEvent->posix.value = 1;
gettimeofday(&newTime, NULL); ((ma_pthread_cond_signal_proc)pEvent->pContext->posix.pthread_cond_signal)(&pEvent->posix.condition);
pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
} }
((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
static double ma_timer_get_time_in_seconds(ma_timer* pTimer) return MA_TRUE;
{ }
ma_uint64 newTimeCounter;
ma_uint64 oldTimeCounter;
struct timeval newTime;
gettimeofday(&newTime, NULL);
newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; static ma_result ma_semaphore_init__posix(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore)
oldTimeCounter = pTimer->counter; {
(void)pContext;
return (newTimeCounter - oldTimeCounter) / 1000000.0; #if defined(MA_APPLE)
/* Not yet implemented for Apple platforms since sem_init() is deprecated. Need to use a named semaphore via sem_open() instead. */
(void)initialValue;
(void)pSemaphore;
return MA_INVALID_OPERATION;
#else
if (sem_init(&pSemaphore->posix.semaphore, 0, (unsigned int)initialValue) == 0) {
return ma_result_from_errno(errno);
} }
#endif
#endif #endif
return MA_SUCCESS;
}
/******************************************************************************* static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
{
Dynamic Linking sem_close(&pSemaphore->posix.semaphore);
}
*******************************************************************************/ static ma_bool32 ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
{ {
ma_handle handle; return sem_wait(&pSemaphore->posix.semaphore) != -1;
}
#if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE static ma_bool32 ma_semaphore_release__posix(ma_semaphore* pSemaphore)
if (pContext != NULL) { {
char message[256]; return sem_post(&pSemaphore->posix.semaphore) != -1;
ma_strappend(message, sizeof(message), "Loading library: ", filename); }
ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
}
#endif #endif
#ifdef _WIN32 static ma_result ma_thread_create(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
#ifdef MA_WIN32_DESKTOP {
handle = (ma_handle)LoadLibraryA(filename); if (pContext == NULL || pThread == NULL || entryProc == NULL) {
#else return MA_FALSE;
/* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
WCHAR filenameW[4096];
if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
handle = NULL;
} else {
handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
} }
pThread->pContext = pContext;
#ifdef MA_WIN32
return ma_thread_create__win32(pContext, pThread, entryProc, pData);
#endif #endif
#else #ifdef MA_POSIX
handle = (ma_handle)dlopen(filename, RTLD_NOW); return ma_thread_create__posix(pContext, pThread, entryProc, pData);
#endif #endif
}
/* static void ma_thread_wait(ma_thread* pThread)
I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority {
backend is a deliberate design choice. Instead I'm logging it as an informational message. if (pThread == NULL) {
*/ return;
#if MA_LOG_LEVEL >= MA_LOG_LEVEL_INFO
if (handle == NULL) {
char message[256];
ma_strappend(message, sizeof(message), "Failed to load library: ", filename);
ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, message);
} }
#endif
(void)pContext; /* It's possible for pContext to be unused. */ #ifdef MA_WIN32
return handle; ma_thread_wait__win32(pThread);
#endif
#ifdef MA_POSIX
ma_thread_wait__posix(pThread);
#endif
} }
MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) #if !defined(MA_EMSCRIPTEN)
static void ma_sleep(ma_uint32 milliseconds)
{ {
#ifdef _WIN32 #ifdef MA_WIN32
FreeLibrary((HMODULE)handle); ma_sleep__win32(milliseconds);
#else #endif
dlclose((void*)handle); #ifdef MA_POSIX
ma_sleep__posix(milliseconds);
#endif #endif
(void)pContext;
} }
#endif
MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
{
ma_proc proc;
#if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE MA_API ma_result ma_mutex_init(ma_mutex* pMutex)
if (pContext != NULL) { {
char message[256]; if (pMutex == NULL) {
ma_strappend(message, sizeof(message), "Loading symbol: ", symbol); return MA_INVALID_ARGS;
ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
} }
#endif
#ifdef _WIN32 #ifdef MA_WIN32
proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); return ma_mutex_init__win32(pMutex);
#else
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
proc = (ma_proc)dlsym((void*)handle, symbol);
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic pop
#endif
#endif #endif
#ifdef MA_POSIX
#if MA_LOG_LEVEL >= MA_LOG_LEVEL_WARNING return ma_mutex_init__posix(pMutex);
if (handle == NULL) {
char message[256];
ma_strappend(message, sizeof(message), "Failed to load symbol: ", symbol);
ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_WARNING, message);
}
#endif #endif
(void)pContext; /* It's possible for pContext to be unused. */
return proc;
} }
MA_API void ma_mutex_uninit(ma_mutex* pMutex)
/*******************************************************************************
Threading
*******************************************************************************/
#ifdef MA_WIN32
static int ma_thread_priority_to_win32(ma_thread_priority priority)
{ {
switch (priority) { if (pMutex == NULL) {
case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; return;
case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
default: return THREAD_PRIORITY_NORMAL;
} }
#ifdef MA_WIN32
ma_mutex_uninit__win32(pMutex);
#endif
#ifdef MA_POSIX
ma_mutex_uninit__posix(pMutex);
#endif
} }
static ma_result ma_thread_create__win32(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData) MA_API void ma_mutex_lock(ma_mutex* pMutex)
{ {
pThread->win32.hThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL); if (pMutex == NULL) {
if (pThread->win32.hThread == NULL) { return;
return ma_result_from_GetLastError(GetLastError());
} }
SetThreadPriority((HANDLE)pThread->win32.hThread, ma_thread_priority_to_win32(pContext->threadPriority)); #ifdef MA_WIN32
ma_mutex_lock__win32(pMutex);
return MA_SUCCESS; #endif
#ifdef MA_POSIX
ma_mutex_lock__posix(pMutex);
#endif
} }
static void ma_thread_wait__win32(ma_thread* pThread) MA_API void ma_mutex_unlock(ma_mutex* pMutex)
{ {
WaitForSingleObject(pThread->win32.hThread, INFINITE); if (pMutex == NULL) {
return;
} }
static void ma_sleep__win32(ma_uint32 milliseconds) #ifdef MA_WIN32
{ ma_mutex_unlock__win32(pMutex);
Sleep((DWORD)milliseconds); #endif
#ifdef MA_POSIX
ma_mutex_unlock__posix(pMutex);
#endif
} }
static ma_result ma_mutex_init__win32(ma_mutex* pMutex) MA_API ma_result ma_event_init(ma_context* pContext, ma_event* pEvent)
{ {
*pMutex = CreateEventW(NULL, FALSE, TRUE, NULL); if (pContext == NULL || pEvent == NULL) {
if (*pMutex == NULL) { return MA_FALSE;
return ma_result_from_GetLastError(GetLastError());
} }
return MA_SUCCESS; pEvent->pContext = pContext;
}
static void ma_mutex_uninit__win32(ma_mutex* pMutex) #ifdef MA_WIN32
{ return ma_event_init__win32(pContext, pEvent);
CloseHandle((HANDLE)*pMutex); #endif
#ifdef MA_POSIX
return ma_event_init__posix(pContext, pEvent);
#endif
} }
static void ma_mutex_lock__win32(ma_mutex* pMutex) MA_API void ma_event_uninit(ma_event* pEvent)
{ {
WaitForSingleObject((HANDLE)*pMutex, INFINITE); if (pEvent == NULL || pEvent->pContext == NULL) {
} return;
}
static void ma_mutex_unlock__win32(ma_mutex* pMutex) #ifdef MA_WIN32
{ ma_event_uninit__win32(pEvent);
SetEvent((HANDLE)*pMutex); #endif
#ifdef MA_POSIX
ma_event_uninit__posix(pEvent);
#endif
} }
MA_API ma_bool32 ma_event_wait(ma_event* pEvent)
static ma_result ma_event_init__win32(ma_context* pContext, ma_event* pEvent)
{ {
(void)pContext; if (pEvent == NULL || pEvent->pContext == NULL) {
return MA_FALSE;
pEvent->win32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if (pEvent->win32.hEvent == NULL) {
return ma_result_from_GetLastError(GetLastError());
} }
return MA_SUCCESS; #ifdef MA_WIN32
} return ma_event_wait__win32(pEvent);
#endif
static void ma_event_uninit__win32(ma_event* pEvent) #ifdef MA_POSIX
{ return ma_event_wait__posix(pEvent);
CloseHandle(pEvent->win32.hEvent); #endif
} }
static ma_bool32 ma_event_wait__win32(ma_event* pEvent) MA_API ma_bool32 ma_event_signal(ma_event* pEvent)
{ {
return WaitForSingleObject(pEvent->win32.hEvent, INFINITE) == WAIT_OBJECT_0; if (pEvent == NULL || pEvent->pContext == NULL) {
} return MA_FALSE;
}
static ma_bool32 ma_event_signal__win32(ma_event* pEvent) #ifdef MA_WIN32
{ return ma_event_signal__win32(pEvent);
return SetEvent(pEvent->win32.hEvent); #endif
#ifdef MA_POSIX
return ma_event_signal__posix(pEvent);
#endif
} }
static ma_result ma_semaphore_init__win32(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore) MA_API ma_result ma_semaphore_init(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore)
{ {
(void)pContext; if (pContext == NULL || pSemaphore == NULL) {
return MA_INVALID_ARGS;
pSemaphore->win32.hSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);
if (pSemaphore->win32.hSemaphore == NULL) {
return ma_result_from_GetLastError(GetLastError());
} }
return MA_SUCCESS; #ifdef MA_WIN32
return ma_semaphore_init__win32(pContext, initialValue, pSemaphore);
#endif
#ifdef MA_POSIX
return ma_semaphore_init__posix(pContext, initialValue, pSemaphore);
#endif
} }
static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
{ {
CloseHandle((HANDLE)pSemaphore->win32.hSemaphore); if (pSemaphore == NULL) {
} return;
}
static ma_bool32 ma_semaphore_wait__win32(ma_semaphore* pSemaphore) #ifdef MA_WIN32
{ ma_semaphore_uninit__win32(pSemaphore);
return WaitForSingleObject((HANDLE)pSemaphore->win32.hSemaphore, INFINITE) == WAIT_OBJECT_0; #endif
#ifdef MA_POSIX
ma_semaphore_uninit__posix(pSemaphore);
#endif
} }
static ma_bool32 ma_semaphore_release__win32(ma_semaphore* pSemaphore) MA_API ma_bool32 ma_semaphore_wait(ma_semaphore* pSemaphore)
{ {
return ReleaseSemaphore((HANDLE)pSemaphore->win32.hSemaphore, 1, NULL) != 0; if (pSemaphore == NULL) {
} return MA_FALSE;
#endif }
#ifdef MA_WIN32
return ma_semaphore_wait__win32(pSemaphore);
#endif
#ifdef MA_POSIX #ifdef MA_POSIX
#include <sched.h> return ma_semaphore_wait__posix(pSemaphore);
#endif
typedef int (* ma_pthread_create_proc)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); }
typedef int (* ma_pthread_join_proc)(pthread_t thread, void **retval);
typedef int (* ma_pthread_mutex_init_proc)(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr);
typedef int (* ma_pthread_mutex_destroy_proc)(pthread_mutex_t *__mutex);
typedef int (* ma_pthread_mutex_lock_proc)(pthread_mutex_t *__mutex);
typedef int (* ma_pthread_mutex_unlock_proc)(pthread_mutex_t *__mutex);
typedef int (* ma_pthread_cond_init_proc)(pthread_cond_t *__restrict __cond, const pthread_condattr_t *__restrict __cond_attr);
typedef int (* ma_pthread_cond_destroy_proc)(pthread_cond_t *__cond);
typedef int (* ma_pthread_cond_signal_proc)(pthread_cond_t *__cond);
typedef int (* ma_pthread_cond_wait_proc)(pthread_cond_t *__restrict __cond, pthread_mutex_t *__restrict __mutex);
typedef int (* ma_pthread_attr_init_proc)(pthread_attr_t *attr);
typedef int (* ma_pthread_attr_destroy_proc)(pthread_attr_t *attr);
typedef int (* ma_pthread_attr_setschedpolicy_proc)(pthread_attr_t *attr, int policy);
typedef int (* ma_pthread_attr_getschedparam_proc)(const pthread_attr_t *attr, struct sched_param *param);
typedef int (* ma_pthread_attr_setschedparam_proc)(pthread_attr_t *attr, const struct sched_param *param);
static ma_result ma_thread_create__posix(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData) MA_API ma_bool32 ma_semaphore_release(ma_semaphore* pSemaphore)
{ {
int result; if (pSemaphore == NULL) {
pthread_attr_t* pAttr = NULL; return MA_FALSE;
#if !defined(__EMSCRIPTEN__)
/* Try setting the thread priority. It's not critical if anything fails here. */
pthread_attr_t attr;
if (((ma_pthread_attr_init_proc)pContext->posix.pthread_attr_init)(&attr) == 0) {
int scheduler = -1;
if (pContext->threadPriority == ma_thread_priority_idle) {
#ifdef SCHED_IDLE
if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_IDLE) == 0) {
scheduler = SCHED_IDLE;
}
#endif
} else if (pContext->threadPriority == ma_thread_priority_realtime) {
#ifdef SCHED_FIFO
if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_FIFO) == 0) {
scheduler = SCHED_FIFO;
} }
#ifdef MA_WIN32
return ma_semaphore_release__win32(pSemaphore);
#endif #endif
#ifdef MA_LINUX #ifdef MA_POSIX
} else { return ma_semaphore_release__posix(pSemaphore);
scheduler = sched_getscheduler(0);
#endif #endif
} }
if (scheduler != -1) {
int priorityMin = sched_get_priority_min(scheduler);
int priorityMax = sched_get_priority_max(scheduler);
int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
struct sched_param sched; /************************************************************************************************************************************************************
if (((ma_pthread_attr_getschedparam_proc)pContext->posix.pthread_attr_getschedparam)(&attr, &sched) == 0) { *************************************************************************************************************************************************************
if (pContext->threadPriority == ma_thread_priority_idle) {
sched.sched_priority = priorityMin;
} else if (pContext->threadPriority == ma_thread_priority_realtime) {
sched.sched_priority = priorityMax;
} else {
sched.sched_priority += ((int)pContext->threadPriority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
if (sched.sched_priority < priorityMin) {
sched.sched_priority = priorityMin;
}
if (sched.sched_priority > priorityMax) {
sched.sched_priority = priorityMax;
}
}
if (((ma_pthread_attr_setschedparam_proc)pContext->posix.pthread_attr_setschedparam)(&attr, &sched) == 0) { DEVICE I/O
pAttr = &attr; ==========
}
}
}
((ma_pthread_attr_destroy_proc)pContext->posix.pthread_attr_destroy)(&attr); *************************************************************************************************************************************************************
} ************************************************************************************************************************************************************/
#ifndef MA_NO_DEVICE_IO
#ifdef MA_WIN32
#include <objbase.h>
#include <mmreg.h>
#include <mmsystem.h>
#endif #endif
result = ((ma_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, pAttr, entryProc, pData); #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
if (result != 0) { #include <mach/mach_time.h> /* For mach_absolute_time() */
return ma_result_from_errno(result); #endif
}
return MA_SUCCESS; #ifdef MA_POSIX
} #include <sys/types.h>
#include <unistd.h>
#include <dlfcn.h>
#endif
static void ma_thread_wait__posix(ma_thread* pThread) /*
{ Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
((ma_pthread_join_proc)pThread->pContext->posix.pthread_join)(pThread->posix.thread, NULL); using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
} compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply
disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
*/
/*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
#if !defined(MA_EMSCRIPTEN) /* Disable run-time linking on certain backends. */
static void ma_sleep__posix(ma_uint32 milliseconds) #ifndef MA_NO_RUNTIME_LINKING
{ #if defined(MA_ANDROID) || defined(MA_EMSCRIPTEN)
#ifdef MA_EMSCRIPTEN #define MA_NO_RUNTIME_LINKING
(void)milliseconds;
MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */
#else
#if _POSIX_C_SOURCE >= 199309L
struct timespec ts;
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = milliseconds % 1000 * 1000000;
nanosleep(&ts, NULL);
#else
struct timeval tv;
tv.tv_sec = milliseconds / 1000;
tv.tv_usec = milliseconds % 1000 * 1000;
select(0, NULL, NULL, NULL, &tv);
#endif #endif
#endif #endif
/*
Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not
certain unused functions and variables can be excluded from the build to avoid warnings.
*/
#ifdef MA_ENABLE_WASAPI
#define MA_HAS_WASAPI /* Every compiler should support WASAPI */
#endif
#ifdef MA_ENABLE_DSOUND
#define MA_HAS_DSOUND /* Every compiler should support DirectSound. */
#endif
#ifdef MA_ENABLE_WINMM
#define MA_HAS_WINMM /* Every compiler I'm aware of supports WinMM. */
#endif
#ifdef MA_ENABLE_ALSA
#define MA_HAS_ALSA
#ifdef MA_NO_RUNTIME_LINKING
#ifdef __has_include
#if !__has_include(<alsa/asoundlib.h>)
#undef MA_HAS_ALSA
#endif
#endif
#endif
#endif
#ifdef MA_ENABLE_PULSEAUDIO
#define MA_HAS_PULSEAUDIO
#ifdef MA_NO_RUNTIME_LINKING
#ifdef __has_include
#if !__has_include(<pulse/pulseaudio.h>)
#undef MA_HAS_PULSEAUDIO
#endif
#endif
#endif
#endif
#ifdef MA_ENABLE_JACK
#define MA_HAS_JACK
#ifdef MA_NO_RUNTIME_LINKING
#ifdef __has_include
#if !__has_include(<jack/jack.h>)
#undef MA_HAS_JACK
#endif
#endif
#endif
#endif
#ifdef MA_ENABLE_COREAUDIO
#define MA_HAS_COREAUDIO
#endif
#ifdef MA_ENABLE_SNDIO
#define MA_HAS_SNDIO
#endif
#ifdef MA_ENABLE_AUDIO4
#define MA_HAS_AUDIO4
#endif
#ifdef MA_ENABLE_OSS
#define MA_HAS_OSS
#endif
#ifdef MA_ENABLE_AAUDIO
#define MA_HAS_AAUDIO
#endif
#ifdef MA_ENABLE_OPENSL
#define MA_HAS_OPENSL
#endif
#ifdef MA_ENABLE_WEBAUDIO
#define MA_HAS_WEBAUDIO
#endif
#ifdef MA_ENABLE_NULL
#define MA_HAS_NULL /* Everything supports the null backend. */
#endif
MA_API const char* ma_get_backend_name(ma_backend backend)
{
switch (backend)
{
case ma_backend_wasapi: return "WASAPI";
case ma_backend_dsound: return "DirectSound";
case ma_backend_winmm: return "WinMM";
case ma_backend_coreaudio: return "Core Audio";
case ma_backend_sndio: return "sndio";
case ma_backend_audio4: return "audio(4)";
case ma_backend_oss: return "OSS";
case ma_backend_pulseaudio: return "PulseAudio";
case ma_backend_alsa: return "ALSA";
case ma_backend_jack: return "JACK";
case ma_backend_aaudio: return "AAudio";
case ma_backend_opensl: return "OpenSL|ES";
case ma_backend_webaudio: return "Web Audio";
case ma_backend_null: return "Null";
default: return "Unknown";
}
}
MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
{
switch (backend)
{
case ma_backend_wasapi: return MA_TRUE;
case ma_backend_dsound: return MA_FALSE;
case ma_backend_winmm: return MA_FALSE;
case ma_backend_coreaudio: return MA_FALSE;
case ma_backend_sndio: return MA_FALSE;
case ma_backend_audio4: return MA_FALSE;
case ma_backend_oss: return MA_FALSE;
case ma_backend_pulseaudio: return MA_FALSE;
case ma_backend_alsa: return MA_FALSE;
case ma_backend_jack: return MA_FALSE;
case ma_backend_aaudio: return MA_FALSE;
case ma_backend_opensl: return MA_FALSE;
case ma_backend_webaudio: return MA_FALSE;
case ma_backend_null: return MA_FALSE;
default: return MA_FALSE;
}
}
#ifdef MA_WIN32
/* WASAPI error codes. */
#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001)
#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002)
#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003)
#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004)
#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005)
#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006)
#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007)
#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008)
#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009)
#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A)
#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B)
#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C)
#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D)
#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E)
#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F)
#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010)
#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011)
#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012)
#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)
#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014)
#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015)
#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016)
#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017)
#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018)
#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019)
#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020)
#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021)
#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)
#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023)
#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024)
#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025)
#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026)
#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027)
#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028)
#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029)
#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030)
#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040)
#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001)
#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002)
#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003)
#define MA_DS_OK ((HRESULT)0)
#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A)
#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A)
#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E)
#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/
#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032)
#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/
#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046)
#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/
#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064)
#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/
#define MA_DSERR_NODRIVER ((HRESULT)0x88780078)
#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082)
#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/
#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096)
#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0)
#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA)
#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/
#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/
#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4)
#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE)
#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8)
#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2)
#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161)
#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC)
static ma_result ma_result_from_HRESULT(HRESULT hr)
{
switch (hr)
{
case NOERROR: return MA_SUCCESS;
/*case S_OK: return MA_SUCCESS;*/
case E_POINTER: return MA_INVALID_ARGS;
case E_UNEXPECTED: return MA_ERROR;
case E_NOTIMPL: return MA_NOT_IMPLEMENTED;
case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY;
case E_INVALIDARG: return MA_INVALID_ARGS;
case E_NOINTERFACE: return MA_API_NOT_FOUND;
case E_HANDLE: return MA_INVALID_ARGS;
case E_ABORT: return MA_ERROR;
case E_FAIL: return MA_ERROR;
case E_ACCESSDENIED: return MA_ACCESS_DENIED;
/* WASAPI */
case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE;
case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED;
case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG;
case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED;
case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY;
case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST;
case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED;
case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED;
case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED;
case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR;
case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR;
case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS;
case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY;
case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA;
case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION;
case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE;
case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS;
case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR;
/* DirectSound */
/*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */
case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS;
case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE;
case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION;
/*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */
case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION;
/*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */
case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION;
/*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */
case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED;
/*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */
case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND;
case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
case MA_DSERR_NOAGGREGATION: return MA_ERROR;
case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE;
case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED;
case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
/*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */
/*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */
case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE;
case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION;
case MA_DSERR_SENDLOOP: return MA_DEADLOCK;
case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS;
case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE;
case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE;
default: return MA_ERROR;
}
}
typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
typedef void (WINAPI * MA_PFN_CoUninitialize)(void);
typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv);
typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void);
typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void);
/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
#endif
#define MA_STATE_UNINITIALIZED 0
#define MA_STATE_STOPPED 1 /* The device's default state after initialization. */
#define MA_STATE_STARTED 2 /* The worker thread is in it's main loop waiting for the driver to request or deliver audio data. */
#define MA_STATE_STARTING 3 /* Transitioning from a stopped state to started. */
#define MA_STATE_STOPPING 4 /* Transitioning from a started state to stopped. */
#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
{
switch (logLevel)
{
case MA_LOG_LEVEL_VERBOSE: return "";
case MA_LOG_LEVEL_INFO: return "INFO";
case MA_LOG_LEVEL_WARNING: return "WARNING";
case MA_LOG_LEVEL_ERROR: return "ERROR";
default: return "ERROR";
}
} }
#endif /* MA_EMSCRIPTEN */
static ma_result ma_mutex_init__posix(ma_mutex* pMutex) /* Posts a log message. */
static void ma_post_log_message(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
{ {
int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); if (pContext == NULL) {
if (result != 0) { if (pDevice != NULL) {
return ma_result_from_errno(result); pContext = pDevice->pContext;
}
} }
return MA_SUCCESS; /* All logs must be output when debug output is enabled. */
} #if defined(MA_DEBUG_OUTPUT)
printf("%s: %s\n", ma_log_level_to_string(logLevel), message);
#endif
static void ma_mutex_uninit__posix(ma_mutex* pMutex) if (pContext == NULL) {
{ return;
pthread_mutex_destroy((pthread_mutex_t*)pMutex); }
}
static void ma_mutex_lock__posix(ma_mutex* pMutex) #if defined(MA_LOG_LEVEL)
{ if (logLevel <= MA_LOG_LEVEL) {
pthread_mutex_lock((pthread_mutex_t*)pMutex); ma_log_proc onLog;
}
static void ma_mutex_unlock__posix(ma_mutex* pMutex) onLog = pContext->logCallback;
{ if (onLog) {
pthread_mutex_unlock((pthread_mutex_t*)pMutex); onLog(pContext, pDevice, logLevel, message);
}
}
#endif
} }
/*
static ma_result ma_event_init__posix(ma_context* pContext, ma_event* pEvent) We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a
logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf().
*/
#if defined(_MSC_VER) && _MSC_VER < 1900
int ma_vscprintf(const char* format, va_list args)
{ {
#if _MSC_VER > 1200
return _vscprintf(format, args);
#else
int result; int result;
char* pTempBuffer = NULL;
size_t tempBufferCap = 1024;
result = ((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pEvent->posix.mutex, NULL); if (format == NULL) {
if (result != 0) { errno = EINVAL;
return ma_result_from_errno(result); return -1;
} }
result = ((ma_pthread_cond_init_proc)pContext->posix.pthread_cond_init)(&pEvent->posix.condition, NULL); for (;;) {
if (result != 0) { char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, NULL); /* TODO: Add support for custom memory allocators? */
((ma_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex); if (pNewTempBuffer == NULL) {
return ma_result_from_errno(result); ma_free(pTempBuffer, NULL);
errno = ENOMEM;
return -1; /* Out of memory. */
} }
pEvent->posix.value = 0; pTempBuffer = pNewTempBuffer;
return MA_SUCCESS;
}
static void ma_event_uninit__posix(ma_event* pEvent) result = _vsnprintf(pTempBuffer, tempBufferCap, format, args);
{ ma_free(pTempBuffer, NULL);
((ma_pthread_cond_destroy_proc)pEvent->pContext->posix.pthread_cond_destroy)(&pEvent->posix.condition);
((ma_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex);
}
static ma_bool32 ma_event_wait__posix(ma_event* pEvent) if (result != -1) {
{ break; /* Got it. */
((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
{
while (pEvent->posix.value == 0) {
((ma_pthread_cond_wait_proc)pEvent->pContext->posix.pthread_cond_wait)(&pEvent->posix.condition, &pEvent->posix.mutex);
} }
pEvent->posix.value = 0; /* Auto-reset. */
/* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */
tempBufferCap *= 2;
} }
((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
return MA_TRUE; return result;
#endif
} }
#endif
static ma_bool32 ma_event_signal__posix(ma_event* pEvent) /* Posts a formatted log message. */
static void ma_post_log_messagev(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* pFormat, va_list args)
{ {
((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex); #if (!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__)
{ {
pEvent->posix.value = 1; char pFormattedMessage[1024];
((ma_pthread_cond_signal_proc)pEvent->pContext->posix.pthread_cond_signal)(&pEvent->posix.condition); vsnprintf(pFormattedMessage, sizeof(pFormattedMessage), pFormat, args);
ma_post_log_message(pContext, pDevice, logLevel, pFormattedMessage);
} }
((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex); #else
{
/*
Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll
need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing
a fixed sized stack allocated buffer.
*/
#if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */
int formattedLen;
va_list args2;
return MA_TRUE; #if _MSC_VER >= 1800
} va_copy(args2, args);
#else
args2 = args;
#endif
formattedLen = ma_vscprintf(pFormat, args2);
va_end(args2);
if (formattedLen > 0) {
char* pFormattedMessage = NULL;
ma_allocation_callbacks* pAllocationCallbacks = NULL;
static ma_result ma_semaphore_init__posix(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore) /* Make sure we have a context so we can allocate memory. */
{ if (pContext == NULL) {
(void)pContext; if (pDevice != NULL) {
pContext = pDevice->pContext;
}
}
#if defined(MA_APPLE) if (pContext != NULL) {
/* Not yet implemented for Apple platforms since sem_init() is deprecated. Need to use a named semaphore via sem_open() instead. */ pAllocationCallbacks = &pContext->allocationCallbacks;
(void)initialValue;
(void)pSemaphore;
return MA_INVALID_OPERATION;
#else
if (sem_init(&pSemaphore->posix.semaphore, 0, (unsigned int)initialValue) == 0) {
return ma_result_from_errno(errno);
} }
#endif
return MA_SUCCESS; pFormattedMessage = (char*)ma_malloc(formattedLen + 1, pAllocationCallbacks);
if (pFormattedMessage != NULL) {
/* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */
#if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */
vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args);
#else
vsprintf(pFormattedMessage, pFormat, args);
#endif
ma_post_log_message(pContext, pDevice, logLevel, pFormattedMessage);
ma_free(pFormattedMessage, pAllocationCallbacks);
}
}
#else
/* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */
(void)pContext;
(void)pDevice;
(void)logLevel;
(void)pFormat;
(void)args;
#endif
}
#endif
} }
static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) MA_API void ma_post_log_messagef(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* pFormat, ...)
{ {
sem_close(&pSemaphore->posix.semaphore); va_list args;
va_start(args, pFormat);
{
ma_post_log_messagev(pContext, pDevice, logLevel, pFormat, args);
}
va_end(args);
} }
static ma_bool32 ma_semaphore_wait__posix(ma_semaphore* pSemaphore) /* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */
static ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
{ {
return sem_wait(&pSemaphore->posix.semaphore) != -1; ma_post_log_message(pContext, pDevice, logLevel, message);
return resultCode;
} }
static ma_bool32 ma_semaphore_release__posix(ma_semaphore* pSemaphore) static ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
{ {
return sem_post(&pSemaphore->posix.semaphore) != -1; return ma_context_post_error(NULL, pDevice, logLevel, message, resultCode);
} }
#endif
static ma_result ma_thread_create(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
{
if (pContext == NULL || pThread == NULL || entryProc == NULL) {
return MA_FALSE;
}
pThread->pContext = pContext; /*******************************************************************************
Timing
*******************************************************************************/
#ifdef MA_WIN32 #ifdef MA_WIN32
return ma_thread_create__win32(pContext, pThread, entryProc, pData); static LARGE_INTEGER g_ma_TimerFrequency = {{0}};
#endif static void ma_timer_init(ma_timer* pTimer)
#ifdef MA_POSIX {
return ma_thread_create__posix(pContext, pThread, entryProc, pData); LARGE_INTEGER counter;
#endif
}
static void ma_thread_wait(ma_thread* pThread) if (g_ma_TimerFrequency.QuadPart == 0) {
{ QueryPerformanceFrequency(&g_ma_TimerFrequency);
if (pThread == NULL) {
return;
} }
#ifdef MA_WIN32 QueryPerformanceCounter(&counter);
ma_thread_wait__win32(pThread); pTimer->counter = counter.QuadPart;
#endif }
#ifdef MA_POSIX
ma_thread_wait__posix(pThread);
#endif
}
#if !defined(MA_EMSCRIPTEN) static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
static void ma_sleep(ma_uint32 milliseconds) {
{ LARGE_INTEGER counter;
#ifdef MA_WIN32 if (!QueryPerformanceCounter(&counter)) {
ma_sleep__win32(milliseconds); return 0;
#endif }
#ifdef MA_POSIX
ma_sleep__posix(milliseconds);
#endif
}
#endif
return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
}
#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
static ma_uint64 g_ma_TimerFrequency = 0;
static void ma_timer_init(ma_timer* pTimer)
{
mach_timebase_info_data_t baseTime;
mach_timebase_info(&baseTime);
g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
MA_API ma_result ma_mutex_init(ma_mutex* pMutex) pTimer->counter = mach_absolute_time();
{
if (pMutex == NULL) {
return MA_INVALID_ARGS;
} }
#ifdef MA_WIN32 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
return ma_mutex_init__win32(pMutex); {
#endif ma_uint64 newTimeCounter = mach_absolute_time();
#ifdef MA_POSIX ma_uint64 oldTimeCounter = pTimer->counter;
return ma_mutex_init__posix(pMutex);
#endif
}
MA_API void ma_mutex_uninit(ma_mutex* pMutex) return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
{ }
if (pMutex == NULL) { #elif defined(MA_EMSCRIPTEN)
return; static MA_INLINE void ma_timer_init(ma_timer* pTimer)
{
pTimer->counterD = emscripten_get_now();
} }
#ifdef MA_WIN32 static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
ma_mutex_uninit__win32(pMutex); {
#endif return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
#ifdef MA_POSIX }
ma_mutex_uninit__posix(pMutex); #else
#endif #if _POSIX_C_SOURCE >= 199309L
} #if defined(CLOCK_MONOTONIC)
#define MA_CLOCK_ID CLOCK_MONOTONIC
#else
#define MA_CLOCK_ID CLOCK_REALTIME
#endif
MA_API void ma_mutex_lock(ma_mutex* pMutex) static void ma_timer_init(ma_timer* pTimer)
{ {
if (pMutex == NULL) { struct timespec newTime;
return; clock_gettime(MA_CLOCK_ID, &newTime);
pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
} }
#ifdef MA_WIN32 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
ma_mutex_lock__win32(pMutex); {
#endif ma_uint64 newTimeCounter;
#ifdef MA_POSIX ma_uint64 oldTimeCounter;
ma_mutex_lock__posix(pMutex);
#endif
}
MA_API void ma_mutex_unlock(ma_mutex* pMutex) struct timespec newTime;
{ clock_gettime(MA_CLOCK_ID, &newTime);
if (pMutex == NULL) {
return;
}
#ifdef MA_WIN32 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
ma_mutex_unlock__win32(pMutex); oldTimeCounter = pTimer->counter;
#endif
#ifdef MA_POSIX
ma_mutex_unlock__posix(pMutex);
#endif
}
return (newTimeCounter - oldTimeCounter) / 1000000000.0;
}
#else
static void ma_timer_init(ma_timer* pTimer)
{
struct timeval newTime;
gettimeofday(&newTime, NULL);
MA_API ma_result ma_event_init(ma_context* pContext, ma_event* pEvent) pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
{
if (pContext == NULL || pEvent == NULL) {
return MA_FALSE;
} }
pEvent->pContext = pContext; static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
ma_uint64 newTimeCounter;
ma_uint64 oldTimeCounter;
#ifdef MA_WIN32 struct timeval newTime;
return ma_event_init__win32(pContext, pEvent); gettimeofday(&newTime, NULL);
#endif
#ifdef MA_POSIX
return ma_event_init__posix(pContext, pEvent);
#endif
}
MA_API void ma_event_uninit(ma_event* pEvent) newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
{ oldTimeCounter = pTimer->counter;
if (pEvent == NULL || pEvent->pContext == NULL) {
return;
}
#ifdef MA_WIN32 return (newTimeCounter - oldTimeCounter) / 1000000.0;
ma_event_uninit__win32(pEvent); }
#endif #endif
#ifdef MA_POSIX
ma_event_uninit__posix(pEvent);
#endif #endif
}
MA_API ma_bool32 ma_event_wait(ma_event* pEvent)
{
if (pEvent == NULL || pEvent->pContext == NULL) {
return MA_FALSE;
}
#ifdef MA_WIN32 /*******************************************************************************
return ma_event_wait__win32(pEvent);
#endif
#ifdef MA_POSIX
return ma_event_wait__posix(pEvent);
#endif
}
MA_API ma_bool32 ma_event_signal(ma_event* pEvent) Dynamic Linking
*******************************************************************************/
MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
{ {
if (pEvent == NULL || pEvent->pContext == NULL) { ma_handle handle;
return MA_FALSE;
#if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
if (pContext != NULL) {
char message[256];
ma_strappend(message, sizeof(message), "Loading library: ", filename);
ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
} }
#endif
#ifdef MA_WIN32 #ifdef _WIN32
return ma_event_signal__win32(pEvent); #ifdef MA_WIN32_DESKTOP
handle = (ma_handle)LoadLibraryA(filename);
#else
/* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
WCHAR filenameW[4096];
if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
handle = NULL;
} else {
handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
}
#endif #endif
#ifdef MA_POSIX #else
return ma_event_signal__posix(pEvent); handle = (ma_handle)dlopen(filename, RTLD_NOW);
#endif #endif
}
/*
MA_API ma_result ma_semaphore_init(ma_context* pContext, int initialValue, ma_semaphore* pSemaphore) I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
{ backend is a deliberate design choice. Instead I'm logging it as an informational message.
if (pContext == NULL || pSemaphore == NULL) { */
return MA_INVALID_ARGS; #if MA_LOG_LEVEL >= MA_LOG_LEVEL_INFO
if (handle == NULL) {
char message[256];
ma_strappend(message, sizeof(message), "Failed to load library: ", filename);
ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, message);
} }
#ifdef MA_WIN32
return ma_semaphore_init__win32(pContext, initialValue, pSemaphore);
#endif
#ifdef MA_POSIX
return ma_semaphore_init__posix(pContext, initialValue, pSemaphore);
#endif #endif
(void)pContext; /* It's possible for pContext to be unused. */
return handle;
} }
MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) MA_API void ma_dlclose(ma_context* pContext, ma_handle handle)
{ {
if (pSemaphore == NULL) { #ifdef _WIN32
return; FreeLibrary((HMODULE)handle);
} #else
dlclose((void*)handle);
#ifdef MA_WIN32
ma_semaphore_uninit__win32(pSemaphore);
#endif
#ifdef MA_POSIX
ma_semaphore_uninit__posix(pSemaphore);
#endif #endif
(void)pContext;
} }
MA_API ma_bool32 ma_semaphore_wait(ma_semaphore* pSemaphore) MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
{ {
if (pSemaphore == NULL) { ma_proc proc;
return MA_FALSE;
#if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
if (pContext != NULL) {
char message[256];
ma_strappend(message, sizeof(message), "Loading symbol: ", symbol);
ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
} }
#endif
#ifdef MA_WIN32 #ifdef _WIN32
return ma_semaphore_wait__win32(pSemaphore); proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
#else
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
proc = (ma_proc)dlsym((void*)handle, symbol);
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic pop
#endif #endif
#ifdef MA_POSIX
return ma_semaphore_wait__posix(pSemaphore);
#endif #endif
}
MA_API ma_bool32 ma_semaphore_release(ma_semaphore* pSemaphore) #if MA_LOG_LEVEL >= MA_LOG_LEVEL_WARNING
{ if (handle == NULL) {
if (pSemaphore == NULL) { char message[256];
return MA_FALSE; ma_strappend(message, sizeof(message), "Failed to load symbol: ", symbol);
ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_WARNING, message);
} }
#ifdef MA_WIN32
return ma_semaphore_release__win32(pSemaphore);
#endif
#ifdef MA_POSIX
return ma_semaphore_release__posix(pSemaphore);
#endif #endif
(void)pContext; /* It's possible for pContext to be unused. */
return proc;
} }
...@@ -10041,6 +10045,7 @@ static ma_result ma_context_init__null(const ma_context_config* pConfig, ma_cont ...@@ -10041,6 +10045,7 @@ static ma_result ma_context_init__null(const ma_context_config* pConfig, ma_cont
#endif #endif
/******************************************************************************* /*******************************************************************************
WIN32 COMMON WIN32 COMMON
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment