mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-21 15:56:58 +02:00
Version 0.11.2
This commit is contained in:
+267
-159
@@ -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(¬ification);
|
||||
notification.pDevice = pDevice;
|
||||
notification.type = type;
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
static void ma_device__on_notification(ma_device_notification notification)
|
||||
{
|
||||
MA_ASSERT(notification.pDevice != NULL);
|
||||
|
||||
if (notification.pDevice->onNotification != NULL) {
|
||||
notification.pDevice->onNotification(¬ification);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user