From 6548f4e2f179fba8691d22474578b667892c209b Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 27 Jan 2019 12:45:09 +1000 Subject: [PATCH] Set up some infrastructure for separate in/out device IDs. --- mini_al.h | 550 ++++++++++++++++++++++++++------------- tests/mal_duplex.c | 6 +- tests/mal_test_0.c | 7 +- tests/mal_test_0.vcxproj | 24 +- 4 files changed, 395 insertions(+), 192 deletions(-) diff --git a/mini_al.h b/mini_al.h index 652828d8..9d042943 100644 --- a/mini_al.h +++ b/mini_al.h @@ -1575,7 +1575,8 @@ typedef struct typedef struct { mal_device_type deviceType; - mal_device_id* pDeviceID; + mal_device_id* pPlaybackDeviceID; + mal_device_id* pCaptureDeviceID; mal_format format; mal_uint32 channels; mal_uint32 sampleRate; @@ -6121,6 +6122,14 @@ static mal_IMMNotificationClientVtbl g_malNotificationCientVtbl = { }; #endif // MAL_WIN32_DESKTOP +#ifdef MAL_WIN32_DESKTOP +typedef mal_IMMDevice mal_WASAPIDeviceInterface; +#else +typedef mal_IUnknown mal_WASAPIDeviceInterface; +#endif + + + mal_bool32 mal_context_is_device_id_equal__wasapi(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) { mal_assert(pContext != NULL); @@ -6144,84 +6153,6 @@ void mal_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, mal_device_i pInfo->maxSampleRate = pWF->nSamplesPerSec; } -#ifndef MAL_WIN32_DESKTOP -mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IAudioClient** ppAudioClient, mal_IUnknown** ppActivatedInterface) -{ - mal_assert(pContext != NULL); - mal_assert(ppAudioClient != NULL); - - mal_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; - mal_completion_handler_uwp completionHandler; - - IID iid; - if (pDeviceID != NULL) { - mal_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid)); - } else { - if (deviceType == mal_device_type_playback) { - iid = MAL_IID_DEVINTERFACE_AUDIO_RENDER; - } else { - iid = MAL_IID_DEVINTERFACE_AUDIO_CAPTURE; - } - } - - LPOLESTR iidStr; -#if defined(__cplusplus) - HRESULT hr = StringFromIID(iid, &iidStr); -#else - HRESULT hr = StringFromIID(&iid, &iidStr); -#endif - if (FAILED(hr)) { - return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", MAL_OUT_OF_MEMORY); - } - - mal_result result = mal_completion_handler_uwp_init(&completionHandler); - if (result != MAL_SUCCESS) { - mal_CoTaskMemFree(pContext, iidStr); - return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - -#if defined(__cplusplus) - hr = ActivateAudioInterfaceAsync(iidStr, MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); -#else - hr = ActivateAudioInterfaceAsync(iidStr, &MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); -#endif - if (FAILED(hr)) { - mal_completion_handler_uwp_uninit(&completionHandler); - mal_CoTaskMemFree(pContext, iidStr); - return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - - mal_CoTaskMemFree(pContext, iidStr); - - // Wait for the async operation for finish. - mal_completion_handler_uwp_wait(&completionHandler); - mal_completion_handler_uwp_uninit(&completionHandler); - - HRESULT activateResult; - mal_IUnknown* pActivatedInterface; - hr = mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); - mal_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); - - if (FAILED(hr) || FAILED(activateResult)) { - return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - - // Here is where we grab the IAudioClient interface. - hr = mal_IUnknown_QueryInterface(pActivatedInterface, &MAL_IID_IAudioClient, (void**)ppAudioClient); - if (FAILED(hr)) { - return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - - if (ppActivatedInterface) { - *ppActivatedInterface = pActivatedInterface; - } else { - mal_IUnknown_Release(pActivatedInterface); - } - - return MAL_SUCCESS; -} -#endif - mal_result mal_context_get_device_info_from_IAudioClient__wasapi(mal_context* pContext, /*mal_IMMDevice**/void* pMMDevice, mal_IAudioClient* pAudioClient, mal_share_mode shareMode, mal_device_info* pInfo) { mal_assert(pAudioClient != NULL); @@ -6452,6 +6383,116 @@ mal_result mal_context_enumerate_device_collection__wasapi(mal_context* pContext } #endif +#ifdef MAL_WIN32_DESKTOP +mal_result mal_context_get_IAudioClient_Desktop__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IAudioClient** ppAudioClient, mal_IMMDevice** ppMMDevice) +{ + mal_result result; + HRESULT hr; + + mal_assert(pContext != NULL); + mal_assert(ppAudioClient != NULL); + mal_assert(ppMMDevice != NULL); + + result = mal_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); + if (result != MAL_SUCCESS) { + return result; + } + + hr = mal_IMMDevice_Activate(*ppMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient); + if (FAILED(hr)) { + return MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + return MAL_SUCCESS; +} +#else +mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IAudioClient** ppAudioClient, mal_IUnknown** ppActivatedInterface) +{ + mal_assert(pContext != NULL); + mal_assert(ppAudioClient != NULL); + + mal_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; + mal_completion_handler_uwp completionHandler; + + IID iid; + if (pDeviceID != NULL) { + mal_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid)); + } else { + if (deviceType == mal_device_type_playback) { + iid = MAL_IID_DEVINTERFACE_AUDIO_RENDER; + } else { + iid = MAL_IID_DEVINTERFACE_AUDIO_CAPTURE; + } + } + + LPOLESTR iidStr; +#if defined(__cplusplus) + HRESULT hr = StringFromIID(iid, &iidStr); +#else + HRESULT hr = StringFromIID(&iid, &iidStr); +#endif + if (FAILED(hr)) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", MAL_OUT_OF_MEMORY); + } + + mal_result result = mal_completion_handler_uwp_init(&completionHandler); + if (result != MAL_SUCCESS) { + mal_CoTaskMemFree(pContext, iidStr); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + +#if defined(__cplusplus) + hr = ActivateAudioInterfaceAsync(iidStr, MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); +#else + hr = ActivateAudioInterfaceAsync(iidStr, &MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); +#endif + if (FAILED(hr)) { + mal_completion_handler_uwp_uninit(&completionHandler); + mal_CoTaskMemFree(pContext, iidStr); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + mal_CoTaskMemFree(pContext, iidStr); + + // Wait for the async operation for finish. + mal_completion_handler_uwp_wait(&completionHandler); + mal_completion_handler_uwp_uninit(&completionHandler); + + HRESULT activateResult; + mal_IUnknown* pActivatedInterface; + hr = mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); + mal_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); + + if (FAILED(hr) || FAILED(activateResult)) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + // Here is where we grab the IAudioClient interface. + hr = mal_IUnknown_QueryInterface(pActivatedInterface, &MAL_IID_IAudioClient, (void**)ppAudioClient); + if (FAILED(hr)) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (ppActivatedInterface) { + *ppActivatedInterface = pActivatedInterface; + } else { + mal_IUnknown_Release(pActivatedInterface); + } + + return MAL_SUCCESS; +} +#endif + +mal_result mal_context_get_IAudioClient__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IAudioClient** ppAudioClient, mal_WASAPIDeviceInterface** ppDeviceInterface) +{ +#ifdef MAL_WIN32_DESKTOP + return mal_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); +#else + return mal_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); +#endif +} + + mal_result mal_context_enumerate_devices__wasapi(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) { mal_assert(pContext != NULL); @@ -6582,6 +6623,7 @@ void mal_device_uninit__wasapi(mal_device* pDevice) } } + typedef struct { // Input. @@ -6611,13 +6653,18 @@ typedef struct char deviceName[256]; } mal_device_init_internal_data__wasapi; -mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_device_init_internal_data__wasapi* pData) +mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pPlaybackDeviceID, const mal_device_id* pCaptureDeviceID, mal_device_init_internal_data__wasapi* pData) { (void)pContext; mal_assert(pContext != NULL); mal_assert(pData != NULL); + /* Not currently supporting full-duplex. */ + if (deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + pData->pAudioClient = NULL; pData->pRenderClient = NULL; pData->pCaptureClient = NULL; @@ -6630,10 +6677,35 @@ mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_ty MAL_REFERENCE_TIME bufferDurationInMicroseconds; mal_bool32 wasInitializedUsingIAudioClient3 = MAL_FALSE; WAVEFORMATEXTENSIBLE wf; + //mal_WASAPIDeviceInterface* pDeviceInterfacePlayback = NULL; + //mal_WASAPIDeviceInterface* pDeviceInterfaceCapture = NULL; + mal_WASAPIDeviceInterface* pDeviceInterface = NULL; // TEMP: Will be split between playback and capture when full-duplex support is implemented. +#if 0 + /* + We first try initializing the capture device if applicable. If the device is full-duplex we will try re-using the capture audio client, but + if that fails we'll need to initialize a separate playback audio client. + */ + if (deviceType == mal_device_type_capture || deviceType == mal_device_type_duplex) { + + } + + /* We may need a playback render client. If it's a duplex device we want to try re-using the same audio client. Otherwise we need a separate one. */ + if (deviceType == mal_device_type_playback || deviceType == mal_device_type_duplex) { + if (deviceType == mal_device_type_duplex) { + if (pPlaybackDeviceID != NULL && pCaptureDeviceID != NULL && mal_context_is_device_id_equal__wasapi(pContext, pPlaybackDeviceID, pCaptureDeviceID)) { + + } + } + + /* Getting here means we need to initialize a separate playback audio client. */ + + } +#endif + +#if 0 #ifdef MAL_WIN32_DESKTOP - mal_IMMDevice* pMMDevice = NULL; - result = mal_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); + result = mal_context_get_MMDevice__wasapi(pContext, deviceType, (deviceType == mal_device_type_playback) ? pPlaybackDeviceID : pCaptureDeviceID, &pMMDevice); if (result != MAL_SUCCESS) { goto done; } @@ -6645,11 +6717,19 @@ mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_ty } #else mal_IUnknown* pActivatedInterface = NULL; - result = mal_context_get_IAudioClient_UWP__wasapi(pContext, type, pDeviceID, &pData->pAudioClient, &pActivatedInterface); + result = mal_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pActivatedInterface); if (result != MAL_SUCCESS) { goto done; } #endif +#endif + + /* TEMP: Will be replaced when full-duplex is added. */ + result = mal_context_get_IAudioClient__wasapi(pContext, deviceType, (deviceType == mal_device_type_playback) ? pPlaybackDeviceID : pCaptureDeviceID, &pData->pAudioClient, &pDeviceInterface); + if (result != MAL_SUCCESS) { + goto done; + } + // Try enabling hardware offloading. mal_IAudioClient2* pAudioClient2; @@ -6674,7 +6754,7 @@ mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_ty #ifdef MAL_WIN32_DESKTOP // In exclusive mode on desktop we always use the backend's native format. mal_IPropertyStore* pStore = NULL; - hr = mal_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pStore); + hr = mal_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); if (SUCCEEDED(hr)) { PROPVARIANT prop; mal_PropVariantInit(&prop); @@ -6780,9 +6860,9 @@ mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_ty mal_IAudioClient_Release((mal_IAudioClient*)pData->pAudioClient); #ifdef MAL_WIN32_DESKTOP - hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); + hr = mal_IMMDevice_Activate(pDeviceInterface, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); #else - hr = mal_IUnknown_QueryInterface(pActivatedInterface, &MAL_IID_IAudioClient, (void**)&pData->pAudioClient); + hr = mal_IUnknown_QueryInterface(pDeviceInterface, &MAL_IID_IAudioClient, (void**)&pData->pAudioClient); #endif if (SUCCEEDED(hr)) { @@ -6876,7 +6956,7 @@ mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_ty // Grab the name of the device. #ifdef MAL_WIN32_DESKTOP mal_IPropertyStore *pProperties; - hr = mal_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); + hr = mal_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { PROPVARIANT varName; mal_PropVariantInit(&varName); @@ -6893,12 +6973,12 @@ mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_ty done: // Clean up. #ifdef MAL_WIN32_DESKTOP - if (pMMDevice != NULL) { - mal_IMMDevice_Release(pMMDevice); + if (pDeviceInterface != NULL) { + mal_IMMDevice_Release(pDeviceInterface); } #else - if (pActivatedInterface != NULL) { - mal_IUnknown_Release(pActivatedInterface); + if (pDeviceInterface != NULL) { + mal_IUnknown_Release(pDeviceInterface); } #endif @@ -6937,7 +7017,7 @@ mal_result mal_device_reinit__wasapi(mal_device* pDevice) data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; data.shareMode = pDevice->initConfig.shareMode; - mal_result result = mal_device_init_internal__wasapi(pDevice->pContext, pDevice->type, NULL, &data); + mal_result result = mal_device_init_internal__wasapi(pDevice->pContext, pDevice->type, NULL, NULL, &data); if (result != MAL_SUCCESS) { return result; } @@ -7002,7 +7082,7 @@ mal_result mal_device_init__wasapi(mal_context* pContext, const mal_device_confi data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; data.shareMode = pConfig->shareMode; - result = mal_device_init_internal__wasapi(pDevice->pContext, pConfig->deviceType, pConfig->pDeviceID, &data); + result = mal_device_init_internal__wasapi(pDevice->pContext, pConfig->deviceType, pConfig->pPlaybackDeviceID, pConfig->pCaptureDeviceID, &data); if (result != MAL_SUCCESS) { return result; } @@ -7159,7 +7239,7 @@ mal_result mal_device__get_available_frames__wasapi(mal_device* pDevice, mal_uin mal_bool32 mal_device_is_reroute_required__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); - return pDevice->wasapi.hasDefaultDeviceChanged && pDevice->isDefaultDevice; + return pDevice->wasapi.hasDefaultDeviceChanged; } mal_result mal_device_reroute__wasapi(mal_device* pDevice) @@ -8258,6 +8338,11 @@ mal_result mal_device_init__dsound(mal_context* pContext, const mal_device_confi mal_assert(pDevice != NULL); mal_zero_object(&pDevice->dsound); + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + /* DirectSound should use a latency of about 20ms per period for low latency mode. */ if (pDevice->usingDefaultBufferSize) { if (pConfig->performanceProfile == mal_performance_profile_low_latency) { @@ -8304,7 +8389,7 @@ mal_result mal_device_init__dsound(mal_context* pContext, const mal_device_confi // Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices :( if (pConfig->deviceType == mal_device_type_playback) { - mal_result result = mal_context_create_IDirectSound__dsound(pContext, pConfig->shareMode, pConfig->pDeviceID, (mal_IDirectSound**)&pDevice->dsound.pPlayback); + mal_result result = mal_context_create_IDirectSound__dsound(pContext, pConfig->shareMode, pConfig->pPlaybackDeviceID, (mal_IDirectSound**)&pDevice->dsound.pPlayback); if (result != MAL_SUCCESS) { mal_device_uninit__dsound(pDevice); return result; @@ -8418,7 +8503,7 @@ mal_result mal_device_init__dsound(mal_context* pContext, const mal_device_confi return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } } else { - mal_result result = mal_context_create_IDirectSoundCapture__dsound(pContext, pConfig->shareMode, pConfig->pDeviceID, (mal_IDirectSoundCapture**)&pDevice->dsound.pCapture); + mal_result result = mal_context_create_IDirectSoundCapture__dsound(pContext, pConfig->shareMode, pConfig->pCaptureDeviceID, (mal_IDirectSoundCapture**)&pDevice->dsound.pCapture); if (result != MAL_SUCCESS) { mal_device_uninit__dsound(pDevice); return result; @@ -9262,6 +9347,11 @@ mal_result mal_device_init__winmm(mal_context* pContext, const mal_device_config mal_assert(pDevice != NULL); mal_zero_object(&pDevice->winmm); + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + /* No exlusive mode with WinMM. */ if (pConfig->shareMode == mal_share_mode_exclusive) { return MAL_SHARE_MODE_NOT_SUPPORTED; @@ -9276,9 +9366,13 @@ mal_result mal_device_init__winmm(mal_context* pContext, const mal_device_config } } - UINT winMMDeviceID = 0; - if (pConfig->pDeviceID != NULL) { - winMMDeviceID = (UINT)pConfig->pDeviceID->winmm; + UINT winMMDeviceIDPlayback = 0; + UINT winMMDeviceIDCapture = 0; + if (pConfig->pPlaybackDeviceID != NULL) { + winMMDeviceIDPlayback = (UINT)pConfig->pPlaybackDeviceID->winmm; + } + if (pConfig->pCaptureDeviceID != NULL) { + winMMDeviceIDCapture = (UINT)pConfig->pCaptureDeviceID->winmm; } const char* errorMsg = ""; @@ -9326,7 +9420,7 @@ mal_result mal_device_init__winmm(mal_context* pContext, const mal_device_config WORD wChannels = 0; if (pConfig->deviceType == mal_device_type_playback) { WAVEOUTCAPSA caps; - if (((MAL_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { + if (((MAL_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { dwFormats = caps.dwFormats; wChannels = caps.wChannels; } else { @@ -9335,7 +9429,7 @@ mal_result mal_device_init__winmm(mal_context* pContext, const mal_device_config } } else { WAVEINCAPSA caps; - if (((MAL_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { + if (((MAL_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { dwFormats = caps.dwFormats; wChannels = caps.wChannels; } else { @@ -9370,13 +9464,13 @@ mal_result mal_device_init__winmm(mal_context* pContext, const mal_device_config if (pConfig->deviceType == mal_device_type_playback) { - MMRESULT resultMM = ((MAL_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); + MMRESULT resultMM = ((MAL_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevice, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); if (resultMM != MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open playback device.", errorCode = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; } } else { - MMRESULT resultMM = ((MAL_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); + MMRESULT resultMM = ((MAL_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDevice, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); if (resultMM != MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open capture device.", errorCode = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; @@ -10990,12 +11084,17 @@ mal_result mal_device_init__alsa(mal_context* pContext, const mal_device_config* { (void)pContext; + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + mal_assert(pDevice != NULL); mal_zero_object(&pDevice->alsa); mal_snd_pcm_format_t formatALSA = mal_convert_mal_format_to_alsa_format(pConfig->format); - mal_result result = mal_context_open_pcm__alsa(pContext, pConfig->shareMode, pConfig->deviceType, pConfig->pDeviceID, (mal_snd_pcm_t**)&pDevice->alsa.pPCM); + mal_result result = mal_context_open_pcm__alsa(pContext, pConfig->shareMode, pConfig->deviceType, (pConfig->deviceType == mal_device_type_playback) ? pConfig->pPlaybackDeviceID : pConfig->pCaptureDeviceID, (mal_snd_pcm_t**)&pDevice->alsa.pPCM); if (result != MAL_SUCCESS) { return result; } @@ -12912,15 +13011,24 @@ mal_result mal_device_init__pulse(mal_context* pContext, const mal_device_config mal_result result = MAL_SUCCESS; int error = 0; + const char* devPlayback = NULL; + const char* devCapture = NULL; + + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } /* No exclusive mode with the PulseAudio backend. */ if (pConfig->shareMode == mal_share_mode_exclusive) { return MAL_SHARE_MODE_NOT_SUPPORTED; } - const char* dev = NULL; - if (pConfig->pDeviceID != NULL) { - dev = pConfig->pDeviceID->pulse; + if ((pConfig->deviceType == mal_device_type_playback || pConfig->deviceType == mal_device_type_duplex) && pConfig->pPlaybackDeviceID != NULL) { + devPlayback = pConfig->pPlaybackDeviceID->pulse; + } + if ((pConfig->deviceType == mal_device_type_capture || pConfig->deviceType == mal_device_type_duplex) && pConfig->pCaptureDeviceID != NULL) { + devCapture = pConfig->pCaptureDeviceID->pulse; } mal_uint32 bufferSizeInFrames = pConfig->bufferSizeInFrames; @@ -12996,9 +13104,9 @@ mal_result mal_device_init__pulse(mal_context* pContext, const mal_device_config if (pConfig->deviceType == mal_device_type_playback) { - pOP = ((mal_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, dev, mal_device_sink_info_callback, &sinkInfo); + pOP = ((mal_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, devPlayback, mal_device_sink_info_callback, &sinkInfo); } else { - pOP = ((mal_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, dev, mal_device_source_info_callback, &sourceInfo); + pOP = ((mal_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, devCapture, mal_device_source_info_callback, &sourceInfo); } if (pOP != NULL) { @@ -13058,14 +13166,15 @@ mal_result mal_device_init__pulse(mal_context* pContext, const mal_device_config streamFlags = MAL_PA_STREAM_START_CORKED; - if (dev != NULL) { + if (((pConfig->deviceType == mal_device_type_playback) && devPlayback != NULL) || + ((pConfig->deviceType == mal_device_type_capture) && devCapture != NULL)) { streamFlags |= MAL_PA_STREAM_DONT_MOVE | MAL_PA_STREAM_FIX_FORMAT | MAL_PA_STREAM_FIX_RATE | MAL_PA_STREAM_FIX_CHANNELS; } if (pConfig->deviceType == mal_device_type_playback) { - error = ((mal_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, streamFlags, NULL, NULL); + error = ((mal_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((mal_pa_stream*)pDevice->pulse.pStream, devPlayback, &attr, streamFlags, NULL, NULL); } else { - error = ((mal_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, streamFlags); + error = ((mal_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((mal_pa_stream*)pDevice->pulse.pStream, devCapture, &attr, streamFlags); } if (error != MAL_PA_OK) { @@ -13134,18 +13243,23 @@ mal_result mal_device_init__pulse(mal_context* pContext, const mal_device_config // Grab the name of the device if we can. - dev = ((mal_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((mal_pa_stream*)pDevice->pulse.pStream); - if (dev != NULL) { - mal_pa_operation* pOP = NULL; - if (pConfig->deviceType == mal_device_type_playback) { - pOP = ((mal_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, dev, mal_device_sink_name_callback, pDevice); - } else { - pOP = ((mal_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, dev, mal_device_source_name_callback, pDevice); + if (pDevice->type == mal_device_type_playback) { + devPlayback = ((mal_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((mal_pa_stream*)pDevice->pulse.pStream); + if (devPlayback != NULL) { + mal_pa_operation* pOP = ((mal_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, devPlayback, mal_device_sink_name_callback, pDevice); + if (pOP != NULL) { + mal_device__wait_for_operation__pulse(pDevice, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + } } - - if (pOP != NULL) { - mal_device__wait_for_operation__pulse(pDevice, pOP); - ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + } else { + devCapture = ((mal_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((mal_pa_stream*)pDevice->pulse.pStream); + if (devCapture != NULL) { + mal_pa_operation* pOP = ((mal_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, devCapture, mal_device_source_name_callback, pDevice); + if (pOP != NULL) { + mal_device__wait_for_operation__pulse(pDevice, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + } } } @@ -13467,16 +13581,15 @@ mal_result mal_context_init__pulse(mal_context* pContext) pContext->pulse.pa_stream_drop = (mal_proc)_pa_stream_drop; #endif - pContext->onUninit = mal_context_uninit__pulse; - pContext->onDeviceIDEqual = mal_context_is_device_id_equal__pulse; - pContext->onEnumDevices = mal_context_enumerate_devices__pulse; - pContext->onGetDeviceInfo = mal_context_get_device_info__pulse; - pContext->onDeviceInit = mal_device_init__pulse; - pContext->onDeviceUninit = mal_device_uninit__pulse; - pContext->onDeviceStart = mal_device_start__pulse; - pContext->onDeviceStop = mal_device_stop__pulse; - pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__pulse; - pContext->onDeviceMainLoop = mal_device_main_loop__pulse; + pContext->onUninit = mal_context_uninit__pulse; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__pulse; + pContext->onEnumDevices = mal_context_enumerate_devices__pulse; + pContext->onGetDeviceInfo = mal_context_get_device_info__pulse; + pContext->onDeviceInit = mal_device_init__pulse; + pContext->onDeviceUninit = mal_device_uninit__pulse; + pContext->onDeviceStart = mal_device_start__pulse; + pContext->onDeviceStop = mal_device_stop__pulse; + pContext->onDeviceMainLoop = mal_device_main_loop__pulse; // Although we have found the libpulse library, it doesn't necessarily mean PulseAudio is useable. We need to initialize @@ -13780,8 +13893,14 @@ mal_result mal_device_init__jack(mal_context* pContext, const mal_device_config* (void)pContext; + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + /* Only supporting default devices with JACK. */ - if (pConfig->pDeviceID != NULL && pConfig->pDeviceID->jack != 0) { + if (((pConfig->deviceType == mal_device_type_playback || pConfig->deviceType == mal_device_type_duplex) && pConfig->pPlaybackDeviceID != NULL && pConfig->pPlaybackDeviceID->jack != 0) || + ((pConfig->deviceType == mal_device_type_capture || pConfig->deviceType == mal_device_type_duplex) && pConfig->pCaptureDeviceID != NULL && pConfig->pCaptureDeviceID->jack != 0)) { return MAL_NO_DEVICE; } @@ -15678,11 +15797,16 @@ typedef struct char deviceName[256]; } mal_device_init_internal_data__coreaudio; -mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ +mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pPlaybackDeviceID, const mal_device_id* pCaptureDeviceID, mal_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ { + /* Not currently supporting full-duplex. */ + if (deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + mal_assert(pContext != NULL); - mal_assert(deviceType == mal_device_type_playback || deviceType == mal_device_type_capture); - + mal_assert(deviceType == mal_device_type_playback || deviceType == mal_device_type_capture || deviceType == mal_device_type_duplex); + #if defined(MAL_APPLE_DESKTOP) pData->deviceObjectID = 0; #endif @@ -15694,7 +15818,7 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device #if defined(MAL_APPLE_DESKTOP) AudioObjectID deviceObjectID; - result = mal_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); + result = mal_find_AudioObjectID(pContext, deviceType, (deviceType == mal_device_type_playback) ? pPlaybackDeviceID : pCaptureDeviceID, &deviceObjectID); if (result != MAL_SUCCESS) { return result; } @@ -16025,7 +16149,7 @@ mal_result mal_device_reinit_internal__coreaudio(mal_device* pDevice, mal_bool32 data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; data.shareMode = pDevice->initConfig.shareMode; - mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, NULL, &data, (void*)pDevice); + mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, NULL, NULL, &data, (void*)pDevice); if (result != MAL_SUCCESS) { return result; } @@ -16085,7 +16209,7 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, const mal_device_co data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; data.shareMode = pDevice->initConfig.shareMode; - mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pConfig->deviceType, pConfig->pDeviceID, &data, (void*)pDevice); + mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pConfig->deviceType, pConfig->pPlaybackDeviceID, pConfig->pCaptureDeviceID, &data, (void*)pDevice); if (result != MAL_SUCCESS) { return result; } @@ -16743,16 +16867,40 @@ mal_result mal_device_init__sndio(mal_context* pContext, const mal_device_config mal_assert(pDevice != NULL); mal_zero_object(&pDevice->sndio); - - const char* deviceName = MAL_SIO_DEVANY; -//#if defined(__FreeBSD__) || defined(__DragonFly__) -// deviceName = "rsnd/0"; -//#else - if (pConfig->pDeviceID != NULL) { - deviceName = pConfig->pDeviceID->sndio; + + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + + const char* deviceNamePlayback = MAL_SIO_DEVANY; + const char* deviceNameCapture = MAL_SIO_DEVANY; + + if (pConfig->pPlaybackDeviceID != NULL) { + deviceNamePlayback = pConfig->pPlaybackDeviceID->sndio; + } + if (pConfig->pCaptureDeviceID != NULL) { + deviceNameCapture = pConfig->pCaptureDeviceID->sndio; + } + + if (pConfig->deviceType == mal_device_type_playback) { + pDevice->sndio.handle = (mal_ptr)((mal_sio_open_proc)pContext->sndio.sio_open)(deviceNamePlayback, MAL_SIO_PLAY, 0); + } else if (pConfig->deviceType == mal_device_type_capture) { + pDevice->sndio.handle = (mal_ptr)((mal_sio_open_proc)pContext->sndio.sio_open)(deviceNameCapture, MAL_SIO_REC, 0); + } else if (pConfig->deviceType == mal_device_type_duplex) { + /* + TODO: Handle this case. + + - If the device names are the same, try opening in MAL_SIO_PLAY | MAL_SIO_REC mode. + - If the device names are different or MAL_SIO_PLAY | MAL_SIO_REC mode fails, fall back to separate device handles. + */ + mal_assert(MAL_FALSE); + return MAL_INVALID_ARGS; + } else { + mal_assert(MAL_FALSE); /* Should never hit this. */ + return MAL_INVALID_ARGS; } - pDevice->sndio.handle = (mal_ptr)((mal_sio_open_proc)pContext->sndio.sio_open)(deviceName, (pConfig->deviceType == mal_device_type_playback) ? MAL_SIO_PLAY : MAL_SIO_REC, 0); if (pDevice->sndio.handle == NULL) { return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } @@ -16782,6 +16930,13 @@ mal_result mal_device_init__sndio(mal_context* pContext, const mal_device_config // value returned by mal_find_best_channels_from_sio_cap__sndio(). mal_uint32 desiredChannels = pDevice->channels; if (pDevice->usingDefaultChannels) { + const char* deviceName; + if (pConfig->deviceType == mal_device_type_playback) { + deviceName = deviceNamePlayback; + } else { + deviceName = deviceNameCapture; + } + if (strlen(deviceName) > strlen("rsnd/") && strncmp(deviceName, "rsnd/", strlen("rsnd/")) == 0) { desiredChannels = mal_find_best_channels_from_sio_cap__sndio(&caps, pConfig->deviceType, desiredFormat); } @@ -17341,6 +17496,11 @@ mal_result mal_device_init__audio4(mal_context* pContext, const mal_device_confi mal_zero_object(&pDevice->audio4); pDevice->audio4.fd = -1; + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + // The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD // introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as // I'm aware. @@ -17354,12 +17514,28 @@ mal_result mal_device_init__audio4(mal_context* pContext, const mal_device_confi #endif // The first thing to do is open the file. - const char* deviceName = "/dev/audio"; - if (pConfig->pDeviceID != NULL) { - deviceName = pConfig->pDeviceID->audio4; + const char* deviceNamePlayback = "/dev/audio"; + const char* deviceNameCapture = "/dev/audio"; + if (pConfig->pPlaybackDeviceID != NULL) { + deviceNamePlayback = pConfig->pPlaybackDeviceID->audio4; + } + if (pConfig->pCaptureDeviceID != NULL) { + deviceNameCapture = pConfig->pCaptureDeviceID->audio4; + } + + if (pConfig->deviceType == mal_device_type_playback) { + pDevice->audio4.fd = open(deviceNamePlayback, O_WRONLY | O_NONBLOCK, 0); + } else if (pConfig->deviceType == mal_device_type_capture) { + pDevice->audio4.fd = open(deviceNameCapture, O_RDONLY | O_NONBLOCK, 0); + } else if (pConfig->deviceType == mal_device_type_duplex) { + /* TOOD: Implement me. */ + mal_assert(MAL_FALSE); + return MAL_INVALID_ARGS; + } else { + mal_assert(MAL_FALSE); + return MAL_INVALID_ARGS; } - pDevice->audio4.fd = open(deviceName, ((pConfig->deviceType == mal_device_type_playback) ? O_WRONLY : O_RDONLY) | O_NONBLOCK, 0); if (pDevice->audio4.fd == -1) { return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } @@ -17633,7 +17809,7 @@ int mal_open_temp_device__oss() return -1; } -mal_result mal_context_open_device__oss(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, int* pfd) +mal_result mal_context_open_device__oss(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pPlaybackDeviceID, const mal_device_id* pCaptureDevice, int* pfd) { mal_assert(pContext != NULL); mal_assert(pfd != NULL); @@ -17641,14 +17817,28 @@ mal_result mal_context_open_device__oss(mal_context* pContext, mal_device_type d *pfd = -1; - char deviceName[64]; - if (pDeviceID != NULL) { - mal_strncpy_s(deviceName, sizeof(deviceName), pDeviceID->oss, (size_t)-1); - } else { - mal_strncpy_s(deviceName, sizeof(deviceName), "/dev/dsp", (size_t)-1); + const char* deviceNamePlayback = "/dev/dsp"; + const char* deviceNameCapture = "/dev/dsp"; + if (pPlaybackDeviceID != NULL) { + deviceNamePlayback = pPlaybackDeviceID->oss; + } + if (pCaptureDeviceID != NULL) { + deviceNameCapture = pCaptureDeviceID->oss; + } + + if (deviceType == mal_device_type_playback) { + *pfd = open(deviceNamePlayback, O_WRONLY, 0); + } else if (deviceType == mal_device_type_capture) { + *pfd = open(deviceNameCapture, O_RDONLY, 0); + } else if (deviceType == mal_device_type_duplex) { + /* TODO: Implement me. */ + mal_assert(MAL_FALSE); + return MAL_INVALID_ARGS; + } else { + mal_assert(MAL_FALSE); /* Should never hit this. */ + return MAL_INVALID_ARGS; } - *pfd = open(deviceName, (deviceType == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0); if (*pfd == -1) { return MAL_FAILED_TO_OPEN_BACKEND_DEVICE; } @@ -17832,7 +18022,12 @@ mal_result mal_device_init__oss(mal_context* pContext, const mal_device_config* mal_assert(pDevice != NULL); mal_zero_object(&pDevice->oss); - mal_result result = mal_context_open_device__oss(pContext, pConfig->deviceType, pConfig->pDeviceID, &pDevice->oss.fd); + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + + mal_result result = mal_context_open_device__oss(pContext, pConfig->deviceType, pConfig->pPlaybackDeviceID, pConfig->pCaptureDeviceID, &pDevice->oss.fd); if (result != MAL_SUCCESS) { return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } @@ -18344,6 +18539,11 @@ mal_result mal_device_init__aaudio(mal_context* pContext, const mal_device_confi mal_assert(pDevice != NULL); + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + /* No exclusive mode with AAudio. */ if (pConfig->shareMode == mal_share_mode_exclusive) { return MAL_SHARE_MODE_NOT_SUPPORTED; @@ -18972,6 +19172,11 @@ mal_result mal_device_init__opensl(mal_context* pContext, const mal_device_confi return MAL_NO_BACKEND; #endif + /* Full-duplex is not yet implemented. */ + if (pConfig->deviceType == mal_device_type_duplex) { + return MAL_INVALID_ARGS; + } + /* No exclusive mode with OpenSL|ES. */ if (pConfig->shareMode == mal_share_mode_exclusive) { return MAL_SHARE_MODE_NOT_SUPPORTED; @@ -20216,7 +20421,7 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun if (pConfig != NULL) { pContext->config = *pConfig; } else { - pContext->config = mal_context_config_init(NULL); + pContext->config = mal_context_config_init(); } // Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. @@ -20559,13 +20764,14 @@ mal_result mal_device_init(mal_context* pContext, const mal_device_config* pConf pDevice->onData = config.dataCallback; pDevice->onStop = config.stopCallback; - if (((size_t)pDevice % sizeof(pDevice)) != 0) { + if (((mal_uintptr)pDevice % sizeof(pDevice)) != 0) { if (pContext->config.logCallback) { pContext->config.logCallback(pContext, pDevice, MAL_LOG_LEVEL_WARNING, "WARNING: mal_device_init() called for a device that is not properly aligned. Thread safety is not supported."); } } - if (config.pDeviceID == NULL) { + /* TODO: This is only used in Core Audio. Move this to the Core Audio backend. Also, this logic does not handle full-duplex devices properly */ + if (config.pPlaybackDeviceID == NULL || config.pCaptureDeviceID == NULL) { pDevice->isDefaultDevice = MAL_TRUE; } @@ -20650,22 +20856,14 @@ mal_result mal_device_init(mal_context* pContext, const mal_device_config* pConf mal_device__post_init_setup(pDevice); - // If the backend did not fill out a name for the device, try a generic method. + // If the backend did not fill out a name for the device, try a generic method. TODO: Update this to support full-duplex devices. Need to split "name" out into "playbackDeviceName" and "captureDeviceName". if (pDevice->name[0] == '\0') { - if (mal_context__try_get_device_name_by_id(pContext, config.deviceType, config.pDeviceID, pDevice->name, sizeof(pDevice->name)) != MAL_SUCCESS) { + if (mal_context__try_get_device_name_by_id(pContext, config.deviceType, (config.deviceType == mal_device_type_playback) ? config.pPlaybackDeviceID : config.pCaptureDeviceID, pDevice->name, sizeof(pDevice->name)) != MAL_SUCCESS) { // We failed to get the device name, so fall back to some generic names. - if (config.pDeviceID == NULL) { - if (config.deviceType == mal_device_type_playback) { - mal_strncpy_s(pDevice->name, sizeof(pDevice->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - mal_strncpy_s(pDevice->name, sizeof(pDevice->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } + if (config.deviceType == mal_device_type_playback) { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), (config.pPlaybackDeviceID == NULL) ? MAL_DEFAULT_PLAYBACK_DEVICE_NAME : "Playback Device", (size_t)-1); } else { - if (config.deviceType == mal_device_type_playback) { - mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Playback Device", (size_t)-1); - } else { - mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Capture Device", (size_t)-1); - } + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), (config.pCaptureDeviceID == NULL) ? MAL_DEFAULT_CAPTURE_DEVICE_NAME : "Capture Device", (size_t)-1); } } } diff --git a/tests/mal_duplex.c b/tests/mal_duplex.c index 3c9d3817..e6cf1d7a 100644 --- a/tests/mal_duplex.c +++ b/tests/mal_duplex.c @@ -32,7 +32,9 @@ int main(int argc, char** argv) mal_backend backend = mal_backend_wasapi; - mal_context_config contextConfig = mal_context_config_init(log_callback); + mal_context_config contextConfig = mal_context_config_init(); + contextConfig.logCallback = log_callback; + mal_context context; result = mal_context_init(&backend, 1, &contextConfig, &context); if (result != MAL_SUCCESS) { @@ -41,7 +43,7 @@ int main(int argc, char** argv) } mal_device_config deviceConfig = mal_device_config_init(mal_device_type_duplex); - deviceConfig.pDeviceID = NULL; + deviceConfig.pPlaybackDeviceID = NULL; deviceConfig.format = mal_format_f32; deviceConfig.channels = 2; deviceConfig.sampleRate = 44100; diff --git a/tests/mal_test_0.c b/tests/mal_test_0.c index 1539b7e3..0ac8a08a 100644 --- a/tests/mal_test_0.c +++ b/tests/mal_test_0.c @@ -2117,7 +2117,8 @@ int do_backend_test(mal_backend backend) // Context. printf(" Creating Context... "); { - mal_context_config contextConfig = mal_context_config_init(on_log); + mal_context_config contextConfig = mal_context_config_init(); + contextConfig.logCallback = on_log; result = mal_context_init(&backend, 1, &contextConfig, &context); if (result == MAL_SUCCESS) { @@ -2285,7 +2286,9 @@ int do_playback_test(mal_backend backend) // Device. printf(" Opening Device... "); { - mal_context_config contextConfig = mal_context_config_init(on_log); + mal_context_config contextConfig = mal_context_config_init(); + contextConfig.logCallback = on_log; + mal_device_config deviceConfig = mal_device_config_init(mal_device_type_playback); deviceConfig.pUserData = &callbackData; deviceConfig.dataCallback = on_send__playback_test; diff --git a/tests/mal_test_0.vcxproj b/tests/mal_test_0.vcxproj index a72f593b..891c4f9d 100644 --- a/tests/mal_test_0.vcxproj +++ b/tests/mal_test_0.vcxproj @@ -319,12 +319,12 @@ true - false - false - false - false - false - false + true + true + true + true + true + true true @@ -359,12 +359,12 @@ true - true - true - true - true - true - true + false + false + false + false + false + false true