diff options
-rw-r--r-- | Alc/winmm.c | 362 |
1 files changed, 343 insertions, 19 deletions
diff --git a/Alc/winmm.c b/Alc/winmm.c index 54101058..8688b7b0 100644 --- a/Alc/winmm.c +++ b/Alc/winmm.c @@ -36,24 +36,71 @@ typedef struct { // MMSYSTEM Device volatile ALboolean bWaveShutdown; - HANDLE hWaveHdrEvent; - HANDLE hWaveThreadEvent; - HANDLE hWaveThread; - DWORD ulWaveThreadID; - ALint lWaveBuffersCommitted; - WAVEHDR WaveBuffer[4]; + HANDLE hWaveHdrEvent; + HANDLE hWaveThreadEvent; + HANDLE hWaveThread; + DWORD ulWaveThreadID; + LONG lWaveBuffersCommitted; + WAVEHDR WaveBuffer[4]; union { HWAVEIN In; + HWAVEOUT Out; } hWaveHandle; - RingBuffer *pRing; + ALsizei Frequency; + + RingBuffer *pRing; } WinMMData; +static const ALCchar woDefault[] = "WaveOut Default"; + +static ALCchar **PlaybackDeviceList; +static ALuint NumPlaybackDevices; static ALCchar **CaptureDeviceList; static ALuint NumCaptureDevices; + +static void ProbePlaybackDevices(void) +{ + ALuint i; + + for(i = 0;i < NumPlaybackDevices;i++) + free(PlaybackDeviceList[i]); + + NumPlaybackDevices = waveOutGetNumDevs(); + PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices); + for(i = 0;i < NumPlaybackDevices;i++) + { + WAVEOUTCAPS WaveCaps; + + PlaybackDeviceList[i] = NULL; + if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) + { + char name[1024]; + ALuint count, j; + + count = 0; + do { + if(count == 0) + snprintf(name, sizeof(name), "%s via WaveOut", WaveCaps.szPname); + else + snprintf(name, sizeof(name), "%s #%d via WaveOut", WaveCaps.szPname, count+1); + count++; + + for(j = 0;j < i;j++) + { + if(strcmp(name, PlaybackDeviceList[j]) == 0) + break; + } + } while(j != i); + + PlaybackDeviceList[i] = strdup(name); + } + } +} + static void ProbeCaptureDevices(void) { ALuint i; @@ -95,6 +142,84 @@ static void ProbeCaptureDevices(void) /* + WaveOutProc + + Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and + returns to the application (for more data) +*/ +static void CALLBACK WaveOutProc(HWAVEOUT hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) +{ + ALCdevice *pDevice = (ALCdevice*)dwInstance; + WinMMData *pData = pDevice->ExtraData; + + (void)hDevice; + (void)dwParam2; + + if(uMsg != WOM_DONE) + return; + + // Decrement number of buffers in use + InterlockedDecrement(&pData->lWaveBuffersCommitted); + + if(pData->bWaveShutdown == AL_FALSE) + { + // Notify Wave Processor Thread that a Wave Header has returned + PostThreadMessage(pData->ulWaveThreadID, uMsg, 0, dwParam1); + } + else + { + if(pData->lWaveBuffersCommitted == 0) + { + // Signal Wave Buffers Returned event + if(pData->hWaveHdrEvent) + SetEvent(pData->hWaveHdrEvent); + + // Post 'Quit' Message to WaveOut Processor Thread + PostThreadMessage(pData->ulWaveThreadID, WM_QUIT, 0, 0); + } + } +} + +/* + PlaybackThreadProc + + Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its + audio data. +*/ +static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter) +{ + ALCdevice *pDevice = (ALCdevice*)lpParameter; + WinMMData *pData = pDevice->ExtraData; + LPWAVEHDR pWaveHdr; + ALuint FrameSize; + MSG msg; + + FrameSize = aluFrameSizeFromFormat(pDevice->Format); + + while(GetMessage(&msg, NULL, 0, 0)) + { + if(msg.message != WOM_DONE || pData->bWaveShutdown) + continue; + + pWaveHdr = ((LPWAVEHDR)msg.lParam); + + aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize); + + // Send buffer back to play more data + waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + // Signal Wave Thread completed event + if(pData->hWaveThreadEvent) + SetEvent(pData->hWaveThreadEvent); + + ExitThread(0); + + return 0; +} + +/* WaveInProc Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and @@ -112,7 +237,7 @@ static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,D return; // Decrement number of buffers in use - pData->lWaveBuffersCommitted--; + InterlockedDecrement(&pData->lWaveBuffersCommitted); if(pData->bWaveShutdown == AL_FALSE) { @@ -161,7 +286,7 @@ static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter) // Send buffer back to capture more data waveInAddBuffer(pData->hWaveHandle.In,pWaveHdr,sizeof(WAVEHDR)); - pData->lWaveBuffersCommitted++; + InterlockedIncrement(&pData->lWaveBuffersCommitted); } // Signal Wave Thread completed event @@ -174,18 +299,191 @@ static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter) } -static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +static ALCboolean WinMMOpenPlayback(ALCdevice *pDevice, const ALCchar *deviceName) { - (void)device; - (void)deviceName; + WAVEFORMATEX wfexFormat; + WinMMData *pData = NULL; + UINT_PTR lDeviceID = 0; + MMRESULT res; + ALuint i = 0; + + // Find the Device ID matching the deviceName if valid + if(!deviceName || strcmp(deviceName, woDefault) == 0) + lDeviceID = WAVE_MAPPER; + else + { + if(!PlaybackDeviceList) + ProbePlaybackDevices(); + + for(i = 0;i < NumPlaybackDevices;i++) + { + if(PlaybackDeviceList[i] && + strcmp(deviceName, PlaybackDeviceList[i]) == 0) + { + lDeviceID = i; + break; + } + } + if(i == NumPlaybackDevices) + return ALC_FALSE; + } + + pData = calloc(1, sizeof(*pData)); + if(!pData) + { + alcSetError(pDevice, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + pDevice->ExtraData = pData; + + if(aluChannelsFromFormat(pDevice->Format) >= 2) + { + if(aluBytesFromFormat(pDevice->Format) >= 2) + pDevice->Format = AL_FORMAT_STEREO16; + else + pDevice->Format = AL_FORMAT_STEREO8; + } + else + { + if(aluBytesFromFormat(pDevice->Format) >= 2) + pDevice->Format = AL_FORMAT_MONO16; + else + pDevice->Format = AL_FORMAT_MONO8; + } + + memset(&wfexFormat, 0, sizeof(WAVEFORMATEX)); + wfexFormat.wFormatTag = WAVE_FORMAT_PCM; + wfexFormat.nChannels = aluChannelsFromFormat(pDevice->Format); + wfexFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8; + wfexFormat.nBlockAlign = wfexFormat.wBitsPerSample * + wfexFormat.nChannels / 8; + wfexFormat.nSamplesPerSec = pDevice->Frequency; + wfexFormat.nAvgBytesPerSec = wfexFormat.nSamplesPerSec * + wfexFormat.nBlockAlign; + wfexFormat.cbSize = 0; + + if((res=waveOutOpen(&pData->hWaveHandle.Out, lDeviceID, &wfexFormat, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) + { + AL_PRINT("waveInOpen failed: %u\n", res); + goto failure; + } + + pData->hWaveHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveOutAllHeadersReturned"); + pData->hWaveThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveOutThreadDestroyed"); + if(pData->hWaveHdrEvent == NULL || pData->hWaveThreadEvent == NULL) + { + AL_PRINT("CreateEvent failed: %lu\n", GetLastError()); + goto failure; + } + + pData->Frequency = pDevice->Frequency; + + pDevice->szDeviceName = strdup((lDeviceID==WAVE_MAPPER) ? woDefault : + PlaybackDeviceList[lDeviceID]); + return ALC_TRUE; + +failure: + if(pData->hWaveThreadEvent) + CloseHandle(pData->hWaveThreadEvent); + if(pData->hWaveHdrEvent) + CloseHandle(pData->hWaveHdrEvent); + + if(pData->hWaveHandle.Out) + waveOutClose(pData->hWaveHandle.Out); + + free(pData); + pDevice->ExtraData = NULL; return ALC_FALSE; } static void WinMMClosePlayback(ALCdevice *device) { - (void)device; + WinMMData *pData = (WinMMData*)device->ExtraData; + + // Close the Wave device + CloseHandle(pData->hWaveThreadEvent); + pData->hWaveThreadEvent = 0; + + CloseHandle(pData->hWaveHdrEvent); + pData->hWaveHdrEvent = 0; + + waveInClose(pData->hWaveHandle.In); + pData->hWaveHandle.In = 0; + + free(pData); + device->ExtraData = NULL; } +static ALCboolean WinMMResetPlayback(ALCdevice *device) +{ + WinMMData *pData = (WinMMData*)device->ExtraData; + ALbyte *BufferData; + ALint lBufferSize; + ALuint i; + + pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &pData->ulWaveThreadID); + if(pData->hWaveThread == NULL) + return ALC_FALSE; + + device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * + pData->Frequency / device->Frequency); + device->Frequency = pData->Frequency; + + pData->lWaveBuffersCommitted = 0; + + // Create 4 Buffers + lBufferSize = device->UpdateSize*device->NumUpdates / 4; + lBufferSize *= aluFrameSizeFromFormat(device->Format); + + BufferData = calloc(4, lBufferSize); + for(i = 0;i < 4;i++) + { + memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR)); + pData->WaveBuffer[i].dwBufferLength = lBufferSize; + pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData : + (pData->WaveBuffer[i-1].lpData + + pData->WaveBuffer[i-1].dwBufferLength)); + waveOutPrepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + waveOutWrite(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + InterlockedIncrement(&pData->lWaveBuffersCommitted); + } + + return ALC_TRUE; +} + +static void WinMMStopPlayback(ALCdevice *device) +{ + WinMMData *pData = (WinMMData*)device->ExtraData; + int i; + + if(pData->hWaveThread == NULL) + return; + + // Set flag to stop processing headers + pData->bWaveShutdown = AL_TRUE; + + // Wait for signal that all Wave Buffers have returned + WaitForSingleObjectEx(pData->hWaveHdrEvent, 5000, FALSE); + + // Wait for signal that Wave Thread has been destroyed + WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE); + + CloseHandle(pData->hWaveThread); + pData->hWaveThread = 0; + + pData->bWaveShutdown = AL_FALSE; + + // Release the wave buffers + for(i = 0;i < 4;i++) + { + waveOutUnprepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) + free(pData->WaveBuffer[i].lpData); + pData->WaveBuffer[i].lpData = NULL; + } +} + + static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName) { WAVEFORMATEX wfexCaptureFormat; @@ -233,6 +531,7 @@ static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName alcSetError(pDevice, ALC_OUT_OF_MEMORY); return ALC_FALSE; } + pDevice->ExtraData = pData; memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX)); wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM; @@ -259,6 +558,8 @@ static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName goto failure; } + pData->Frequency = pDevice->Frequency; + // Allocate circular memory buffer for the captured audio ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates; @@ -291,11 +592,9 @@ static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName pData->WaveBuffer[i].dwLoops = 0; waveInPrepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); waveInAddBuffer(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR)); - pData->lWaveBuffersCommitted++; + InterlockedIncrement(&pData->lWaveBuffersCommitted); } - pDevice->ExtraData = pData; - pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveThreadID); if (pData->hWaveThread == NULL) goto failure; @@ -409,8 +708,8 @@ static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lS static BackendFuncs WinMMFuncs = { WinMMOpenPlayback, WinMMClosePlayback, - NULL, - NULL, + WinMMResetPlayback, + WinMMStopPlayback, WinMMOpenCapture, WinMMCloseCapture, WinMMStartCapture, @@ -428,6 +727,14 @@ void alcWinMMDeinit() { ALuint lLoop; + for(lLoop = 0;lLoop < NumPlaybackDevices;lLoop++) + free(PlaybackDeviceList[lLoop]); + free(PlaybackDeviceList); + PlaybackDeviceList = NULL; + + NumPlaybackDevices = 0; + + for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++) free(CaptureDeviceList[lLoop]); free(CaptureDeviceList); @@ -440,7 +747,24 @@ void alcWinMMProbe(int type) { ALuint i; - if(type == CAPTURE_DEVICE_PROBE) + if(type == DEVICE_PROBE) + { + ProbePlaybackDevices(); + if(NumPlaybackDevices > 0) + AppendDeviceList(woDefault); + } + else if(type == ALL_DEVICE_PROBE) + { + ProbePlaybackDevices(); + if(NumPlaybackDevices > 0) + AppendAllDeviceList(woDefault); + for(i = 0;i < NumPlaybackDevices;i++) + { + if(PlaybackDeviceList[i]) + AppendAllDeviceList(PlaybackDeviceList[i]); + } + } + else if(type == CAPTURE_DEVICE_PROBE) { ProbeCaptureDevices(); for(i = 0;i < NumCaptureDevices;i++) |