Commit f960c12a authored by David Reid's avatar David Reid

WinMM: Work on the blocking write API.

parent e30bcb13
...@@ -1952,7 +1952,7 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device ...@@ -1952,7 +1952,7 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
mal_uint32 deviceBufferFramesCapacityPlayback; mal_uint32 deviceBufferFramesCapacityPlayback;
mal_uint32 deviceBufferFramesCapacityCapture; mal_uint32 deviceBufferFramesCapacityCapture;
mal_bool32 isStarted; mal_bool32 isStarted;
mal_bool32 breakFromMainLoop; mal_bool32 breakFromMainLoop; /* TODO: Delete me once the new main loop is finialized. */
mal_bool32 hasDefaultDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ mal_bool32 hasDefaultDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
} wasapi; } wasapi;
#endif #endif
...@@ -1978,11 +1978,14 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device ...@@ -1978,11 +1978,14 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
/*HANDLE*/ mal_handle hEvent; /*HANDLE*/ mal_handle hEvent;
mal_uint32 fragmentSizeInFrames; mal_uint32 fragmentSizeInFrames;
mal_uint32 fragmentSizeInBytes; mal_uint32 fragmentSizeInBytes;
mal_uint32 iNextHeader; // [0,periods). Used as an index into pWAVEHDR. mal_uint32 iNextHeader; /* [0,periods). Used as an index into pWAVEHDR. */
/*WAVEHDR**/ mal_uint8* pWAVEHDR; // One instantiation for each period. mal_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
mal_uint32 headerFramesConsumedCapture; /* ^^^ */
/*WAVEHDR**/ mal_uint8* pWAVEHDR; /* One instantiation for each period. */
mal_uint8* pIntermediaryBuffer; mal_uint8* pIntermediaryBuffer;
mal_uint8* _pHeapData; // Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. mal_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
mal_bool32 breakFromMainLoop; mal_bool32 isStarted;
mal_bool32 breakFromMainLoop; /* TODO: Delete me once the new main loop is finialized. */
} winmm; } winmm;
#endif #endif
#ifdef MAL_SUPPORT_ALSA #ifdef MAL_SUPPORT_ALSA
...@@ -9207,6 +9210,8 @@ void mal_device_uninit__winmm(mal_device* pDevice) ...@@ -9207,6 +9210,8 @@ void mal_device_uninit__winmm(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
((MAL_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset);
if (pDevice->type == mal_device_type_playback) { if (pDevice->type == mal_device_type_playback) {
((MAL_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevice); ((MAL_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevice);
} else { } else {
...@@ -9373,7 +9378,7 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, c ...@@ -9373,7 +9378,7 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, c
// The size of the intermediary buffer needs to be able to fit every fragment. // The size of the intermediary buffer needs to be able to fit every fragment.
pDevice->winmm.fragmentSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; pDevice->winmm.fragmentSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods;
pDevice->winmm.fragmentSizeInBytes = pDevice->winmm.fragmentSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); pDevice->winmm.fragmentSizeInBytes = pDevice->winmm.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
heapSize = (sizeof(WAVEHDR) * pDevice->periods) + (pDevice->winmm.fragmentSizeInBytes * pDevice->periods); heapSize = (sizeof(WAVEHDR) * pDevice->periods) + (pDevice->winmm.fragmentSizeInBytes * pDevice->periods);
pDevice->winmm._pHeapData = (mal_uint8*)mal_malloc(heapSize); pDevice->winmm._pHeapData = (mal_uint8*)mal_malloc(heapSize);
...@@ -9388,9 +9393,39 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, c ...@@ -9388,9 +9393,39 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, c
pDevice->winmm.pIntermediaryBuffer = pDevice->winmm._pHeapData + (sizeof(WAVEHDR) * pDevice->periods); pDevice->winmm.pIntermediaryBuffer = pDevice->winmm._pHeapData + (sizeof(WAVEHDR) * pDevice->periods);
/* Prepare headers. */
for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; ++iPeriod) {
((WAVEHDR*)pDevice->winmm.pWAVEHDR)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBuffer + (pDevice->winmm.fragmentSizeInBytes * iPeriod));
((WAVEHDR*)pDevice->winmm.pWAVEHDR)[iPeriod].dwBufferLength = pDevice->winmm.fragmentSizeInBytes;
((WAVEHDR*)pDevice->winmm.pWAVEHDR)[iPeriod].dwFlags = 0L;
((WAVEHDR*)pDevice->winmm.pWAVEHDR)[iPeriod].dwLoops = 0L;
if (pDevice->type == mal_device_type_playback) {
((MAL_PFN_waveOutPrepareHeader)pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((WAVEHDR*)pDevice->winmm.pWAVEHDR)[iPeriod], sizeof(WAVEHDR));
} else {
((MAL_PFN_waveInPrepareHeader)pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((WAVEHDR*)pDevice->winmm.pWAVEHDR)[iPeriod], sizeof(WAVEHDR));
}
/*
The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means
it's unlocked and available for writing. A value of 1 means it's locked.
*/
((WAVEHDR*)pDevice->winmm.pWAVEHDR)[iPeriod].dwUser = 0;
}
return MAL_SUCCESS; return MAL_SUCCESS;
on_error: on_error:
if (pDevice->winmm.pWAVEHDR != NULL) {
for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; ++iPeriod) {
if (pDevice->type == mal_device_type_playback) {
((MAL_PFN_waveOutUnprepareHeader)pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((WAVEHDR*)pDevice->winmm.pWAVEHDR)[iPeriod], sizeof(WAVEHDR));
} else {
((MAL_PFN_waveInUnprepareHeader)pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((WAVEHDR*)pDevice->winmm.pWAVEHDR)[iPeriod], sizeof(WAVEHDR));
}
}
}
if (pDevice->type == mal_device_type_playback) { if (pDevice->type == mal_device_type_playback) {
((MAL_PFN_waveOutClose)pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevice); ((MAL_PFN_waveOutClose)pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevice);
} else { } else {
...@@ -9467,6 +9502,8 @@ mal_result mal_device_start__winmm(mal_device* pDevice) ...@@ -9467,6 +9502,8 @@ mal_result mal_device_start__winmm(mal_device* pDevice)
} }
pDevice->winmm.iNextHeader = 0; pDevice->winmm.iNextHeader = 0;
mal_atomic_exchange_32(&pDevice->winmm.isStarted, MAL_TRUE);
return MAL_SUCCESS; return MAL_SUCCESS;
} }
...@@ -9506,9 +9543,108 @@ mal_result mal_device_stop__winmm(mal_device* pDevice) ...@@ -9506,9 +9543,108 @@ mal_result mal_device_stop__winmm(mal_device* pDevice)
} }
} }
mal_atomic_exchange_32(&pDevice->winmm.isStarted, MAL_FALSE);
return MAL_SUCCESS;
}
mal_result mal_device_write__winmm(mal_device* pDevice, mal_uint32 pcmFrameCount, const void* pPCMFrames, mal_uint32* pPCMFramesWritten)
{
mal_assert(pDevice != NULL);
mal_assert(pPCMFrames != NULL);
mal_result result;
MMRESULT resultMM;
mal_uint32 totalPCMFramesWritten;
WAVEHDR* pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDR;
*pPCMFramesWritten = 0;
/* Keep processing as much data as possible. */
totalPCMFramesWritten = 0;
while (totalPCMFramesWritten < pcmFrameCount) {
/* If the current header has some space available we need to write part of it. */
if (pWAVEHDR[pDevice->winmm.iNextHeader].dwUser == 0) { /* 0 = unlocked. */
/*
This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to
write it out and move on to the next iteration.
*/
mal_uint32 bpf = mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
mal_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeader].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
mal_uint32 framesToCopy = mal_min(framesRemainingInHeader, (pcmFrameCount - totalPCMFramesWritten));
const void* pSrc = mal_offset_ptr(pPCMFrames, totalPCMFramesWritten*bpf);
void* pDst = mal_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeader].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
mal_copy_memory(pDst, pSrc, framesToCopy*bpf);
pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
totalPCMFramesWritten += framesToCopy;
/* If we've consumed the buffer entirely we need to write it out to the device. */
if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeader].dwBufferLength/bpf)) {
pWAVEHDR[pDevice->winmm.iNextHeader].dwUser = 1; /* 1 = locked. */
pWAVEHDR[pDevice->winmm.iNextHeader].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
/* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
ResetEvent((HANDLE)pDevice->winmm.hEvent);
/* The device will be started here. */
resultMM = ((MAL_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevice, &pWAVEHDR[pDevice->winmm.iNextHeader], sizeof(WAVEHDR));
if (resultMM != MMSYSERR_NOERROR) {
result = mal_result_from_MMRESULT(resultMM);
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.", result);
break;
}
/* Make sure we move to the next header. */
pDevice->winmm.iNextHeader = (pDevice->winmm.iNextHeader + 1) % pDevice->periods;
pDevice->winmm.headerFramesConsumedPlayback = 0;
}
/* If at this point we have consumed the entire input buffer we can return. */
mal_assert(totalPCMFramesWritten <= pcmFrameCount);
if (totalPCMFramesWritten == pcmFrameCount) {
break;
}
/* Getting here means there's more to process. */
continue;
}
/* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
if (WaitForSingleObject((HANDLE)pDevice->winmm.hEvent, INFINITE) != WAIT_OBJECT_0) {
result = MAL_ERROR;
break;
}
/* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
if ((pWAVEHDR[pDevice->winmm.iNextHeader].dwFlags & WHDR_DONE) != 0) {
pWAVEHDR[pDevice->winmm.iNextHeader].dwUser = 0; /* 0 = unlocked (make it available for writing). */
pDevice->winmm.headerFramesConsumedPlayback = 0;
}
/* If the device has been stopped we need to break. */
if (!pDevice->winmm.isStarted) {
break;
}
}
*pPCMFramesWritten = totalPCMFramesWritten;
return MAL_SUCCESS; return MAL_SUCCESS;
} }
mal_result mal_device_read__winmm(mal_device* pDevice, mal_uint32 pcmFrameCount, void* pPCMFrames, mal_uint32* pPCMFramesRead)
{
mal_assert(pDevice != NULL);
mal_assert(pPCMFrames != NULL);
(void)pDevice;
(void)pcmFrameCount;
(void)pPCMFrames;
(void)pPCMFramesRead;
return MAL_INVALID_OPERATION;
}
mal_result mal_device_break_main_loop__winmm(mal_device* pDevice) mal_result mal_device_break_main_loop__winmm(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
...@@ -9673,6 +9809,9 @@ mal_result mal_context_init__winmm(mal_context* pContext) ...@@ -9673,6 +9809,9 @@ mal_result mal_context_init__winmm(mal_context* pContext)
pContext->onDeviceUninit = mal_device_uninit__winmm; pContext->onDeviceUninit = mal_device_uninit__winmm;
pContext->onDeviceStart = mal_device_start__winmm; pContext->onDeviceStart = mal_device_start__winmm;
pContext->onDeviceStop = mal_device_stop__winmm; pContext->onDeviceStop = mal_device_stop__winmm;
pContext->onDeviceWrite = mal_device_write__winmm;
pContext->onDeviceRead = mal_device_read__winmm;
pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__winmm; pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__winmm;
pContext->onDeviceMainLoop = mal_device_main_loop__winmm; pContext->onDeviceMainLoop = mal_device_main_loop__winmm;
...@@ -20870,7 +21009,29 @@ mal_result mal_device_write(mal_device* pDevice, mal_uint32 pcmFrameCount, const ...@@ -20870,7 +21009,29 @@ mal_result mal_device_write(mal_device* pDevice, mal_uint32 pcmFrameCount, const
/* Slow path. Perform a data conversion. */ /* Slow path. Perform a data conversion. */
/* TODO: Implement me. */ /* TODO: Implement me. */
result = MAL_INVALID_OPERATION; result = MAL_SUCCESS;
/* NOTE: Only doing format conversion for now just while testing. */
mal_uint8 buffer[4096];
mal_uint32 bufferSizeInFrames = sizeof(buffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
while (totalPCMFramesWritten < pcmFrameCount) {
mal_uint32 framesRemaining = (pcmFrameCount - totalPCMFramesWritten);
mal_uint32 framesToProcess = framesRemaining;
if (framesToProcess > bufferSizeInFrames) {
framesToProcess = bufferSizeInFrames;
}
mal_pcm_convert(buffer, pDevice->internalFormat, mal_offset_ptr(pPCMFrames, totalPCMFramesWritten * mal_get_bytes_per_frame(pDevice->format, pDevice->channels)), pDevice->format, framesToProcess*pDevice->channels, mal_dither_mode_none);
mal_uint32 framesProcessed = 0;
result = pDevice->pContext->onDeviceWrite(pDevice, framesToProcess, buffer, &framesProcessed);
totalPCMFramesWritten += framesProcessed;
if (result != MAL_SUCCESS) {
break;
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment