mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 01:04:02 +02:00
Clean up the WASAPI backend.
This commit is contained in:
@@ -2180,14 +2180,6 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
|
|||||||
mal_IMMNotificationClient notificationClient;
|
mal_IMMNotificationClient notificationClient;
|
||||||
/*HANDLE*/ mal_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
|
/*HANDLE*/ mal_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
|
||||||
/*HANDLE*/ mal_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
|
/*HANDLE*/ mal_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
|
||||||
void* pDeviceBufferPlayback;
|
|
||||||
void* pDeviceBufferCapture;
|
|
||||||
mal_uint32 deviceBufferFramesRemainingPlayback;
|
|
||||||
mal_uint32 deviceBufferFramesRemainingCapture;
|
|
||||||
mal_uint32 deviceBufferFramesCapacityPlayback;
|
|
||||||
mal_uint32 deviceBufferFramesCapacityCapture;
|
|
||||||
mal_uint32 periodSizeInFramesPlayback;
|
|
||||||
mal_uint32 periodSizeInFramesCapture;
|
|
||||||
mal_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalBufferSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */
|
mal_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalBufferSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */
|
||||||
mal_uint32 actualBufferSizeInFramesCapture;
|
mal_uint32 actualBufferSizeInFramesCapture;
|
||||||
mal_uint32 originalBufferSizeInFrames;
|
mal_uint32 originalBufferSizeInFrames;
|
||||||
@@ -2195,7 +2187,10 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
|
|||||||
mal_uint32 originalPeriods;
|
mal_uint32 originalPeriods;
|
||||||
mal_bool32 hasDefaultPlaybackDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
|
mal_bool32 hasDefaultPlaybackDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
|
||||||
mal_bool32 hasDefaultCaptureDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
|
mal_bool32 hasDefaultCaptureDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
|
||||||
mal_bool32 isStarted;
|
mal_uint32 periodSizeInFramesPlayback;
|
||||||
|
mal_uint32 periodSizeInFramesCapture;
|
||||||
|
mal_bool32 isStartedCapture;
|
||||||
|
mal_bool32 isStartedPlayback;
|
||||||
} wasapi;
|
} wasapi;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MAL_SUPPORT_DSOUND
|
#ifdef MAL_SUPPORT_DSOUND
|
||||||
@@ -7380,7 +7375,7 @@ mal_result mal_device_reinit__wasapi(mal_device* pDevice, mal_device_type device
|
|||||||
mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
|
mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
|
||||||
|
|
||||||
/* The device may be in a started state. If so we need to immediately restart it. */
|
/* The device may be in a started state. If so we need to immediately restart it. */
|
||||||
if (pDevice->wasapi.isStarted) {
|
if (pDevice->wasapi.isStartedCapture) {
|
||||||
HRESULT hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
HRESULT hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device after reinitialization.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device after reinitialization.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
||||||
@@ -7416,7 +7411,7 @@ mal_result mal_device_reinit__wasapi(mal_device* pDevice, mal_device_type device
|
|||||||
mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
|
mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
|
||||||
|
|
||||||
/* The device may be in a started state. If so we need to immediately restart it. */
|
/* The device may be in a started state. If so we need to immediately restart it. */
|
||||||
if (pDevice->wasapi.isStarted) {
|
if (pDevice->wasapi.isStartedPlayback) {
|
||||||
HRESULT hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
HRESULT hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device after reinitialization.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device after reinitialization.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
||||||
@@ -7604,7 +7599,8 @@ mal_result mal_device_init__wasapi(mal_context* pContext, const mal_device_confi
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mal_atomic_exchange_32(&pDevice->wasapi.isStarted, MAL_FALSE);
|
mal_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MAL_FALSE);
|
||||||
|
mal_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MAL_FALSE);
|
||||||
|
|
||||||
result = MAL_SUCCESS;
|
result = MAL_SUCCESS;
|
||||||
|
|
||||||
@@ -7618,84 +7614,6 @@ done:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
mal_result mal_device_start__wasapi(mal_device* pDevice)
|
|
||||||
{
|
|
||||||
mal_assert(pDevice != NULL);
|
|
||||||
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
if (pDevice->type == mal_device_type_capture || pDevice->type == mal_device_type_duplex) {
|
|
||||||
hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pDevice->type == mal_device_type_playback || pDevice->type == mal_device_type_duplex) {
|
|
||||||
hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
if (pDevice->type == mal_device_type_duplex) {
|
|
||||||
mal_IAudioClient_Stop((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
||||||
mal_IAudioClient_Reset((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
||||||
}
|
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mal_atomic_exchange_32(&pDevice->wasapi.isStarted, MAL_TRUE);
|
|
||||||
return MAL_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
mal_result mal_device_stop__wasapi(mal_device* pDevice)
|
|
||||||
{
|
|
||||||
mal_assert(pDevice != NULL);
|
|
||||||
|
|
||||||
if (pDevice->type == mal_device_type_capture || pDevice->type == mal_device_type_duplex) {
|
|
||||||
if (pDevice->wasapi.pAudioClientCapture == NULL) {
|
|
||||||
return MAL_DEVICE_NOT_INITIALIZED;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT hr = mal_IAudioClient_Stop((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The audio client needs to be reset otherwise restarting will fail. */
|
|
||||||
hr = mal_IAudioClient_Reset((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
#ifdef MAL_DEBUG_OUTPUT
|
|
||||||
printf("IAudioClient_Reset (Capture) Returned %d\n", (int)hr);
|
|
||||||
#endif
|
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pDevice->type == mal_device_type_playback || pDevice->type == mal_device_type_duplex) {
|
|
||||||
if (pDevice->wasapi.pAudioClientPlayback == NULL) {
|
|
||||||
return MAL_DEVICE_NOT_INITIALIZED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Wait until every sample that was written by the callback has been processed. */
|
|
||||||
|
|
||||||
HRESULT hr = mal_IAudioClient_Stop((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The audio client needs to be reset otherwise restarting will fail. */
|
|
||||||
hr = mal_IAudioClient_Reset((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mal_atomic_exchange_32(&pDevice->wasapi.isStarted, MAL_FALSE);
|
|
||||||
return MAL_SUCCESS;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
mal_result mal_device__get_available_frames__wasapi(mal_device* pDevice, mal_IAudioClient* pAudioClient, mal_uint32* pFrameCount)
|
mal_result mal_device__get_available_frames__wasapi(mal_device* pDevice, mal_IAudioClient* pAudioClient, mal_uint32* pFrameCount)
|
||||||
{
|
{
|
||||||
mal_assert(pDevice != NULL);
|
mal_assert(pDevice != NULL);
|
||||||
@@ -7771,299 +7689,11 @@ mal_result mal_device_reroute__wasapi(mal_device* pDevice, mal_device_type devic
|
|||||||
return MAL_SUCCESS;
|
return MAL_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
mal_result mal_device_write__wasapi(mal_device* pDevice, const void* pPCMFrames, mal_uint32 frameCount)
|
|
||||||
{
|
|
||||||
mal_result result = MAL_SUCCESS;
|
|
||||||
mal_bool32 wasStartedOnEntry;
|
|
||||||
mal_bool32 exitOuterLoop = MAL_FALSE;
|
|
||||||
mal_uint32 totalFramesWritten;
|
|
||||||
HRESULT hr;
|
|
||||||
DWORD waitResult;
|
|
||||||
|
|
||||||
wasStartedOnEntry = pDevice->wasapi.isStarted;
|
|
||||||
|
|
||||||
/* Try to write every frame. */
|
|
||||||
totalFramesWritten = 0;
|
|
||||||
while (totalFramesWritten < frameCount) {
|
|
||||||
/*
|
|
||||||
If we've already got a pointer to the device buffer we will want to fill that up first. Once it's consumed we'll want to reset
|
|
||||||
the event and set the cached pointer to NULL.
|
|
||||||
*/
|
|
||||||
if (pDevice->wasapi.pDeviceBufferPlayback != NULL && pDevice->wasapi.deviceBufferFramesRemainingPlayback > 0) {
|
|
||||||
mal_uint32 bpf = mal_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
||||||
mal_uint32 deviceBufferFramesConsumed = pDevice->wasapi.deviceBufferFramesCapacityPlayback - pDevice->wasapi.deviceBufferFramesRemainingPlayback;
|
|
||||||
|
|
||||||
void* pDst = (mal_uint8*)pDevice->wasapi.pDeviceBufferPlayback + (deviceBufferFramesConsumed * bpf);
|
|
||||||
const void* pSrc = (const mal_uint8*)pPCMFrames + (totalFramesWritten * bpf);
|
|
||||||
mal_uint32 framesToCopy = mal_min(pDevice->wasapi.deviceBufferFramesRemainingPlayback, (frameCount - totalFramesWritten));
|
|
||||||
mal_copy_memory(pDst, pSrc, framesToCopy * bpf);
|
|
||||||
|
|
||||||
pDevice->wasapi.deviceBufferFramesRemainingPlayback -= framesToCopy;
|
|
||||||
totalFramesWritten += framesToCopy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getting here means we've consumed the device buffer and need to wait for more to become available. */
|
|
||||||
if (pDevice->wasapi.deviceBufferFramesCapacityPlayback > 0 && pDevice->wasapi.deviceBufferFramesRemainingPlayback == 0) {
|
|
||||||
hr = mal_IAudioRenderClient_ReleaseBuffer((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.deviceBufferFramesCapacityPlayback, 0);
|
|
||||||
pDevice->wasapi.pDeviceBufferPlayback = NULL;
|
|
||||||
pDevice->wasapi.deviceBufferFramesRemainingPlayback = 0;
|
|
||||||
pDevice->wasapi.deviceBufferFramesCapacityPlayback = 0;
|
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
result = MAL_FAILED_TO_UNMAP_DEVICE_BUFFER;
|
|
||||||
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetEvent(pDevice->wasapi.hEventPlayback);
|
|
||||||
|
|
||||||
/*
|
|
||||||
After releasing the buffer, if the device is not started we need to do so. Note from MSDN:
|
|
||||||
|
|
||||||
The event handle should be in the nonsignaled state at the time that the client calls the Start method.
|
|
||||||
|
|
||||||
This means we should start the device only after setting the event to non-signaled (after the call to ResetEvent()).
|
|
||||||
*/
|
|
||||||
if (!pDevice->wasapi.isStarted && !wasStartedOnEntry) {
|
|
||||||
result = mal_device_start__wasapi(pDevice);
|
|
||||||
if (result != MAL_SUCCESS) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mal_assert(totalFramesWritten <= frameCount);
|
|
||||||
if (totalFramesWritten == frameCount) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait for data to become available. Exclusive mode is slightly different. We always wait and then use the exact frame count returned by GetCurrentPadding(). */
|
|
||||||
for (;;) {
|
|
||||||
if (pDevice->playback.shareMode == mal_share_mode_exclusive) {
|
|
||||||
waitResult = WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
|
|
||||||
if (waitResult == WAIT_FAILED) {
|
|
||||||
result = MAL_ERROR;
|
|
||||||
exitOuterLoop = MAL_TRUE;
|
|
||||||
break; /* An error occurred while waiting for the event. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the device has been stopped don't continue. */
|
|
||||||
if (!pDevice->wasapi.isStarted && wasStartedOnEntry) {
|
|
||||||
exitOuterLoop = MAL_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We may need to reroute the device. */
|
|
||||||
if (mal_device_is_reroute_required__wasapi(pDevice, mal_device_type_playback)) {
|
|
||||||
result = mal_device_reroute__wasapi(pDevice, mal_device_type_playback);
|
|
||||||
if (result != MAL_SUCCESS) {
|
|
||||||
exitOuterLoop = MAL_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Check what's available. If there's not enough data available we need to wait. How much data must be available depends on whether or not the
|
|
||||||
device is in playback-only mode or duplex mode. In playback-only mode we only care about a period being available. In duplex mode we want at
|
|
||||||
least a whole period in the buffer ready for playback in addition to a whole period being available.
|
|
||||||
*/
|
|
||||||
result = mal_device__get_available_frames__wasapi(pDevice, (mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.deviceBufferFramesCapacityPlayback);
|
|
||||||
if (result != MAL_SUCCESS) {
|
|
||||||
exitOuterLoop = MAL_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* In exclusive mode, the frame count needs to exactly match the value returned by GetCurrentPadding(). */
|
|
||||||
if (pDevice->playback.shareMode == mal_share_mode_exclusive && pDevice->wasapi.deviceBufferFramesCapacityPlayback > 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
mal_uint32 minAvailableFrames = pDevice->wasapi.periodSizeInFramesPlayback;
|
|
||||||
if (pDevice->type == mal_device_type_duplex) {
|
|
||||||
if (!pDevice->wasapi.isStarted) {
|
|
||||||
minAvailableFrames = pDevice->wasapi.periodSizeInFramesPlayback*2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pDevice->wasapi.deviceBufferFramesCapacityPlayback >= minAvailableFrames) {
|
|
||||||
pDevice->wasapi.deviceBufferFramesCapacityPlayback = minAvailableFrames;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//printf("TRACE: WAITING\n");
|
|
||||||
|
|
||||||
/* Getting here means we need to wait for more data. */
|
|
||||||
waitResult = WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
|
|
||||||
if (waitResult == WAIT_FAILED) {
|
|
||||||
result = MAL_ERROR;
|
|
||||||
exitOuterLoop = MAL_TRUE;
|
|
||||||
break; /* An error occurred while waiting for the event. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exitOuterLoop) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = mal_IAudioRenderClient_GetBuffer((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.deviceBufferFramesCapacityPlayback, (BYTE**)&pDevice->wasapi.pDeviceBufferPlayback);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
result = MAL_FAILED_TO_MAP_DEVICE_BUFFER;
|
|
||||||
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
pDevice->wasapi.deviceBufferFramesRemainingPlayback = pDevice->wasapi.deviceBufferFramesCapacityPlayback;
|
|
||||||
//printf("TRACE 1: Playback: %d, %d\n", pDevice->wasapi.deviceBufferFramesCapacityPlayback, pDevice->wasapi.periodSizeInFramesPlayback);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
mal_result mal_device_read__wasapi(mal_device* pDevice, void* pPCMFrames, mal_uint32 frameCount)
|
|
||||||
{
|
|
||||||
mal_result result = MAL_SUCCESS;
|
|
||||||
mal_uint32 totalFramesRead;
|
|
||||||
HRESULT hr;
|
|
||||||
DWORD waitResult;
|
|
||||||
DWORD flags; /* Passed to IAudioCaptureClient_GetBuffer(). */
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is mostly the same as mal_device_write__wasapi() with only a few exceptions:
|
|
||||||
- If the device is not already started, it's started immediately.
|
|
||||||
*/
|
|
||||||
if (!pDevice->wasapi.isStarted) {
|
|
||||||
result = mal_device_start__wasapi(pDevice);
|
|
||||||
if (result != MAL_SUCCESS) {
|
|
||||||
return result; /* Failed to auto-start device. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to read every frame. */
|
|
||||||
totalFramesRead = 0;
|
|
||||||
while (totalFramesRead < frameCount) {
|
|
||||||
/* Make sure we consume any cached data before waiting for more. */
|
|
||||||
if (pDevice->wasapi.pDeviceBufferCapture != NULL && pDevice->wasapi.deviceBufferFramesRemainingCapture > 0) {
|
|
||||||
mal_uint32 bpf = mal_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
||||||
mal_uint32 deviceBufferFramesConsumed = pDevice->wasapi.deviceBufferFramesCapacityCapture - pDevice->wasapi.deviceBufferFramesRemainingCapture;
|
|
||||||
|
|
||||||
void* pDst = (mal_uint8*)pPCMFrames + (totalFramesRead * bpf);
|
|
||||||
const void* pSrc = (const mal_uint8*)pDevice->wasapi.pDeviceBufferCapture + (deviceBufferFramesConsumed * bpf);
|
|
||||||
mal_uint32 framesToCopy = mal_min(pDevice->wasapi.deviceBufferFramesRemainingCapture, (frameCount - totalFramesRead));
|
|
||||||
mal_copy_memory(pDst, pSrc, framesToCopy * bpf);
|
|
||||||
|
|
||||||
pDevice->wasapi.deviceBufferFramesRemainingCapture -= framesToCopy;
|
|
||||||
totalFramesRead += framesToCopy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getting here means we've consumed the device buffer and need to wait for more to become available. */
|
|
||||||
if (pDevice->wasapi.deviceBufferFramesCapacityCapture > 0 && pDevice->wasapi.deviceBufferFramesRemainingCapture == 0) {
|
|
||||||
hr = mal_IAudioCaptureClient_ReleaseBuffer((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.deviceBufferFramesCapacityCapture);
|
|
||||||
pDevice->wasapi.pDeviceBufferCapture = NULL;
|
|
||||||
pDevice->wasapi.deviceBufferFramesRemainingCapture = 0;
|
|
||||||
pDevice->wasapi.deviceBufferFramesCapacityCapture = 0;
|
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
result = MAL_FAILED_TO_UNMAP_DEVICE_BUFFER;
|
|
||||||
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
If we're running in full-duplex mode and there's too much data in the buffer we need to discard some to ensure we don't get stuck stradling the
|
|
||||||
edge of the buffer and causing endless glitching.
|
|
||||||
*/
|
|
||||||
if (pDevice->type == mal_device_type_duplex) {
|
|
||||||
mal_uint32 framesAvailable;
|
|
||||||
result = mal_device__get_available_frames__wasapi(pDevice, (mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailable);
|
|
||||||
if (result == MAL_SUCCESS) {
|
|
||||||
if (framesAvailable > (pDevice->wasapi.periodSizeInFramesCapture*(pDevice->capture.internalPeriods-1))) {
|
|
||||||
mal_uint32 framesToDiscard = framesAvailable - pDevice->wasapi.periodSizeInFramesCapture;
|
|
||||||
if (framesToDiscard > 0) {
|
|
||||||
BYTE* pUnused;
|
|
||||||
hr = mal_IAudioCaptureClient_GetBuffer((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, &pUnused, &framesToDiscard, &flags, NULL, NULL);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
#ifdef MAL_DEBUG_OUTPUT
|
|
||||||
printf("[WASAPI] (Duplex/Capture) Discarding %d frames...\n", framesToDiscard);
|
|
||||||
#endif
|
|
||||||
mal_IAudioCaptureClient_ReleaseBuffer((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, framesToDiscard);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetEvent(pDevice->wasapi.hEventCapture);
|
|
||||||
}
|
|
||||||
|
|
||||||
mal_assert(totalFramesRead <= frameCount);
|
|
||||||
if (totalFramesRead == frameCount) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait for data. */
|
|
||||||
if (pDevice->type == mal_device_type_capture) {
|
|
||||||
waitResult = WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE);
|
|
||||||
} else {
|
|
||||||
if (pDevice->playback.shareMode == mal_share_mode_shared) {
|
|
||||||
waitResult = WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE);
|
|
||||||
} else {
|
|
||||||
waitResult = WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE); /* Wait on the exclusive-mode playback event instead. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (waitResult == WAIT_FAILED) {
|
|
||||||
result = MAL_ERROR;
|
|
||||||
break; /* An error occurred while waiting for the event. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the device has been stopped don't continue. */
|
|
||||||
if (!pDevice->wasapi.isStarted) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We may need to reroute the device. */
|
|
||||||
if (mal_device_is_reroute_required__wasapi(pDevice, mal_device_type_capture)) {
|
|
||||||
result = mal_device_reroute__wasapi(pDevice, mal_device_type_capture);
|
|
||||||
if (result != MAL_SUCCESS) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The device buffer has become available, so now we need to get a pointer to it. */
|
|
||||||
result = mal_device__get_available_frames__wasapi(pDevice, (mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.deviceBufferFramesCapacityCapture);
|
|
||||||
if (result != MAL_SUCCESS) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//printf("TRACE 1: capacity capture: %d, %d\n", pDevice->wasapi.deviceBufferFramesCapacityCapture, pDevice->wasapi.periodSizeInFramesCapture);
|
|
||||||
|
|
||||||
/* In exclusive mode, the frame count needs to exactly match the value returned by GetCurrentPadding(). */
|
|
||||||
if (pDevice->playback.shareMode != mal_share_mode_exclusive) {
|
|
||||||
if (pDevice->wasapi.deviceBufferFramesCapacityCapture > pDevice->wasapi.periodSizeInFramesCapture) {
|
|
||||||
pDevice->wasapi.deviceBufferFramesCapacityCapture = pDevice->wasapi.periodSizeInFramesCapture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = mal_IAudioCaptureClient_GetBuffer((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pDeviceBufferCapture, &pDevice->wasapi.deviceBufferFramesCapacityCapture, &flags, NULL, NULL);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
result = MAL_FAILED_TO_MAP_DEVICE_BUFFER;
|
|
||||||
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
pDevice->wasapi.deviceBufferFramesRemainingCapture = pDevice->wasapi.deviceBufferFramesCapacityCapture;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
||||||
{
|
{
|
||||||
mal_result result;
|
mal_result result;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
mal_bool32 isPlaybackDeviceStarted = MAL_FALSE;
|
|
||||||
mal_bool32 exitLoop = MAL_FALSE;
|
mal_bool32 exitLoop = MAL_FALSE;
|
||||||
mal_uint32 framesWrittenToPlaybackDevice = 0;
|
mal_uint32 framesWrittenToPlaybackDevice = 0;
|
||||||
mal_uint32 mappedBufferSizeInFramesCapture = 0;
|
mal_uint32 mappedBufferSizeInFramesCapture = 0;
|
||||||
@@ -8087,9 +7717,26 @@ mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
|||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
||||||
}
|
}
|
||||||
|
mal_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MAL_TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (mal_device__get_state(pDevice) == MAL_STATE_STARTED && !exitLoop) {
|
while (mal_device__get_state(pDevice) == MAL_STATE_STARTED && !exitLoop) {
|
||||||
|
/* We may need to reroute the device. */
|
||||||
|
if (mal_device_is_reroute_required__wasapi(pDevice, mal_device_type_playback)) {
|
||||||
|
result = mal_device_reroute__wasapi(pDevice, mal_device_type_playback);
|
||||||
|
if (result != MAL_SUCCESS) {
|
||||||
|
exitLoop = MAL_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mal_device_is_reroute_required__wasapi(pDevice, mal_device_type_capture)) {
|
||||||
|
result = mal_device_reroute__wasapi(pDevice, mal_device_type_capture);
|
||||||
|
if (result != MAL_SUCCESS) {
|
||||||
|
exitLoop = MAL_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (pDevice->type)
|
switch (pDevice->type)
|
||||||
{
|
{
|
||||||
case mal_device_type_duplex:
|
case mal_device_type_duplex:
|
||||||
@@ -8098,22 +7745,6 @@ mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
|||||||
mal_uint32 framesAvailablePlayback;
|
mal_uint32 framesAvailablePlayback;
|
||||||
DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
|
DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
|
||||||
|
|
||||||
/* We may need to reroute the device. */
|
|
||||||
if (mal_device_is_reroute_required__wasapi(pDevice, mal_device_type_playback)) {
|
|
||||||
result = mal_device_reroute__wasapi(pDevice, mal_device_type_playback);
|
|
||||||
if (result != MAL_SUCCESS) {
|
|
||||||
exitLoop = MAL_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mal_device_is_reroute_required__wasapi(pDevice, mal_device_type_capture)) {
|
|
||||||
result = mal_device_reroute__wasapi(pDevice, mal_device_type_capture);
|
|
||||||
if (result != MAL_SUCCESS) {
|
|
||||||
exitLoop = MAL_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The process is to map the playback buffer and fill it as quickly as possible from input data. */
|
/* The process is to map the playback buffer and fill it as quickly as possible from input data. */
|
||||||
if (pMappedBufferPlayback == NULL) {
|
if (pMappedBufferPlayback == NULL) {
|
||||||
/* WASAPI is weird with exclusive mode. You need to wait on the event _before_ querying the available frames. */
|
/* WASAPI is weird with exclusive mode. You need to wait on the event _before_ querying the available frames. */
|
||||||
@@ -8354,7 +7985,7 @@ mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
|||||||
mappedBufferSizeInFramesPlayback = 0;
|
mappedBufferSizeInFramesPlayback = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPlaybackDeviceStarted) {
|
if (!pDevice->wasapi.isStartedPlayback) {
|
||||||
if (pDevice->playback.shareMode == mal_share_mode_exclusive || framesWrittenToPlaybackDevice >= (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*2) {
|
if (pDevice->playback.shareMode == mal_share_mode_exclusive || framesWrittenToPlaybackDevice >= (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*2) {
|
||||||
hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
@@ -8362,7 +7993,7 @@ mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
|||||||
mal_IAudioClient_Reset((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
mal_IAudioClient_Reset((mal_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", MAL_FAILED_TO_START_BACKEND_DEVICE);
|
||||||
}
|
}
|
||||||
isPlaybackDeviceStarted = MAL_TRUE;
|
mal_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MAL_TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -8467,7 +8098,7 @@ mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
|||||||
}
|
}
|
||||||
|
|
||||||
framesWrittenToPlaybackDevice += framesAvailablePlayback;
|
framesWrittenToPlaybackDevice += framesAvailablePlayback;
|
||||||
if (!isPlaybackDeviceStarted) {
|
if (!pDevice->wasapi.isStartedPlayback) {
|
||||||
if (pDevice->playback.shareMode == mal_share_mode_exclusive || framesWrittenToPlaybackDevice >= (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*1) {
|
if (pDevice->playback.shareMode == mal_share_mode_exclusive || framesWrittenToPlaybackDevice >= (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*1) {
|
||||||
hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
@@ -8475,7 +8106,7 @@ mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
|||||||
exitLoop = MAL_TRUE;
|
exitLoop = MAL_TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
isPlaybackDeviceStarted = MAL_TRUE;
|
mal_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MAL_TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -8513,7 +8144,7 @@ mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
|||||||
The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
|
The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
|
||||||
the speakers. This is a problem for very short sounds because it'll result in a significant potion of it not getting played.
|
the speakers. This is a problem for very short sounds because it'll result in a significant potion of it not getting played.
|
||||||
*/
|
*/
|
||||||
if (isPlaybackDeviceStarted) {
|
if (pDevice->wasapi.isStartedPlayback) {
|
||||||
if (pDevice->playback.shareMode == mal_share_mode_exclusive) {
|
if (pDevice->playback.shareMode == mal_share_mode_exclusive) {
|
||||||
WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
|
WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user