diff options
author | Chris Robinson <[email protected]> | 2007-11-13 18:02:18 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2007-11-13 18:02:18 -0800 |
commit | ae5f4e9a742b07e004b330c04a72fac4457c9b58 (patch) | |
tree | d1d5c9fadd918d9346fb871033f60e8c91600a63 /Alc/winmm.c |
Initial import
Diffstat (limited to 'Alc/winmm.c')
-rw-r--r-- | Alc/winmm.c | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/Alc/winmm.c b/Alc/winmm.c new file mode 100644 index 00000000..26f40db2 --- /dev/null +++ b/Alc/winmm.c @@ -0,0 +1,421 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> + +#include <windows.h> +#include <mmsystem.h> + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + // MMSYSTEM Capture Device + ALboolean bWaveInShutdown; + HANDLE hWaveInHdrEvent; + HANDLE hWaveInThreadEvent; + HANDLE hWaveInThread; + DWORD ulWaveInThreadID; + ALint lWaveInBuffersCommitted; + HWAVEIN hWaveInHandle; + WAVEHDR WaveInBuffer[4]; + ALCchar *pCapturedSampleData; + ALuint ulCapturedDataSize; + ALuint ulReadCapturedDataPos; + ALuint ulWriteCapturedDataPos; +} WinMMData; + + +static ALCchar *CaptureDeviceList[16]; + +/* + WaveInProc + + Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and + returns to the application (with more data) +*/ +static void CALLBACK WaveInProc(HWAVEIN 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==WIM_DATA) && (pDevice)) + { + // Decrement number of buffers in use + pData->lWaveInBuffersCommitted--; + + if (pData->bWaveInShutdown == AL_FALSE) + { + // Notify Wave Processor Thread that a Wave Header has returned + PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1); + } + else + { + if (pData->lWaveInBuffersCommitted == 0) + { + // Signal Wave Buffers Returned event + if (pData->hWaveInHdrEvent) + SetEvent(pData->hWaveInHdrEvent); + + // Post 'Quit' Message to WaveIn Processor Thread + PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0); + } + } + } +} + +/* + CaptureThreadProc + + Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new + audio data. +*/ +DWORD WINAPI CaptureThreadProc(LPVOID lpParameter) +{ + ALCdevice *pDevice = (ALCdevice*)lpParameter; + WinMMData *pData = pDevice->ExtraData; + ALuint ulOffset, ulMaxSize, ulSection; + LPWAVEHDR pWaveHdr; + MSG msg; + + while (GetMessage(&msg, NULL, 0, 0)) + { + if ((msg.message==WIM_DATA)&&(!pData->bWaveInShutdown)) + { + SuspendContext(NULL); + + pWaveHdr = ((LPWAVEHDR)msg.lParam); + + // Calculate offset in local buffer to write data to + ulOffset = pData->ulWriteCapturedDataPos % pData->ulCapturedDataSize; + + if ((ulOffset + pWaveHdr->dwBytesRecorded) > pData->ulCapturedDataSize) + { + ulSection = pData->ulCapturedDataSize - ulOffset; + memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, ulSection); + memcpy(pData->pCapturedSampleData, pWaveHdr->lpData + ulSection, pWaveHdr->dwBytesRecorded - ulSection); + } + else + { + memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded); + } + + pData->ulWriteCapturedDataPos += pWaveHdr->dwBytesRecorded; + + if (pData->ulWriteCapturedDataPos > (pData->ulReadCapturedDataPos + pData->ulCapturedDataSize)) + { + // Application has not read enough audio data from the capture buffer so data has been + // overwritten. Reset ReadPosition. + pData->ulReadCapturedDataPos = pData->ulWriteCapturedDataPos - pData->ulCapturedDataSize; + } + + // To prevent an over-flow prevent the offset values from getting too large + ulMaxSize = pData->ulCapturedDataSize << 4; + if ((pData->ulReadCapturedDataPos > ulMaxSize) && (pData->ulWriteCapturedDataPos > ulMaxSize)) + { + pData->ulReadCapturedDataPos -= ulMaxSize; + pData->ulWriteCapturedDataPos -= ulMaxSize; + } + + // Send buffer back to capture more data + waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR)); + pData->lWaveInBuffersCommitted++; + + ProcessContext(NULL); + } + } + + // Signal Wave Thread completed event + if (pData->hWaveInThreadEvent) + SetEvent(pData->hWaveInThreadEvent); + + ExitThread(0); + + return 0; +} + + +static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +{ + (void)device; + (void)deviceName; + return ALC_FALSE; +} + +static void WinMMClosePlayback(ALCdevice *device) +{ + (void)device; +} + + +static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) +{ + WAVEFORMATEX wfexCaptureFormat; + WinMMData *pData = NULL; + ALint lDeviceID = 0; + ALint lBufferSize; + ALint i; + + (void)format; + + // Find the Device ID matching the deviceName if valid + if (deviceName) + { + for(i = 0;CaptureDeviceList[i];i++) + { + if (!strcmp(deviceName, CaptureDeviceList[i])) + { + lDeviceID = i; + break; + } + } + if(!CaptureDeviceList[i]) + return ALC_FALSE; + } + + pData = calloc(1, sizeof(*pData)); + if(!pData) + { + SetALCError(ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + + memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX)); + wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM; + wfexCaptureFormat.nChannels = pDevice->Channels; + wfexCaptureFormat.wBitsPerSample = pDevice->FrameSize / pDevice->Channels * 8; + wfexCaptureFormat.nBlockAlign = pDevice->FrameSize; + wfexCaptureFormat.nSamplesPerSec = frequency; + wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec * + pDevice->FrameSize; + wfexCaptureFormat.cbSize = 0; + + if (waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) + goto failure; + + pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned"); + if (pData->hWaveInHdrEvent == NULL) + goto failure; + + pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed"); + if (pData->hWaveInThreadEvent == NULL) + goto failure; + + pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID); + if (pData->hWaveInThread == NULL) + goto failure; + + // Allocate circular memory buffer for the captured audio + pData->ulCapturedDataSize = SampleSize * wfexCaptureFormat.nBlockAlign; + + // Make sure circular buffer is at least 100ms in size (and an exact multiple of + // the block alignment + if (pData->ulCapturedDataSize < (wfexCaptureFormat.nAvgBytesPerSec / 10)) + { + pData->ulCapturedDataSize = wfexCaptureFormat.nAvgBytesPerSec / 10; + pData->ulCapturedDataSize -= (pData->ulCapturedDataSize % wfexCaptureFormat.nBlockAlign); + } + + pData->pCapturedSampleData = (ALCchar*)malloc(pData->ulCapturedDataSize); + pData->lWaveInBuffersCommitted=0; + + // Create 4 Buffers of 50ms each + lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20; + lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign); + + for (i=0;i<4;i++) + { + memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR)); + pData->WaveInBuffer[i].dwBufferLength = lBufferSize; + pData->WaveInBuffer[i].lpData = calloc(1,pData->WaveInBuffer[i].dwBufferLength); + pData->WaveInBuffer[i].dwFlags = 0; + pData->WaveInBuffer[i].dwLoops = 0; + waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR)); + waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR)); + pData->lWaveInBuffersCommitted++; + } + + pData->ulReadCapturedDataPos = 0; + pData->ulWriteCapturedDataPos = 0; + + strcpy(pDevice->szDeviceName, CaptureDeviceList[lDeviceID]); + + pDevice->ExtraData = pData; + return ALC_TRUE; + +failure: + if (pData->hWaveInThreadEvent) + CloseHandle(pData->hWaveInThreadEvent); + if (pData->hWaveInHdrEvent) + CloseHandle(pData->hWaveInHdrEvent); + if (pData->hWaveInHandle) + waveInClose(pData->hWaveInHandle); + + free(pData); + return ALC_FALSE; +} + +static void WinMMCloseCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + int i; + + // Call waveOutReset to shutdown wave device + pData->bWaveInShutdown = AL_TRUE; + waveInReset(pData->hWaveInHandle); + + // Wait for signal that all Wave Buffers have returned + WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE); + + // Wait for signal that Wave Thread has been destroyed + WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE); + + // Release the wave buffers + for (i=0;i<4;i++) + { + waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR)); + free(pData->WaveInBuffer[i].lpData); + } + + // Free Audio Buffer data + free(pData->pCapturedSampleData); + pData->pCapturedSampleData = NULL; + + // Close the Wave device + waveInClose(pData->hWaveInHandle); + pData->hWaveInHandle = 0; + + CloseHandle(pData->hWaveInThread); + pData->hWaveInThread = 0; + + if (pData->hWaveInHdrEvent) + { + CloseHandle(pData->hWaveInHdrEvent); + pData->hWaveInHdrEvent = 0; + } + + if (pData->hWaveInThreadEvent) + { + CloseHandle(pData->hWaveInThreadEvent); + pData->hWaveInThreadEvent = 0; + } + + free(pData); + pDevice->ExtraData = NULL; +} + +static void WinMMStartCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + waveInStart(pData->hWaveInHandle); +} + +static void WinMMStopCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + waveInStop(pData->hWaveInHandle); +} + +static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + ALuint ulSamples = (unsigned long)lSamples; + ALuint ulBytes, ulBytesToCopy; + ALuint ulCapturedSamples; + ALuint ulReadOffset; + + // Check that we have the requested numbers of Samples + ulCapturedSamples = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos) / pDevice->FrameSize; + if(ulSamples > ulCapturedSamples) + { + SetALCError(ALC_INVALID_VALUE); + return; + } + + ulBytes = ulSamples * pDevice->FrameSize; + + // Get Read Offset + ulReadOffset = (pData->ulReadCapturedDataPos % pData->ulCapturedDataSize); + + // Check for wrap-around condition + if ((ulReadOffset + ulBytes) > pData->ulCapturedDataSize) + { + // Copy data from last Read position to end of data + ulBytesToCopy = pData->ulCapturedDataSize - ulReadOffset; + memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytesToCopy); + + // Copy rest of the data from the start of the captured data + memcpy(((char *)pBuffer) + ulBytesToCopy, pData->pCapturedSampleData, ulBytes - ulBytesToCopy); + } + else + { + // Copy data from the read position in the captured data + memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytes); + } + + // Update Read Position + pData->ulReadCapturedDataPos += ulBytes; +} + +static ALCuint WinMMAvailableSamples(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + ALCuint lCapturedBytes = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos); + return lCapturedBytes / pDevice->FrameSize; +} + + +BackendFuncs WinMMFuncs = { + WinMMOpenPlayback, + WinMMClosePlayback, + WinMMOpenCapture, + WinMMCloseCapture, + WinMMStartCapture, + WinMMStopCapture, + WinMMCaptureSamples, + WinMMAvailableSamples +}; + +void alcWinMMInit(BackendFuncs *FuncList) +{ + ALint lNumDevs; + ALint lLoop; + + *FuncList = WinMMFuncs; + + lNumDevs = waveInGetNumDevs(); + for (lLoop = 0; lLoop < lNumDevs; lLoop++) + { + WAVEINCAPS WaveInCaps; + if(waveInGetDevCaps(lLoop, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR) + { + char name[128]; + snprintf(name, sizeof(name), "WaveIn on %s", WaveInCaps.szPname); + CaptureDeviceList[lLoop] = AppendCaptureDeviceList(name); + } + } +} |