Version 0.11.2

This commit is contained in:
David Reid
2021-12-31 19:15:03 +10:00
parent 354d0c91c6
commit 42abbbea46
3 changed files with 405 additions and 195 deletions
+267 -159
View File
@@ -1,6 +1,6 @@
/*
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
@@ -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)
{
if (src == NULL) {
return NULL;
}
size_t sz = strlen(src)+1;
char* dst = (char*)ma_malloc(sz, pAllocationCallbacks);
if (dst == NULL) {
@@ -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)
{
float masterVolumeFactor;
@@ -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__on_notification_rerouted(pDevice);
return MA_SUCCESS;
}
@@ -10890,9 +10947,9 @@ struct ma_IDirectSoundCapture
{
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 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 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_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_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); }
@@ -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) {
BYTE nameFromReg[512];
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);
if (result == ERROR_SUCCESS) {
if (resultWin32 == ERROR_SUCCESS) {
/* We have the value from the registry, so now we need to construct the name string. */
char name[1024];
if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
@@ -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_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s);
typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
@@ -16365,7 +16423,7 @@ static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position
}
#endif
static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operation* pOP)
static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
{
int resultPA;
ma_pa_operation_state_t state;
@@ -16379,7 +16437,7 @@ static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operat
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) {
return ma_result_from_pulse(resultPA);
}
@@ -16388,7 +16446,7 @@ static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operat
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;
@@ -16396,19 +16454,19 @@ static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma
return MA_INVALID_ARGS;
}
result = ma_wait_for_operation__pulse(pContext, pOP);
result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
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;
ma_pa_context_state_t state;
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) {
break; /* Done. */
}
@@ -16418,7 +16476,7 @@ static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pC
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) {
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
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;
ma_pa_stream_state_t state;
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) {
break; /* Done. */
}
@@ -16444,7 +16502,7 @@ static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pCo
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) {
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
}
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)
{
ma_pa_sink_info* pInfoOut;
@@ -16486,6 +16590,7 @@ static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const m
(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)
{
ma_device* pDevice;
@@ -16517,7 +16622,7 @@ static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const m
(void)pPulseContext; /* Unused. */
}
#endif
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
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)
@@ -16540,7 +16645,7 @@ static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const c
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)
@@ -16684,7 +16789,7 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en
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);
if (result != MA_SUCCESS) {
@@ -16701,7 +16806,7 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en
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);
if (result != MA_SUCCESS) {
@@ -16822,7 +16927,7 @@ static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_devi
}
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 {
result = MA_ERROR;
goto done;
@@ -16860,6 +16965,10 @@ static ma_result ma_device_uninit__pulse(ma_device* pDevice)
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;
}
@@ -16875,7 +16984,7 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra
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;
char actualStreamName[256];
@@ -16888,7 +16997,7 @@ static ma_pa_stream* ma_context__pa_stream_new__pulse(ma_context* pContext, cons
}
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
if (suspended == 1) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n");
if (pDevice->onStop) {
pDevice->onStop(pDevice);
}
ma_device__on_notification_stopped(pDevice);
} else {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n");
ma_device__on_notification_started(pDevice);
}
}
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)
{
/*
@@ -17118,6 +17235,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
MA_ASSERT(pDevice != NULL);
MA_ZERO_OBJECT(&pDevice->pulse);
printf("TESTING\n");
if (pConfig->deviceType == ma_device_type_loopback) {
return MA_DEVICE_TYPE_NOT_SUPPORTED;
}
@@ -17148,6 +17267,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
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) {
result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);
if (result != MA_SUCCESS) {
@@ -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);
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) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.");
result = MA_ERROR;
@@ -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. */
((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. */
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
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) {
goto on_error2;
}
@@ -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);
#if 0
/* Name. */
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) {
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_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
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, pDevice->pulse.pMainLoop, pOP);
}
#endif
}
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
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) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.");
result = MA_ERROR;
@@ -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. */
((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. */
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
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) {
goto on_error3;
}
@@ -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;
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. */
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) {
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_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
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, pDevice->pulse.pMainLoop, pOP);
}
#endif
}
@@ -17443,7 +17578,7 @@ static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_typ
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) {
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;
@@ -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) {
/* 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_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);
if (result != MA_SUCCESS) {
@@ -17527,7 +17662,7 @@ static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
the callbacks deal with it.
*/
while (ma_device_get_state(pDevice) == ma_device_state_started) {
resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->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) {
break;
}
@@ -17541,7 +17676,7 @@ static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
{
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;
}
@@ -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_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);
ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
#ifndef MA_NO_RUNTIME_LINKING
ma_dlclose(pContext, pContext->pulse.pulseSO);
#endif
@@ -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_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_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_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");
@@ -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_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback;
ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback;
ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended;
ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
@@ -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_read_callback = (ma_proc)_pa_stream_set_read_callback;
pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback;
pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback;
pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended;
pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
@@ -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;
#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. */
pContext->pulse.pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
if (pContext->pulse.pMainLoop == NULL) {
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop.");
#ifndef MA_NO_RUNTIME_LINKING
ma_dlclose(pContext, pContext->pulse.pulseSO);
#endif
return MA_FAILED_TO_INIT_BACKEND;
/* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */
pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) {
return MA_OUT_OF_MEMORY;
}
pContext->pulse.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;
pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) {
ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
return MA_OUT_OF_MEMORY;
}
/* 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*)pContext->pulse.pPulseContext, pConfig->pulse.pServerName, (pConfig->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL));
result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext);
if (result != MA_SUCCESS) {
ma_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*)(pContext->pulse.pMainLoop));
ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
#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_context_wait_for_pa_context_to_connect__pulse(pContext);
if (result != MA_SUCCESS) {
((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;
}
/* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */
pCallbacks->onContextInit = ma_context_init__pulse;
pCallbacks->onContextUninit = ma_context_uninit__pulse;
@@ -18348,17 +18469,13 @@ static ma_result ma_device_start__jack(ma_device* pDevice)
static ma_result ma_device_stop__jack(ma_device* pDevice)
{
ma_context* pContext = pDevice->pContext;
ma_stop_proc onStop;
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.");
return MA_ERROR;
}
onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
ma_device__on_notification_stopped(pDevice);
return MA_SUCCESS;
}
@@ -20083,7 +20200,7 @@ static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice
AudioBufferList* pNewAudioBufferList;
pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
if (pNewAudioBufferList != NULL) {
if (pNewAudioBufferList == NULL) {
return MA_OUT_OF_MEMORY;
}
@@ -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.");
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
@@ -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.
*/
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;
if (onStop) {
onStop(pDevice);
}
ma_device__on_notification_stopped(pDevice);
ma_event_signal(&pDevice->coreaudio.stopEvent);
} else {
UInt32 isRunning;
@@ -20337,8 +20453,6 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio
}
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:
@@ -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
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!).
*/
if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
@@ -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
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;
}
/* Getting here means we need to stop the device. */
onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
ma_device__on_notification_stopped(pDevice);
}
}
@@ -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)
#endif
#if defined(MA_APPLE_MOBILE)
@interface ma_router_change_handler:NSObject {
@interface ma_ios_notification_handler:NSObject {
ma_device* m_pDevice;
}
@end
@implementation ma_router_change_handler
@implementation ma_ios_notification_handler
-(id)init:(ma_device*)pDevice
{
self = [super init];
m_pDevice = pDevice;
/* For route changes. */
[[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;
}
-(void)dealloc
{
[self remove_handler];
[super dealloc];
}
-(void)remove_handler
{
[[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
@@ -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);
/* Temporarily disabling this section of code because it appears to be causing errors. */
#if 0
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
/* Let the application know about the route change. */
ma_device__on_notification_rerouted(m_pDevice);
}
@end
#endif
@@ -20707,9 +20822,9 @@ static ma_result ma_device_uninit__coreaudio(ma_device* pDevice)
ma_device__untrack__coreaudio(pDevice);
#endif
#if defined(MA_APPLE_MOBILE)
if (pDevice->coreaudio.pRouteChangeHandler != NULL) {
ma_router_change_handler* pRouteChangeHandler = (MA_BRIDGE_TRANSFER ma_router_change_handler*)pDevice->coreaudio.pRouteChangeHandler;
[pRouteChangeHandler remove_handler];
if (pDevice->coreaudio.pNotificationHandler != NULL) {
ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler;
[pNotificationHandler remove_handler];
}
#endif
@@ -20769,8 +20884,6 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
AURenderCallbackStruct callbackInfo;
#if defined(MA_APPLE_DESKTOP)
AudioObjectID deviceObjectID;
#else
UInt32 actualPeriodSizeInFramesSize = sizeof(actualPeriodSizeInFrames);
#endif
/* 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
}
#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
just going to set it to the value of kAudioUnitProperty_MaximumFramesPerSlice.
On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point
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);
if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(status);
@autoreleasepool {
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
MA_ASSERT(pAudioSession != NULL);
[pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil];
actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate));
}
#endif
@@ -21382,7 +21498,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c
differently on non-Desktop Apple platforms.
*/
#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
return MA_SUCCESS;
@@ -24701,8 +24817,6 @@ static ma_result ma_device_start__aaudio(ma_device* pDevice)
static ma_result ma_device_stop__aaudio(ma_device* pDevice)
{
ma_stop_proc onStop;
MA_ASSERT(pDevice != NULL);
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)
}
}
onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
ma_device__on_notification_stopped(pDevice);
return MA_SUCCESS;
}
@@ -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)
{
SLresult resultSL;
ma_stop_proc onStop;
MA_ASSERT(pDevice != NULL);
@@ -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. */
onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
ma_device__on_notification_stopped(pDevice);
return MA_SUCCESS;
}
@@ -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
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) {
device.scriptNode.onaudioprocess = function(e) {
@@ -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. */
}
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. */
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
}
} else {
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) {
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)
}, pDevice->webaudio.indexPlayback);
}
ma_stop_proc onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
ma_device__on_notification_stopped(pDevice);
return MA_SUCCESS;
}
@@ -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. */
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. */
ma_event_wait(&pDevice->wakeupEvent);
@@ -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_event_signal(&pDevice->startEvent);
ma_device__on_notification_started(pDevice);
if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
} else {
@@ -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
stopped due to the device being physically unplugged or disabled via an OS setting.
*/
if (pDevice->onStop && stopResult != MA_SUCCESS) {
pDevice->onStop(pDevice);
if (stopResult == MA_SUCCESS) {
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. */
@@ -27623,13 +27731,11 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
return MA_INVALID_ARGS;
}
/* Check that we have our callbacks defined. */
if (pContext->callbacks.onDeviceInit == NULL) {
return MA_INVALID_OPERATION;
}
/* Basic config validation. */
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
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
pDevice->pContext = pContext;
/* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
pDevice->pUserData = pConfig->pUserData;
pDevice->onData = pConfig->dataCallback;
pDevice->onStop = pConfig->stopCallback;
pDevice->pUserData = pConfig->pUserData;
pDevice->onData = pConfig->dataCallback;
pDevice->onNotification = pConfig->notificationCallback;
pDevice->onStop = pConfig->stopCallback;
if (pConfig->playback.pDeviceID != NULL) {
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)
if (result == MA_SUCCESS) {
ma_device__set_state(pDevice, ma_device_state_started);
ma_device__on_notification_started(pDevice);
}
} else {
/*
@@ -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;