Commit 42abbbea authored by David Reid's avatar David Reid

Version 0.11.2

parent 354d0c91
/* /*
Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.11.1 - 2021-12-27 miniaudio - v0.11.2 - 2021-12-31
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
...@@ -1199,6 +1199,10 @@ MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* ...@@ -1199,6 +1199,10 @@ MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char*
MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)
{ {
if (src == NULL) {
return NULL;
}
size_t sz = strlen(src)+1; size_t sz = strlen(src)+1;
char* dst = (char*)ma_malloc(sz, pAllocationCallbacks); char* dst = (char*)ma_malloc(sz, pAllocationCallbacks);
if (dst == NULL) { if (dst == NULL) {
...@@ -5959,6 +5963,57 @@ static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned i ...@@ -5959,6 +5963,57 @@ 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(&notification);
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(&notification);
}
/* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */
if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) {
notification.pDevice->onStop(notification.pDevice);
}
}
void ma_device__on_notification_started(ma_device* pDevice)
{
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started));
}
void ma_device__on_notification_stopped(ma_device* pDevice)
{
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped));
}
void ma_device__on_notification_rerouted(ma_device* pDevice)
{
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted));
}
void ma_device__on_notification_interruption_began(ma_device* pDevice)
{
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began));
}
void ma_device__on_notification_interruption_ended(ma_device* pDevice)
{
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended));
}
static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{ {
float masterVolumeFactor; float masterVolumeFactor;
...@@ -9866,6 +9921,8 @@ static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type de ...@@ -9866,6 +9921,8 @@ static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type de
ma_device__post_init_setup(pDevice, deviceType); ma_device__post_init_setup(pDevice, deviceType);
ma_device__on_notification_rerouted(pDevice);
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -10890,9 +10947,9 @@ struct ma_IDirectSoundCapture ...@@ -10890,9 +10947,9 @@ struct ma_IDirectSoundCapture
{ {
ma_IDirectSoundCaptureVtbl* lpVtbl; ma_IDirectSoundCaptureVtbl* lpVtbl;
}; };
static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
static MA_INLINE ULONG ma_IDirectSoundCapture_Release(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); }
static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
...@@ -12619,10 +12676,10 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, ...@@ -12619,10 +12676,10 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext,
if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
BYTE nameFromReg[512]; BYTE nameFromReg[512];
DWORD nameFromRegSize = sizeof(nameFromReg); DWORD nameFromRegSize = sizeof(nameFromReg);
result = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize); LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
if (result == ERROR_SUCCESS) { if (resultWin32 == ERROR_SUCCESS) {
/* We have the value from the registry, so now we need to construct the name string. */ /* We have the value from the registry, so now we need to construct the name string. */
char name[1024]; char name[1024];
if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
...@@ -16171,6 +16228,7 @@ typedef const char* (* ma_pa_stream_get_device_name_proc) ( ...@@ -16171,6 +16228,7 @@ typedef const char* (* ma_pa_stream_get_device_name_proc) (
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_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_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_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 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_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 ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
...@@ -16365,7 +16423,7 @@ static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position ...@@ -16365,7 +16423,7 @@ static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position
} }
#endif #endif
static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operation* pOP) static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
{ {
int resultPA; int resultPA;
ma_pa_operation_state_t state; ma_pa_operation_state_t state;
...@@ -16379,7 +16437,7 @@ static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operat ...@@ -16379,7 +16437,7 @@ static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operat
break; /* Done. */ break; /* Done. */
} }
resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL); resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
if (resultPA < 0) { if (resultPA < 0) {
return ma_result_from_pulse(resultPA); return ma_result_from_pulse(resultPA);
} }
...@@ -16388,7 +16446,7 @@ static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operat ...@@ -16388,7 +16446,7 @@ static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operat
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_pa_operation* pOP) static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
{ {
ma_result result; ma_result result;
...@@ -16396,19 +16454,19 @@ static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma ...@@ -16396,19 +16454,19 @@ static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
result = ma_wait_for_operation__pulse(pContext, pOP); result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
return result; return result;
} }
static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pContext) static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext)
{ {
int resultPA; int resultPA;
ma_pa_context_state_t state; ma_pa_context_state_t state;
for (;;) { for (;;) {
state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pContext->pulse.pPulseContext); state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext);
if (state == MA_PA_CONTEXT_READY) { if (state == MA_PA_CONTEXT_READY) {
break; /* Done. */ break; /* Done. */
} }
...@@ -16418,7 +16476,7 @@ static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pC ...@@ -16418,7 +16476,7 @@ static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pC
return MA_ERROR; return MA_ERROR;
} }
resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL); resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
if (resultPA < 0) { if (resultPA < 0) {
return ma_result_from_pulse(resultPA); return ma_result_from_pulse(resultPA);
} }
...@@ -16428,13 +16486,13 @@ static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pC ...@@ -16428,13 +16486,13 @@ static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pC
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_pa_stream* pStream) static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream)
{ {
int resultPA; int resultPA;
ma_pa_stream_state_t state; ma_pa_stream_state_t state;
for (;;) { for (;;) {
state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)(pStream); state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream);
if (state == MA_PA_STREAM_READY) { if (state == MA_PA_STREAM_READY) {
break; /* Done. */ break; /* Done. */
} }
...@@ -16444,7 +16502,7 @@ static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pCo ...@@ -16444,7 +16502,7 @@ static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pCo
return MA_ERROR; return MA_ERROR;
} }
resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL); resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
if (resultPA < 0) { if (resultPA < 0) {
return ma_result_from_pulse(resultPA); return ma_result_from_pulse(resultPA);
} }
...@@ -16454,6 +16512,52 @@ static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pCo ...@@ -16454,6 +16512,52 @@ static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pCo
} }
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)
{
ma_result result;
ma_ptr pMainLoop;
ma_ptr 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)();
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);
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));
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));
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));
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);
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));
return result;
}
*ppMainLoop = pMainLoop;
*ppPulseContext = pPulseContext;
return MA_SUCCESS;
}
static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
{ {
ma_pa_sink_info* pInfoOut; ma_pa_sink_info* pInfoOut;
...@@ -16486,6 +16590,7 @@ static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const m ...@@ -16486,6 +16590,7 @@ static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const m
(void)pPulseContext; /* Unused. */ (void)pPulseContext; /* Unused. */
} }
#if 0
static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
{ {
ma_device* pDevice; ma_device* pDevice;
...@@ -16517,7 +16622,7 @@ static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const m ...@@ -16517,7 +16622,7 @@ static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const m
(void)pPulseContext; /* Unused. */ (void)pPulseContext; /* Unused. */
} }
#endif
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__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo)
{ {
...@@ -16528,7 +16633,7 @@ static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const cha ...@@ -16528,7 +16633,7 @@ static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const cha
return MA_ERROR; return MA_ERROR;
} }
return ma_wait_for_operation_and_unref__pulse(pContext, pOP); return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.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__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo)
...@@ -16540,7 +16645,7 @@ static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const c ...@@ -16540,7 +16645,7 @@ static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const c
return MA_ERROR; return MA_ERROR;
} }
return ma_wait_for_operation_and_unref__pulse(pContext, pOP);; return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.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__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex)
...@@ -16684,7 +16789,7 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en ...@@ -16684,7 +16789,7 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en
goto done; goto done;
} }
result = ma_wait_for_operation__pulse(pContext, pOP); result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -16701,7 +16806,7 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en ...@@ -16701,7 +16806,7 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en
goto done; goto done;
} }
result = ma_wait_for_operation__pulse(pContext, pOP); result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -16822,7 +16927,7 @@ static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_devi ...@@ -16822,7 +16927,7 @@ static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_devi
} }
if (pOP != NULL) { if (pOP != NULL) {
ma_wait_for_operation_and_unref__pulse(pContext, pOP); ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
} else { } else {
result = MA_ERROR; result = MA_ERROR;
goto done; goto done;
...@@ -16860,6 +16965,10 @@ static ma_result ma_device_uninit__pulse(ma_device* pDevice) ...@@ -16860,6 +16965,10 @@ static ma_result ma_device_uninit__pulse(ma_device* pDevice)
ma_duplex_rb_uninit(&pDevice->duplexRB); 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; return MA_SUCCESS;
} }
...@@ -16875,7 +16984,7 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra ...@@ -16875,7 +16984,7 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra
return attr; return attr;
} }
static ma_pa_stream* ma_context__pa_stream_new__pulse(ma_context* pContext, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) 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 int g_StreamCounter = 0; static int g_StreamCounter = 0;
char actualStreamName[256]; char actualStreamName[256];
...@@ -16888,7 +16997,7 @@ static ma_pa_stream* ma_context__pa_stream_new__pulse(ma_context* pContext, cons ...@@ -16888,7 +16997,7 @@ static ma_pa_stream* ma_context__pa_stream_new__pulse(ma_context* pContext, cons
} }
g_StreamCounter += 1; g_StreamCounter += 1;
return ((ma_pa_stream_new_proc)pContext->pulse.pa_stream_new)((ma_pa_context*)pContext->pulse.pPulseContext, actualStreamName, ss, cmap); return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
} }
...@@ -17070,15 +17179,23 @@ static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData ...@@ -17070,15 +17179,23 @@ static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData
if (suspended == 1) { if (suspended == 1) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); 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);
if (pDevice->onStop) {
pDevice->onStop(pDevice);
}
} else { } else {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); 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);
} }
} }
static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData)
{
ma_device* pDevice = (ma_device*)pUserData;
(void)pStream;
(void)pUserData;
ma_device__on_notification_rerouted(pDevice);
}
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__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
{ {
/* /*
...@@ -17118,6 +17235,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17118,6 +17235,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
MA_ASSERT(pDevice != NULL); MA_ASSERT(pDevice != NULL);
MA_ZERO_OBJECT(&pDevice->pulse); MA_ZERO_OBJECT(&pDevice->pulse);
printf("TESTING\n");
if (pConfig->deviceType == ma_device_type_loopback) { if (pConfig->deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED; return MA_DEVICE_TYPE_NOT_SUPPORTED;
} }
...@@ -17148,6 +17267,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17148,6 +17267,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
sampleRate = pDescriptorCapture->sampleRate; 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);
if (result != MA_SUCCESS) {
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) { if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -17181,7 +17308,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17181,7 +17308,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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_context__pa_stream_new__pulse(pDevice->pContext, pConfig->pulse.pStreamNameCapture, &ss, &cmap); pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
if (pDevice->pulse.pStreamCapture == NULL) { if (pDevice->pulse.pStreamCapture == NULL) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream."); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.");
result = MA_ERROR; result = MA_ERROR;
...@@ -17195,6 +17322,9 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17195,6 +17322,9 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
/* State callback for checking when the device has been corked. */ /* 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); ((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);
/* 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);
/* Connect after we've got all of our internal state set up. */ /* Connect after we've got all of our internal state set up. */
streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
...@@ -17209,7 +17339,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17209,7 +17339,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
goto on_error1; goto on_error1;
} }
result = ma_context_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, (ma_pa_stream*)pDevice->pulse.pStreamCapture); result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
goto on_error2; goto on_error2;
} }
...@@ -17246,13 +17376,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17246,13 +17376,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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);
#if 0
/* Name. */ /* Name. */
devCapture = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture); devCapture = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
if (devCapture != NULL) { if (devCapture != NULL) {
ma_pa_operation* pOP = ((ma_pa_context_get_source_info_by_name_proc)pDevice->pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pContext->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice); ma_pa_operation* pOP = ((ma_pa_context_get_source_info_by_name_proc)pDevice->pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice);
ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP); ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
} }
#endif
} }
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
...@@ -17289,7 +17420,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17289,7 +17420,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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_context__pa_stream_new__pulse(pDevice->pContext, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
if (pDevice->pulse.pStreamPlayback == NULL) { if (pDevice->pulse.pStreamPlayback == NULL) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream."); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.");
result = MA_ERROR; result = MA_ERROR;
...@@ -17306,6 +17437,9 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17306,6 +17437,9 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
/* State callback for checking when the device has been corked. */ /* 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); ((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);
/* 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);
/* Connect after we've got all of our internal state set up. */ /* Connect after we've got all of our internal state set up. */
streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
...@@ -17320,7 +17454,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17320,7 +17454,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
goto on_error3; goto on_error3;
} }
result = ma_context_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
goto on_error3; goto on_error3;
} }
...@@ -17357,13 +17491,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ...@@ -17357,13 +17491,14 @@ 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; 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_DEBUG, "[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); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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);
#if 0
/* Name. */ /* Name. */
devPlayback = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); devPlayback = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
if (devPlayback != NULL) { if (devPlayback != NULL) {
ma_pa_operation* pOP = ((ma_pa_context_get_sink_info_by_name_proc)pDevice->pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pContext->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice); ma_pa_operation* pOP = ((ma_pa_context_get_sink_info_by_name_proc)pDevice->pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice);
ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP); ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
} }
#endif
} }
...@@ -17443,7 +17578,7 @@ static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_typ ...@@ -17443,7 +17578,7 @@ static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_typ
return MA_ERROR; return MA_ERROR;
} }
result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP); result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
if (result != MA_SUCCESS) { 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."); 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; return result;
...@@ -17503,7 +17638,7 @@ static ma_result ma_device_stop__pulse(ma_device* pDevice) ...@@ -17503,7 +17638,7 @@ static ma_result ma_device_stop__pulse(ma_device* pDevice)
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
/* The stream needs to be drained if it's a playback device. */ /* The stream needs to be drained if it's a playback device. */
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_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, pOP); ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -17527,7 +17662,7 @@ static ma_result ma_device_data_loop__pulse(ma_device* pDevice) ...@@ -17527,7 +17662,7 @@ static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
the callbacks deal with it. the callbacks deal with it.
*/ */
while (ma_device_get_state(pDevice) == ma_device_state_started) { 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->pContext->pulse.pMainLoop, 1, NULL); resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
if (resultPA < 0) { if (resultPA < 0) {
break; break;
} }
...@@ -17541,7 +17676,7 @@ static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) ...@@ -17541,7 +17676,7 @@ static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
{ {
MA_ASSERT(pDevice != NULL); MA_ASSERT(pDevice != NULL);
((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pContext->pulse.pMainLoop); ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -17555,6 +17690,9 @@ static ma_result ma_context_uninit__pulse(ma_context* pContext) ...@@ -17555,6 +17690,9 @@ static ma_result ma_context_uninit__pulse(ma_context* pContext)
((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((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_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 #ifndef MA_NO_RUNTIME_LINKING
ma_dlclose(pContext, pContext->pulse.pulseSO); ma_dlclose(pContext, pContext->pulse.pulseSO);
#endif #endif
...@@ -17631,6 +17769,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c ...@@ -17631,6 +17769,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c
pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback"); pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback"); pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback");
pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback");
pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended"); pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended");
pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush"); pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain"); pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
...@@ -17693,6 +17832,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c ...@@ -17693,6 +17832,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c
ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; 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_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_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_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_flush_proc _pa_stream_flush = pa_stream_flush;
ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
...@@ -17754,6 +17894,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c ...@@ -17754,6 +17894,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c
pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; 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_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_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_is_suspended = (ma_proc)_pa_stream_is_suspended;
pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
...@@ -17768,48 +17909,28 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c ...@@ -17768,48 +17909,28 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c
pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
#endif #endif
/* 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. */ /* 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.pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
if (pContext->pulse.pMainLoop == NULL) { if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) {
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); return MA_OUT_OF_MEMORY;
#ifndef MA_NO_RUNTIME_LINKING
ma_dlclose(pContext, pContext->pulse.pulseSO);
#endif
return MA_FAILED_TO_INIT_BACKEND;
}
pContext->pulse.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*)pContext->pulse.pMainLoop), pConfig->pulse.pApplicationName);
if (pContext->pulse.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*)(pContext->pulse.pMainLoop));
#ifndef MA_NO_RUNTIME_LINKING
ma_dlclose(pContext, pContext->pulse.pulseSO);
#endif
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. */ pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pContext->pulse.pPulseContext, pConfig->pulse.pServerName, (pConfig->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) {
if (result != MA_SUCCESS) { ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); return MA_OUT_OF_MEMORY;
((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop));
#ifndef MA_NO_RUNTIME_LINKING
ma_dlclose(pContext, pContext->pulse.pulseSO);
#endif
return result;
} }
/* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ 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);
result = ma_context_wait_for_pa_context_to_connect__pulse(pContext);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
((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 #ifndef MA_NO_RUNTIME_LINKING
ma_dlclose(pContext, pContext->pulse.pulseSO); ma_dlclose(pContext, pContext->pulse.pulseSO);
#endif #endif
return result; return result;
} }
/* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */
pCallbacks->onContextInit = ma_context_init__pulse; pCallbacks->onContextInit = ma_context_init__pulse;
pCallbacks->onContextUninit = ma_context_uninit__pulse; pCallbacks->onContextUninit = ma_context_uninit__pulse;
...@@ -18348,17 +18469,13 @@ static ma_result ma_device_start__jack(ma_device* pDevice) ...@@ -18348,17 +18469,13 @@ static ma_result ma_device_start__jack(ma_device* pDevice)
static ma_result ma_device_stop__jack(ma_device* pDevice) static ma_result ma_device_stop__jack(ma_device* pDevice)
{ {
ma_context* pContext = pDevice->pContext; ma_context* pContext = pDevice->pContext;
ma_stop_proc onStop;
if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.");
return MA_ERROR; return MA_ERROR;
} }
onStop = pDevice->onStop; ma_device__on_notification_stopped(pDevice);
if (onStop) {
onStop(pDevice);
}
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -20083,7 +20200,7 @@ static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice ...@@ -20083,7 +20200,7 @@ static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice
AudioBufferList* pNewAudioBufferList; AudioBufferList* pNewAudioBufferList;
pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
if (pNewAudioBufferList != NULL) { if (pNewAudioBufferList == NULL) {
return MA_OUT_OF_MEMORY; return MA_OUT_OF_MEMORY;
} }
...@@ -20213,6 +20330,9 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla ...@@ -20213,6 +20330,9 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture."); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.");
return noErr; return noErr;
} }
pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
MA_ASSERT(pRenderedBufferList);
/* /*
When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes
...@@ -20322,11 +20442,7 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio ...@@ -20322,11 +20442,7 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio
Audio APIs in the callback when the device has been stopped or uninitialized. 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) { 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_stop_proc onStop = pDevice->onStop; ma_device__on_notification_stopped(pDevice);
if (onStop) {
onStop(pDevice);
}
ma_event_signal(&pDevice->coreaudio.stopEvent); ma_event_signal(&pDevice->coreaudio.stopEvent);
} else { } else {
UInt32 isRunning; UInt32 isRunning;
...@@ -20337,8 +20453,6 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio ...@@ -20337,8 +20453,6 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio
} }
if (!isRunning) { if (!isRunning) {
ma_stop_proc onStop;
/* /*
The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider:
...@@ -20352,7 +20466,7 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio ...@@ -20352,7 +20466,7 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio
/* /*
It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device 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 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 onStop event and thinking that the device has stopped when it 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!). hasn't!).
*/ */
if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
...@@ -20365,16 +20479,13 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio ...@@ -20365,16 +20479,13 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio
will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most
likely be successful in switching to the new device. likely be successful in switching to the new device.
TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted. TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted.
*/ */
return; return;
} }
/* Getting here means we need to stop the device. */ /* Getting here means we need to stop the device. */
onStop = pDevice->onStop; ma_device__on_notification_stopped(pDevice);
if (onStop) {
onStop(pDevice);
}
} }
} }
...@@ -20449,6 +20560,8 @@ static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UIn ...@@ -20449,6 +20560,8 @@ static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UIn
} }
} }
} }
ma_device__on_notification_rerouted(pDevice);
} }
} }
} }
...@@ -20594,31 +20707,54 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) ...@@ -20594,31 +20707,54 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
#endif #endif
#if defined(MA_APPLE_MOBILE) #if defined(MA_APPLE_MOBILE)
@interface ma_router_change_handler:NSObject { @interface ma_ios_notification_handler:NSObject {
ma_device* m_pDevice; ma_device* m_pDevice;
} }
@end @end
@implementation ma_router_change_handler @implementation ma_ios_notification_handler
-(id)init:(ma_device*)pDevice -(id)init:(ma_device*)pDevice
{ {
self = [super init]; self = [super init];
m_pDevice = pDevice; m_pDevice = pDevice;
/* For route changes. */
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
/* For interruptions. */
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
return self; return self;
} }
-(void)dealloc -(void)dealloc
{ {
[self remove_handler]; [self remove_handler];
[super dealloc];
} }
-(void)remove_handler -(void)remove_handler
{ {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
}
-(void)handle_interruption:(NSNotification*)pNotification
{
NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];
switch (type)
{
case AVAudioSessionInterruptionTypeBegan:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n");
ma_device__on_notification_interruption_began(m_pDevice);
} break;
case AVAudioSessionInterruptionTypeEnded:
{
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n");
ma_device__on_notification_interruption_ended(m_pDevice);
} break;
}
} }
-(void)handle_route_change:(NSNotification*)pNotification -(void)handle_route_change:(NSNotification*)pNotification
...@@ -20666,30 +20802,9 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) ...@@ -20666,30 +20802,9 @@ 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); 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);
/* Temporarily disabling this section of code because it appears to be causing errors. */ /* Let the application know about the route change. */
#if 0 ma_device__on_notification_rerouted(m_pDevice);
ma_uint32 previousState = ma_device_get_state(m_pDevice);
if (previousState == ma_device_state_started) {
ma_device_stop(m_pDevice);
}
if (m_pDevice->type == ma_device_type_capture || m_pDevice->type == ma_device_type_duplex) {
m_pDevice->capture.internalChannels = (ma_uint32)pSession.inputNumberOfChannels;
m_pDevice->capture.internalSampleRate = (ma_uint32)pSession.sampleRate;
ma_device__post_init_setup(m_pDevice, ma_device_type_capture);
}
if (m_pDevice->type == ma_device_type_playback || m_pDevice->type == ma_device_type_duplex) {
m_pDevice->playback.internalChannels = (ma_uint32)pSession.outputNumberOfChannels;
m_pDevice->playback.internalSampleRate = (ma_uint32)pSession.sampleRate;
ma_device__post_init_setup(m_pDevice, ma_device_type_playback);
}
if (previousState == ma_device_state_started) {
ma_device_start(m_pDevice);
}
#endif
} }
@end @end
#endif #endif
...@@ -20707,9 +20822,9 @@ static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) ...@@ -20707,9 +20822,9 @@ static ma_result ma_device_uninit__coreaudio(ma_device* pDevice)
ma_device__untrack__coreaudio(pDevice); ma_device__untrack__coreaudio(pDevice);
#endif #endif
#if defined(MA_APPLE_MOBILE) #if defined(MA_APPLE_MOBILE)
if (pDevice->coreaudio.pRouteChangeHandler != NULL) { if (pDevice->coreaudio.pNotificationHandler != NULL) {
ma_router_change_handler* pRouteChangeHandler = (MA_BRIDGE_TRANSFER ma_router_change_handler*)pDevice->coreaudio.pRouteChangeHandler; ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler;
[pRouteChangeHandler remove_handler]; [pNotificationHandler remove_handler];
} }
#endif #endif
...@@ -20769,8 +20884,6 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ...@@ -20769,8 +20884,6 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
AURenderCallbackStruct callbackInfo; AURenderCallbackStruct callbackInfo;
#if defined(MA_APPLE_DESKTOP) #if defined(MA_APPLE_DESKTOP)
AudioObjectID deviceObjectID; AudioObjectID deviceObjectID;
#else
UInt32 actualPeriodSizeInFramesSize = sizeof(actualPeriodSizeInFrames);
#endif #endif
/* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
...@@ -21055,13 +21168,16 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ...@@ -21055,13 +21168,16 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
} }
#else #else
/* /*
I don't know how to configure buffer sizes on iOS so for now we're not allowing it to be configured. Instead we're On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point
just going to set it to the value of kAudioUnitProperty_MaximumFramesPerSlice. number. I don't trust any potential truncation errors due to converting from float to integer
so I'm going to explicitly set the actual period size to the next power of 2.
*/ */
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, &actualPeriodSizeInFramesSize); @autoreleasepool {
if (status != noErr) { AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); MA_ASSERT(pAudioSession != NULL);
return ma_result_from_OSStatus(status);
[pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil];
actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate));
} }
#endif #endif
...@@ -21382,7 +21498,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c ...@@ -21382,7 +21498,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c
differently on non-Desktop Apple platforms. differently on non-Desktop Apple platforms.
*/ */
#if defined(MA_APPLE_MOBILE) #if defined(MA_APPLE_MOBILE)
pDevice->coreaudio.pRouteChangeHandler = (MA_BRIDGE_RETAINED void*)[[ma_router_change_handler alloc] init:pDevice]; pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice];
#endif #endif
return MA_SUCCESS; return MA_SUCCESS;
...@@ -24701,8 +24817,6 @@ static ma_result ma_device_start__aaudio(ma_device* pDevice) ...@@ -24701,8 +24817,6 @@ static ma_result ma_device_start__aaudio(ma_device* pDevice)
static ma_result ma_device_stop__aaudio(ma_device* pDevice) static ma_result ma_device_stop__aaudio(ma_device* pDevice)
{ {
ma_stop_proc onStop;
MA_ASSERT(pDevice != NULL); MA_ASSERT(pDevice != NULL);
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
...@@ -24719,10 +24833,7 @@ static ma_result ma_device_stop__aaudio(ma_device* pDevice) ...@@ -24719,10 +24833,7 @@ static ma_result ma_device_stop__aaudio(ma_device* pDevice)
} }
} }
onStop = pDevice->onStop; ma_device__on_notification_stopped(pDevice);
if (onStop) {
onStop(pDevice);
}
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -25878,7 +25989,6 @@ static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type devi ...@@ -25878,7 +25989,6 @@ static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type devi
static ma_result ma_device_stop__opensl(ma_device* pDevice) static ma_result ma_device_stop__opensl(ma_device* pDevice)
{ {
SLresult resultSL; SLresult resultSL;
ma_stop_proc onStop;
MA_ASSERT(pDevice != NULL); MA_ASSERT(pDevice != NULL);
...@@ -25912,10 +26022,7 @@ static ma_result ma_device_stop__opensl(ma_device* pDevice) ...@@ -25912,10 +26022,7 @@ static ma_result ma_device_stop__opensl(ma_device* pDevice)
} }
/* 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. */ /* 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. */
onStop = pDevice->onStop; ma_device__on_notification_stopped(pDevice);
if (onStop) {
onStop(pDevice);
}
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -26365,7 +26472,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d ...@@ -26365,7 +26472,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d
how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like
this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know! this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know!
*/ */
device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels); device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, (isCapture) ? channels : 0, (isCapture) ? 0 : channels);
if (isCapture) { if (isCapture) {
device.scriptNode.onaudioprocess = function(e) { device.scriptNode.onaudioprocess = function(e) {
...@@ -26373,7 +26480,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d ...@@ -26373,7 +26480,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d
return; /* This means the device has been uninitialized. */ return; /* This means the device has been uninitialized. */
} }
if(device.intermediaryBufferView.length == 0) { if (device.intermediaryBufferView.length == 0) {
/* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */
device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
} }
...@@ -26471,8 +26578,10 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d ...@@ -26471,8 +26578,10 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d
} }
} else { } else {
for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
var outputBuffer = e.outputBuffer.getChannelData(iChannel);
var intermediaryBuffer = device.intermediaryBufferView;
for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel]; outputBuffer[totalFramesProcessed + iFrame] = intermediaryBuffer[iFrame*channels + iChannel];
} }
} }
} }
...@@ -26594,10 +26703,7 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) ...@@ -26594,10 +26703,7 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice)
}, pDevice->webaudio.indexPlayback); }, pDevice->webaudio.indexPlayback);
} }
ma_stop_proc onStop = pDevice->onStop; ma_device__on_notification_stopped(pDevice);
if (onStop) {
onStop(pDevice);
}
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -26926,7 +27032,7 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ...@@ -26926,7 +27032,7 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
ma_result startResult; ma_result startResult;
ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the onStop callback. */ ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */
/* We wait on an event to know when something has requested that the device be started and the main loop entered. */ /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
ma_event_wait(&pDevice->wakeupEvent); ma_event_wait(&pDevice->wakeupEvent);
...@@ -26962,6 +27068,8 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ...@@ -26962,6 +27068,8 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
ma_device__set_state(pDevice, ma_device_state_started); ma_device__set_state(pDevice, ma_device_state_started);
ma_event_signal(&pDevice->startEvent); ma_event_signal(&pDevice->startEvent);
ma_device__on_notification_started(pDevice);
if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
} else { } else {
...@@ -26977,12 +27085,12 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ...@@ -26977,12 +27085,12 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
} }
/* /*
After the device has stopped, make sure an event is posted. Don't post an onStop event if After the device has stopped, make sure an event is posted. Don't post a stopped event if
stopping failed. This can happen on some backends when the underlying stream has been stopping failed. This can happen on some backends when the underlying stream has been
stopped due to the device being physically unplugged or disabled via an OS setting. stopped due to the device being physically unplugged or disabled via an OS setting.
*/ */
if (pDevice->onStop && stopResult != MA_SUCCESS) { if (stopResult == MA_SUCCESS) {
pDevice->onStop(pDevice); ma_device__on_notification_stopped(pDevice);
} }
/* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */
...@@ -27623,13 +27731,11 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ...@@ -27623,13 +27731,11 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
/* Check that we have our callbacks defined. */ /* Check that we have our callbacks defined. */
if (pContext->callbacks.onDeviceInit == NULL) { if (pContext->callbacks.onDeviceInit == NULL) {
return MA_INVALID_OPERATION; return MA_INVALID_OPERATION;
} }
/* Basic config validation. */ /* Basic config validation. */
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
if (pConfig->capture.channels > MA_MAX_CHANNELS) { if (pConfig->capture.channels > MA_MAX_CHANNELS) {
...@@ -27654,9 +27760,10 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ...@@ -27654,9 +27760,10 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
pDevice->pContext = pContext; pDevice->pContext = pContext;
/* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
pDevice->pUserData = pConfig->pUserData; pDevice->pUserData = pConfig->pUserData;
pDevice->onData = pConfig->dataCallback; pDevice->onData = pConfig->dataCallback;
pDevice->onStop = pConfig->stopCallback; pDevice->onNotification = pConfig->notificationCallback;
pDevice->onStop = pConfig->stopCallback;
if (pConfig->playback.pDeviceID != NULL) { if (pConfig->playback.pDeviceID != NULL) {
MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
...@@ -28150,6 +28257,7 @@ MA_API ma_result ma_device_start(ma_device* pDevice) ...@@ -28150,6 +28257,7 @@ MA_API ma_result ma_device_start(ma_device* pDevice)
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
ma_device__set_state(pDevice, ma_device_state_started); ma_device__set_state(pDevice, ma_device_state_started);
ma_device__on_notification_started(pDevice);
} }
} else { } else {
/* /*
...@@ -52985,7 +53093,7 @@ MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager ...@@ -52985,7 +53093,7 @@ MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager
MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init() MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void)
{ {
ma_resource_manager_data_source_config config; ma_resource_manager_data_source_config config;
/* /*
Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.11.1 - 2021-12-27 miniaudio - v0.11.2 - 2021-12-31
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
...@@ -20,7 +20,7 @@ extern "C" { ...@@ -20,7 +20,7 @@ extern "C" {
#define MA_VERSION_MAJOR 0 #define MA_VERSION_MAJOR 0
#define MA_VERSION_MINOR 11 #define MA_VERSION_MINOR 11
#define MA_VERSION_REVISION 1 #define MA_VERSION_REVISION 2
#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
#if defined(_MSC_VER) && !defined(__clang__) #if defined(_MSC_VER) && !defined(__clang__)
...@@ -47,29 +47,42 @@ extern "C" { ...@@ -47,29 +47,42 @@ extern "C" {
#include <stddef.h> /* For size_t. */ #include <stddef.h> /* For size_t. */
/* Sized types. */ /* Sized types. */
typedef signed char ma_int8; #if defined(MA_USE_STDINT)
typedef unsigned char ma_uint8; #include <stdint.h>
typedef signed short ma_int16; typedef int8_t ma_int8;
typedef unsigned short ma_uint16; typedef uint8_t ma_uint8;
typedef signed int ma_int32; typedef int16_t ma_int16;
typedef unsigned int ma_uint32; typedef uint16_t ma_uint16;
#if defined(_MSC_VER) && !defined(__clang__) typedef int32_t ma_int32;
typedef signed __int64 ma_int64; typedef uint32_t ma_uint32;
typedef unsigned __int64 ma_uint64; typedef int64_t ma_int64;
typedef uint64_t ma_uint64;
#else #else
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) typedef signed char ma_int8;
#pragma GCC diagnostic push typedef unsigned char ma_uint8;
#pragma GCC diagnostic ignored "-Wlong-long" typedef signed short ma_int16;
#if defined(__clang__) typedef unsigned short ma_uint16;
#pragma GCC diagnostic ignored "-Wc++11-long-long" typedef signed int ma_int32;
typedef unsigned int ma_uint32;
#if defined(_MSC_VER) && !defined(__clang__)
typedef signed __int64 ma_int64;
typedef unsigned __int64 ma_uint64;
#else
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlong-long"
#if defined(__clang__)
#pragma GCC diagnostic ignored "-Wc++11-long-long"
#endif
#endif
typedef signed long long ma_int64;
typedef unsigned long long ma_uint64;
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic pop
#endif #endif
#endif #endif
typedef signed long long ma_int64; #endif /* MA_USE_STDINT */
typedef unsigned long long ma_uint64;
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic pop
#endif
#endif
#if MA_SIZEOF_PTR == 8 #if MA_SIZEOF_PTR == 8
typedef ma_uint64 ma_uintptr; typedef ma_uint64 ma_uintptr;
#else #else
...@@ -2143,6 +2156,84 @@ typedef enum ...@@ -2143,6 +2156,84 @@ typedef enum
#define MA_BACKEND_COUNT (ma_backend_null+1) #define MA_BACKEND_COUNT (ma_backend_null+1)
/* Device notification types. */
typedef enum
{
ma_device_notification_type_started,
ma_device_notification_type_stopped,
ma_device_notification_type_rerouted,
ma_device_notification_type_interruption_began,
ma_device_notification_type_interruption_ended
} ma_device_notification_type;
typedef struct
{
ma_device* pDevice;
ma_device_notification_type type;
union
{
struct
{
int _unused;
} started;
struct
{
int _unused;
} stopped;
struct
{
int _unused;
} rerouted;
struct
{
int _unused;
} interruption;
} data;
} ma_device_notification;
/*
The notification callback for when the application should be notified of a change to the device.
This callback is used for notifying the application of changes such as when the device has started,
stopped, rerouted or an interruption has occurred. Note that not all backends will post all
notification types. For example, some backends will perform automatic stream routing without any
kind of notification to the host program which means miniaudio will never know about it and will
never be able to fire the rerouted notification. You should keep this in mind when designing your
program.
The stopped notification will *not* get fired when a device is rerouted.
Parameters
----------
pNotification (in)
A pointer to a structure containing information about the event. Use the `pDevice` member of
this object to retrieve the relevant device. The `type` member can be used to discriminate
against each of the notification types.
Remarks
-------
Do not restart or uninitialize the device from the callback.
Not all notifications will be triggered by all backends, however the started and stopped events
should be reliable for all backends. Some backends do not have a good way to detect device
stoppages due to unplugging the device which may result in the stopped callback not getting
fired. This has been observed with at least one BSD variant.
The rerouted notification is fired *after* the reroute has occurred. The stopped notification will
*not* get fired when a device is rerouted. The following backends are known to do automatic stream
rerouting, but do not have a way to be notified of the change:
* DirectSound
The interruption notifications are used on mobile platforms for detecting when audio is interrupted
due to things like an incoming phone call. Currently this is only implemented on iOS. None of the
Android backends will report this notification.
*/
typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification);
/* /*
The callback for processing audio data from the device. The callback for processing audio data from the device.
...@@ -2185,7 +2276,12 @@ The proper way to stop the device is to call `ma_device_stop()` from a different ...@@ -2185,7 +2276,12 @@ The proper way to stop the device is to call `ma_device_stop()` from a different
*/ */
typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
/* /*
DEPRECATED. Use ma_device_notification_proc instead.
The callback for when the device has been stopped. The callback for when the device has been stopped.
This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces
...@@ -2202,7 +2298,7 @@ Remarks ...@@ -2202,7 +2298,7 @@ Remarks
------- -------
Do not restart or uninitialize the device from the callback. Do not restart or uninitialize the device from the callback.
*/ */
typedef void (* ma_stop_proc)(ma_device* pDevice); typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */
typedef enum typedef enum
{ {
...@@ -2381,6 +2477,7 @@ struct ma_device_config ...@@ -2381,6 +2477,7 @@ struct ma_device_config
ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */ ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */
ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */
ma_device_data_proc dataCallback; ma_device_data_proc dataCallback;
ma_device_notification_proc notificationCallback;
ma_stop_proc stopCallback; ma_stop_proc stopCallback;
void* pUserData; void* pUserData;
ma_resampler_config resampling; ma_resampler_config resampling;
...@@ -2812,6 +2909,7 @@ struct ma_context ...@@ -2812,6 +2909,7 @@ struct ma_context
ma_proc pa_stream_set_write_callback; ma_proc pa_stream_set_write_callback;
ma_proc pa_stream_set_read_callback; ma_proc pa_stream_set_read_callback;
ma_proc pa_stream_set_suspended_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_is_suspended;
ma_proc pa_stream_flush; ma_proc pa_stream_flush;
ma_proc pa_stream_drain; ma_proc pa_stream_drain;
...@@ -2827,6 +2925,8 @@ struct ma_context ...@@ -2827,6 +2925,8 @@ struct ma_context
/*pa_mainloop**/ ma_ptr pMainLoop; /*pa_mainloop**/ ma_ptr pMainLoop;
/*pa_context**/ ma_ptr pPulseContext; /*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; } pulse;
#endif #endif
#ifdef MA_SUPPORT_JACK #ifdef MA_SUPPORT_JACK
...@@ -3037,21 +3137,22 @@ struct ma_device ...@@ -3037,21 +3137,22 @@ struct ma_device
ma_device_type type; ma_device_type type;
ma_uint32 sampleRate; ma_uint32 sampleRate;
MA_ATOMIC(4, ma_device_state) state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ MA_ATOMIC(4, ma_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_data_proc onData; /* Set once at initialization time and should not be changed after. */
ma_stop_proc onStop; /* 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. */ ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */
void* pUserData; /* Application defined data. */
ma_mutex startStopLock; ma_mutex startStopLock;
ma_event wakeupEvent; ma_event wakeupEvent;
ma_event startEvent; ma_event startEvent;
ma_event stopEvent; ma_event stopEvent;
ma_thread thread; ma_thread thread;
ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */
ma_bool8 noPreSilencedOutputBuffer; ma_bool8 noPreSilencedOutputBuffer;
ma_bool8 noClip; ma_bool8 noClip;
ma_bool8 noDisableDenormals; ma_bool8 noDisableDenormals;
MA_ATOMIC(4, float) masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ MA_ATOMIC(4, 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 device on asynchronous backends. */
struct struct
{ {
ma_resample_algorithm algorithm; ma_resample_algorithm algorithm;
...@@ -3180,6 +3281,8 @@ struct ma_device ...@@ -3180,6 +3281,8 @@ struct ma_device
#ifdef MA_SUPPORT_PULSEAUDIO #ifdef MA_SUPPORT_PULSEAUDIO
struct struct
{ {
/*pa_mainloop**/ ma_ptr pMainLoop;
/*pa_context**/ ma_ptr pPulseContext;
/*pa_stream**/ ma_ptr pStreamPlayback; /*pa_stream**/ ma_ptr pStreamPlayback;
/*pa_stream**/ ma_ptr pStreamCapture; /*pa_stream**/ ma_ptr pStreamCapture;
} pulse; } pulse;
...@@ -3212,7 +3315,7 @@ struct ma_device ...@@ -3212,7 +3315,7 @@ struct ma_device
ma_bool32 isDefaultCaptureDevice; 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 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. */ ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
void* pRouteChangeHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
} coreaudio; } coreaudio;
#endif #endif
#ifdef MA_SUPPORT_SNDIO #ifdef MA_SUPPORT_SNDIO
...@@ -3928,9 +4031,8 @@ then be set directly on the structure. Below are the members of the `ma_device_c ...@@ -3928,9 +4031,8 @@ then be set directly on the structure. Below are the members of the `ma_device_c
dataCallback dataCallback
The callback to fire whenever data is ready to be delivered to or from the device. The callback to fire whenever data is ready to be delivered to or from the device.
stopCallback notificationCallback
The callback to fire whenever the device has stopped, either explicitly via `ma_device_stop()`, or implicitly due to things like the device being The callback to fire when something has changed with the device, such as whether or not it has been started or stopped.
disconnected.
pUserData pUserData
The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`.
...@@ -6008,7 +6110,7 @@ typedef struct ...@@ -6008,7 +6110,7 @@ typedef struct
ma_uint32 flags; ma_uint32 flags;
} ma_resource_manager_data_source_config; } ma_resource_manager_data_source_config;
MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(); MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void);
typedef enum typedef enum
......
/* /*
Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.11.2 - TBD miniaudio - v0.11.2 - 2021-12-31
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
...@@ -89335,7 +89335,7 @@ There have also been some other smaller changes added to this release. ...@@ -89335,7 +89335,7 @@ There have also been some other smaller changes added to this release.
/* /*
REVISION HISTORY REVISION HISTORY
================ ================
v0.11.2 - TBD v0.11.2 - 2021-12-31
- Add a new device notification system to replace the stop callback. The stop callback is still - Add a new device notification system to replace the stop callback. The stop callback is still
in place, but will be removed in version 0.12. New code should use the notificationCallback in place, but will be removed in version 0.12. New code should use the notificationCallback
member in the device config instead of stopCallback. member in the device config instead of stopCallback.
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