summaryrefslogtreecommitdiffstats
path: root/Alc/winmm.c
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2007-11-13 18:02:18 -0800
committerChris Robinson <[email protected]>2007-11-13 18:02:18 -0800
commitae5f4e9a742b07e004b330c04a72fac4457c9b58 (patch)
treed1d5c9fadd918d9346fb871033f60e8c91600a63 /Alc/winmm.c
Initial import
Diffstat (limited to 'Alc/winmm.c')
-rw-r--r--Alc/winmm.c421
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);
+ }
+ }
+}