Use CheckFunctionExists instead of abusing CheckLibraryExists
[openal-soft.git] / Alc / winmm.c
blob26f40db28e3ffca21c7ad1bf9f34627758eb7173
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <memory.h>
25 #include <windows.h>
26 #include <mmsystem.h>
28 #include "alMain.h"
29 #include "AL/al.h"
30 #include "AL/alc.h"
33 typedef struct {
34 // MMSYSTEM Capture Device
35 ALboolean bWaveInShutdown;
36 HANDLE hWaveInHdrEvent;
37 HANDLE hWaveInThreadEvent;
38 HANDLE hWaveInThread;
39 DWORD ulWaveInThreadID;
40 ALint lWaveInBuffersCommitted;
41 HWAVEIN hWaveInHandle;
42 WAVEHDR WaveInBuffer[4];
43 ALCchar *pCapturedSampleData;
44 ALuint ulCapturedDataSize;
45 ALuint ulReadCapturedDataPos;
46 ALuint ulWriteCapturedDataPos;
47 } WinMMData;
50 static ALCchar *CaptureDeviceList[16];
53 WaveInProc
55 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
56 returns to the application (with more data)
58 static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
60 ALCdevice *pDevice = (ALCdevice *)dwInstance;
61 WinMMData *pData = pDevice->ExtraData;
63 (void)hDevice;
64 (void)dwParam2;
66 if ((uMsg==WIM_DATA) && (pDevice))
68 // Decrement number of buffers in use
69 pData->lWaveInBuffersCommitted--;
71 if (pData->bWaveInShutdown == AL_FALSE)
73 // Notify Wave Processor Thread that a Wave Header has returned
74 PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1);
76 else
78 if (pData->lWaveInBuffersCommitted == 0)
80 // Signal Wave Buffers Returned event
81 if (pData->hWaveInHdrEvent)
82 SetEvent(pData->hWaveInHdrEvent);
84 // Post 'Quit' Message to WaveIn Processor Thread
85 PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0);
92 CaptureThreadProc
94 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
95 audio data.
97 DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
99 ALCdevice *pDevice = (ALCdevice*)lpParameter;
100 WinMMData *pData = pDevice->ExtraData;
101 ALuint ulOffset, ulMaxSize, ulSection;
102 LPWAVEHDR pWaveHdr;
103 MSG msg;
105 while (GetMessage(&msg, NULL, 0, 0))
107 if ((msg.message==WIM_DATA)&&(!pData->bWaveInShutdown))
109 SuspendContext(NULL);
111 pWaveHdr = ((LPWAVEHDR)msg.lParam);
113 // Calculate offset in local buffer to write data to
114 ulOffset = pData->ulWriteCapturedDataPos % pData->ulCapturedDataSize;
116 if ((ulOffset + pWaveHdr->dwBytesRecorded) > pData->ulCapturedDataSize)
118 ulSection = pData->ulCapturedDataSize - ulOffset;
119 memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, ulSection);
120 memcpy(pData->pCapturedSampleData, pWaveHdr->lpData + ulSection, pWaveHdr->dwBytesRecorded - ulSection);
122 else
124 memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded);
127 pData->ulWriteCapturedDataPos += pWaveHdr->dwBytesRecorded;
129 if (pData->ulWriteCapturedDataPos > (pData->ulReadCapturedDataPos + pData->ulCapturedDataSize))
131 // Application has not read enough audio data from the capture buffer so data has been
132 // overwritten. Reset ReadPosition.
133 pData->ulReadCapturedDataPos = pData->ulWriteCapturedDataPos - pData->ulCapturedDataSize;
136 // To prevent an over-flow prevent the offset values from getting too large
137 ulMaxSize = pData->ulCapturedDataSize << 4;
138 if ((pData->ulReadCapturedDataPos > ulMaxSize) && (pData->ulWriteCapturedDataPos > ulMaxSize))
140 pData->ulReadCapturedDataPos -= ulMaxSize;
141 pData->ulWriteCapturedDataPos -= ulMaxSize;
144 // Send buffer back to capture more data
145 waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR));
146 pData->lWaveInBuffersCommitted++;
148 ProcessContext(NULL);
152 // Signal Wave Thread completed event
153 if (pData->hWaveInThreadEvent)
154 SetEvent(pData->hWaveInThreadEvent);
156 ExitThread(0);
158 return 0;
162 static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
164 (void)device;
165 (void)deviceName;
166 return ALC_FALSE;
169 static void WinMMClosePlayback(ALCdevice *device)
171 (void)device;
175 static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
177 WAVEFORMATEX wfexCaptureFormat;
178 WinMMData *pData = NULL;
179 ALint lDeviceID = 0;
180 ALint lBufferSize;
181 ALint i;
183 (void)format;
185 // Find the Device ID matching the deviceName if valid
186 if (deviceName)
188 for(i = 0;CaptureDeviceList[i];i++)
190 if (!strcmp(deviceName, CaptureDeviceList[i]))
192 lDeviceID = i;
193 break;
196 if(!CaptureDeviceList[i])
197 return ALC_FALSE;
200 pData = calloc(1, sizeof(*pData));
201 if(!pData)
203 SetALCError(ALC_OUT_OF_MEMORY);
204 return ALC_FALSE;
207 memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
208 wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
209 wfexCaptureFormat.nChannels = pDevice->Channels;
210 wfexCaptureFormat.wBitsPerSample = pDevice->FrameSize / pDevice->Channels * 8;
211 wfexCaptureFormat.nBlockAlign = pDevice->FrameSize;
212 wfexCaptureFormat.nSamplesPerSec = frequency;
213 wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
214 pDevice->FrameSize;
215 wfexCaptureFormat.cbSize = 0;
217 if (waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
218 goto failure;
220 pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned");
221 if (pData->hWaveInHdrEvent == NULL)
222 goto failure;
224 pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed");
225 if (pData->hWaveInThreadEvent == NULL)
226 goto failure;
228 pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID);
229 if (pData->hWaveInThread == NULL)
230 goto failure;
232 // Allocate circular memory buffer for the captured audio
233 pData->ulCapturedDataSize = SampleSize * wfexCaptureFormat.nBlockAlign;
235 // Make sure circular buffer is at least 100ms in size (and an exact multiple of
236 // the block alignment
237 if (pData->ulCapturedDataSize < (wfexCaptureFormat.nAvgBytesPerSec / 10))
239 pData->ulCapturedDataSize = wfexCaptureFormat.nAvgBytesPerSec / 10;
240 pData->ulCapturedDataSize -= (pData->ulCapturedDataSize % wfexCaptureFormat.nBlockAlign);
243 pData->pCapturedSampleData = (ALCchar*)malloc(pData->ulCapturedDataSize);
244 pData->lWaveInBuffersCommitted=0;
246 // Create 4 Buffers of 50ms each
247 lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
248 lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
250 for (i=0;i<4;i++)
252 memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR));
253 pData->WaveInBuffer[i].dwBufferLength = lBufferSize;
254 pData->WaveInBuffer[i].lpData = calloc(1,pData->WaveInBuffer[i].dwBufferLength);
255 pData->WaveInBuffer[i].dwFlags = 0;
256 pData->WaveInBuffer[i].dwLoops = 0;
257 waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
258 waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
259 pData->lWaveInBuffersCommitted++;
262 pData->ulReadCapturedDataPos = 0;
263 pData->ulWriteCapturedDataPos = 0;
265 strcpy(pDevice->szDeviceName, CaptureDeviceList[lDeviceID]);
267 pDevice->ExtraData = pData;
268 return ALC_TRUE;
270 failure:
271 if (pData->hWaveInThreadEvent)
272 CloseHandle(pData->hWaveInThreadEvent);
273 if (pData->hWaveInHdrEvent)
274 CloseHandle(pData->hWaveInHdrEvent);
275 if (pData->hWaveInHandle)
276 waveInClose(pData->hWaveInHandle);
278 free(pData);
279 return ALC_FALSE;
282 static void WinMMCloseCapture(ALCdevice *pDevice)
284 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
285 int i;
287 // Call waveOutReset to shutdown wave device
288 pData->bWaveInShutdown = AL_TRUE;
289 waveInReset(pData->hWaveInHandle);
291 // Wait for signal that all Wave Buffers have returned
292 WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE);
294 // Wait for signal that Wave Thread has been destroyed
295 WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE);
297 // Release the wave buffers
298 for (i=0;i<4;i++)
300 waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
301 free(pData->WaveInBuffer[i].lpData);
304 // Free Audio Buffer data
305 free(pData->pCapturedSampleData);
306 pData->pCapturedSampleData = NULL;
308 // Close the Wave device
309 waveInClose(pData->hWaveInHandle);
310 pData->hWaveInHandle = 0;
312 CloseHandle(pData->hWaveInThread);
313 pData->hWaveInThread = 0;
315 if (pData->hWaveInHdrEvent)
317 CloseHandle(pData->hWaveInHdrEvent);
318 pData->hWaveInHdrEvent = 0;
321 if (pData->hWaveInThreadEvent)
323 CloseHandle(pData->hWaveInThreadEvent);
324 pData->hWaveInThreadEvent = 0;
327 free(pData);
328 pDevice->ExtraData = NULL;
331 static void WinMMStartCapture(ALCdevice *pDevice)
333 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
334 waveInStart(pData->hWaveInHandle);
337 static void WinMMStopCapture(ALCdevice *pDevice)
339 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
340 waveInStop(pData->hWaveInHandle);
343 static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
345 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
346 ALuint ulSamples = (unsigned long)lSamples;
347 ALuint ulBytes, ulBytesToCopy;
348 ALuint ulCapturedSamples;
349 ALuint ulReadOffset;
351 // Check that we have the requested numbers of Samples
352 ulCapturedSamples = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos) / pDevice->FrameSize;
353 if(ulSamples > ulCapturedSamples)
355 SetALCError(ALC_INVALID_VALUE);
356 return;
359 ulBytes = ulSamples * pDevice->FrameSize;
361 // Get Read Offset
362 ulReadOffset = (pData->ulReadCapturedDataPos % pData->ulCapturedDataSize);
364 // Check for wrap-around condition
365 if ((ulReadOffset + ulBytes) > pData->ulCapturedDataSize)
367 // Copy data from last Read position to end of data
368 ulBytesToCopy = pData->ulCapturedDataSize - ulReadOffset;
369 memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytesToCopy);
371 // Copy rest of the data from the start of the captured data
372 memcpy(((char *)pBuffer) + ulBytesToCopy, pData->pCapturedSampleData, ulBytes - ulBytesToCopy);
374 else
376 // Copy data from the read position in the captured data
377 memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytes);
380 // Update Read Position
381 pData->ulReadCapturedDataPos += ulBytes;
384 static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
386 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
387 ALCuint lCapturedBytes = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos);
388 return lCapturedBytes / pDevice->FrameSize;
392 BackendFuncs WinMMFuncs = {
393 WinMMOpenPlayback,
394 WinMMClosePlayback,
395 WinMMOpenCapture,
396 WinMMCloseCapture,
397 WinMMStartCapture,
398 WinMMStopCapture,
399 WinMMCaptureSamples,
400 WinMMAvailableSamples
403 void alcWinMMInit(BackendFuncs *FuncList)
405 ALint lNumDevs;
406 ALint lLoop;
408 *FuncList = WinMMFuncs;
410 lNumDevs = waveInGetNumDevs();
411 for (lLoop = 0; lLoop < lNumDevs; lLoop++)
413 WAVEINCAPS WaveInCaps;
414 if(waveInGetDevCaps(lLoop, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
416 char name[128];
417 snprintf(name, sizeof(name), "WaveIn on %s", WaveInCaps.szPname);
418 CaptureDeviceList[lLoop] = AppendCaptureDeviceList(name);