if (FAILED(mal_IDirectSoundCaptureBuffer_GetCurrentPosition((mal_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, NULL, &dwCurrentPosition))) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", MAL_FAILED_TO_MAP_DEVICE_BUFFER);
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", result);
break;
}
if (!pDevice->dsound.isStarted && !wasStartedOnEntry) {
result = mal_device_start__dsound(pDevice);
if (result != MAL_SUCCESS) {
break;
}
}
}
mal_assert(totalFramesWritten <= frameCount);
if (totalFramesWritten == frameCount) {
break;
}
/*
Relying exclusively on the notification events turns out to be horribly slow. It looks like by the time the event is signaled, the cursor
has already progressed way beyond the notification point. It's better to instead poll the position, only returning when a whole period is
available.
This loop just crudely sleeps for a bit and then queries the current position. If there's enough room for a whole period, we will fill it
with data from the client. Otherwise we just wait a bit longer. It's crude, but it's simple and it works _much_ better than the notification
system.
*/
for (;;) {
DWORD timeoutInMilliseconds = 1;
mal_sleep((mal_uint32)timeoutInMilliseconds);
/* We've woken up, so now we need to poll the current position. If there are enough frames for a whole period we can be done with the wait. */
mal_uint32 currentPos;
result = mal_device_get_current_frame__dsound(pDevice, mal_device_type_playback, ¤tPos);
if ((currentPos - (pDevice->dsound.iNextPeriodPlayback * periodSizeInFrames)) >= periodSizeInFrames) {
break; /* There's enough room. */
}
/* Don't keep waiting if the device has stopped. */
if (!pDevice->dsound.isStarted && wasStartedOnEntry) {
break;
}
}
if (result != MAL_SUCCESS) {
break;
}
/* If the device has been stopped don't continue. */
if (!pDevice->dsound.isStarted && wasStartedOnEntry) {
break;
}
/* Getting here means we can map the next period. */
result = mal_device_map_next_playback_buffer__dsound(pDevice);
if (result != MAL_SUCCESS) {
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", result);
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", MAL_FAILED_TO_MAP_DEVICE_BUFFER);
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", result);
break;
}
}
mal_assert(totalFramesRead <= frameCount);
if (totalFramesRead == frameCount) {
break;
}
/* Just like in the playback case we just use a simple wait-and-poll to know when a chunk of data is available. */
for (;;) {
DWORD timeoutInMilliseconds = 1;
mal_sleep((mal_uint32)timeoutInMilliseconds);
/* We've woken up, so now we need to poll the current position. If there are enough frames for a whole period we can be done with the wait. */
mal_uint32 currentPos;
result = mal_device_get_current_frame__dsound(pDevice, mal_device_type_capture, ¤tPos);
if ((currentPos - (pDevice->dsound.iNextPeriodCapture * periodSizeInFrames)) >= periodSizeInFrames) {
break; /* There's enough room. */
}
/* Don't keep waiting if the device has stopped. */
if (!pDevice->dsound.isStarted) {
break;
}
}
if (result != MAL_SUCCESS) {
break;
}
/* Don't keep waiting if the device has stopped. */
if (!pDevice->dsound.isStarted) {
break;
}
result = mal_device_map_next_capture_buffer__dsound(pDevice);
if (result != MAL_SUCCESS) {
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", result);