Don't use deprecated macros
[openal-soft.git] / Alc / winmm.c
blob8f27519e52a0fccfa2ae5965cea6c5e86438cf27
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 "config.h"
23 #define _WIN32_WINNT 0x0500
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <memory.h>
28 #include <windows.h>
29 #include <mmsystem.h>
31 #include "alMain.h"
32 #include "AL/al.h"
33 #include "AL/alc.h"
36 typedef struct {
37 // MMSYSTEM Capture Device
38 ALboolean bWaveInShutdown;
39 HANDLE hWaveInHdrEvent;
40 HANDLE hWaveInThreadEvent;
41 HANDLE hWaveInThread;
42 DWORD ulWaveInThreadID;
43 ALint lWaveInBuffersCommitted;
44 HWAVEIN hWaveInHandle;
45 WAVEHDR WaveInBuffer[4];
46 ALCchar *pCapturedSampleData;
47 ALuint ulCapturedDataSize;
48 ALuint ulReadCapturedDataPos;
49 ALuint ulWriteCapturedDataPos;
50 } WinMMData;
53 static ALCchar **CaptureDeviceList;
54 static ALuint NumCaptureDevices;
56 static void ProbeDevices(void)
58 ALuint i;
60 for(i = 0;i < NumCaptureDevices;i++)
61 free(CaptureDeviceList[i]);
63 NumCaptureDevices = waveInGetNumDevs();
64 CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices);
65 for(i = 0;i < NumCaptureDevices;i++)
67 WAVEINCAPS WaveInCaps;
69 CaptureDeviceList[i] = NULL;
70 if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
72 char name[128];
73 snprintf(name, sizeof(name), "WaveIn on %s", WaveInCaps.szPname);
74 CaptureDeviceList[i] = strdup(name);
80 WaveInProc
82 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
83 returns to the application (with more data)
85 static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
87 ALCdevice *pDevice = (ALCdevice *)dwInstance;
88 WinMMData *pData = pDevice->ExtraData;
90 (void)hDevice;
91 (void)dwParam2;
93 if ((uMsg==WIM_DATA))
95 // Decrement number of buffers in use
96 pData->lWaveInBuffersCommitted--;
98 if (pData->bWaveInShutdown == AL_FALSE)
100 // Notify Wave Processor Thread that a Wave Header has returned
101 PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1);
103 else
105 if (pData->lWaveInBuffersCommitted == 0)
107 // Signal Wave Buffers Returned event
108 if (pData->hWaveInHdrEvent)
109 SetEvent(pData->hWaveInHdrEvent);
111 // Post 'Quit' Message to WaveIn Processor Thread
112 PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0);
119 CaptureThreadProc
121 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
122 audio data.
124 DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
126 ALCdevice *pDevice = (ALCdevice*)lpParameter;
127 WinMMData *pData = pDevice->ExtraData;
128 ALuint ulOffset, ulMaxSize, ulSection;
129 LPWAVEHDR pWaveHdr;
130 MSG msg;
132 while (GetMessage(&msg, NULL, 0, 0))
134 if ((msg.message==WIM_DATA)&&(!pData->bWaveInShutdown))
136 SuspendContext(NULL);
138 pWaveHdr = ((LPWAVEHDR)msg.lParam);
140 // Calculate offset in local buffer to write data to
141 ulOffset = pData->ulWriteCapturedDataPos % pData->ulCapturedDataSize;
143 if ((ulOffset + pWaveHdr->dwBytesRecorded) > pData->ulCapturedDataSize)
145 ulSection = pData->ulCapturedDataSize - ulOffset;
146 memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, ulSection);
147 memcpy(pData->pCapturedSampleData, pWaveHdr->lpData + ulSection, pWaveHdr->dwBytesRecorded - ulSection);
149 else
151 memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded);
154 pData->ulWriteCapturedDataPos += pWaveHdr->dwBytesRecorded;
156 if (pData->ulWriteCapturedDataPos > (pData->ulReadCapturedDataPos + pData->ulCapturedDataSize))
158 // Application has not read enough audio data from the capture buffer so data has been
159 // overwritten. Reset ReadPosition.
160 pData->ulReadCapturedDataPos = pData->ulWriteCapturedDataPos - pData->ulCapturedDataSize;
163 // To prevent an over-flow prevent the offset values from getting too large
164 ulMaxSize = pData->ulCapturedDataSize << 4;
165 if ((pData->ulReadCapturedDataPos > ulMaxSize) && (pData->ulWriteCapturedDataPos > ulMaxSize))
167 pData->ulReadCapturedDataPos -= ulMaxSize;
168 pData->ulWriteCapturedDataPos -= ulMaxSize;
171 // Send buffer back to capture more data
172 waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR));
173 pData->lWaveInBuffersCommitted++;
175 ProcessContext(NULL);
179 // Signal Wave Thread completed event
180 if (pData->hWaveInThreadEvent)
181 SetEvent(pData->hWaveInThreadEvent);
183 ExitThread(0);
185 return 0;
189 static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
191 (void)device;
192 (void)deviceName;
193 return ALC_FALSE;
196 static void WinMMClosePlayback(ALCdevice *device)
198 (void)device;
202 static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
204 WAVEFORMATEX wfexCaptureFormat;
205 WinMMData *pData = NULL;
206 ALint lDeviceID = 0;
207 ALint lBufferSize;
208 ALuint i;
210 if(!CaptureDeviceList)
211 ProbeDevices();
213 // Find the Device ID matching the deviceName if valid
214 if(deviceName)
216 for(i = 0;i < NumCaptureDevices;i++)
218 if(CaptureDeviceList[i] &&
219 strcmp(deviceName, CaptureDeviceList[i]) == 0)
221 lDeviceID = i;
222 break;
226 else
228 for(i = 0;i < NumCaptureDevices;i++)
230 if(CaptureDeviceList[i])
232 lDeviceID = i;
233 break;
237 if(i == NumCaptureDevices)
238 return ALC_FALSE;
240 pData = calloc(1, sizeof(*pData));
241 if(!pData)
243 alcSetError(pDevice, ALC_OUT_OF_MEMORY);
244 return ALC_FALSE;
247 memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
248 wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
249 wfexCaptureFormat.nChannels = aluChannelsFromFormat(pDevice->Format);
250 wfexCaptureFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8;
251 wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
252 wfexCaptureFormat.nChannels / 8;
253 wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
254 wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
255 wfexCaptureFormat.nBlockAlign;
256 wfexCaptureFormat.cbSize = 0;
258 if (waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
259 goto failure;
261 pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned");
262 if (pData->hWaveInHdrEvent == NULL)
263 goto failure;
265 pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed");
266 if (pData->hWaveInThreadEvent == NULL)
267 goto failure;
269 // Allocate circular memory buffer for the captured audio
270 pData->ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates *
271 wfexCaptureFormat.nBlockAlign;
273 // Make sure circular buffer is at least 100ms in size (and an exact multiple of
274 // the block alignment
275 if (pData->ulCapturedDataSize < (wfexCaptureFormat.nAvgBytesPerSec / 10))
277 pData->ulCapturedDataSize = wfexCaptureFormat.nAvgBytesPerSec / 10;
278 pData->ulCapturedDataSize -= (pData->ulCapturedDataSize % wfexCaptureFormat.nBlockAlign);
281 pData->pCapturedSampleData = (ALCchar*)malloc(pData->ulCapturedDataSize);
282 pData->lWaveInBuffersCommitted=0;
284 // Create 4 Buffers of 50ms each
285 lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
286 lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
288 for (i=0;i<4;i++)
290 memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR));
291 pData->WaveInBuffer[i].dwBufferLength = lBufferSize;
292 pData->WaveInBuffer[i].lpData = calloc(1,pData->WaveInBuffer[i].dwBufferLength);
293 pData->WaveInBuffer[i].dwFlags = 0;
294 pData->WaveInBuffer[i].dwLoops = 0;
295 waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
296 waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
297 pData->lWaveInBuffersCommitted++;
300 pData->ulReadCapturedDataPos = 0;
301 pData->ulWriteCapturedDataPos = 0;
303 pDevice->ExtraData = pData;
305 pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID);
306 if (pData->hWaveInThread == NULL)
307 goto failure;
309 pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
310 return ALC_TRUE;
312 failure:
313 for (i=0;i<4;i++)
315 if(pData->WaveInBuffer[i].lpData)
317 waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
318 free(pData->WaveInBuffer[i].lpData);
322 free(pData->pCapturedSampleData);
323 if(pData->hWaveInHandle)
324 waveInClose(pData->hWaveInHandle);
325 if(pData->hWaveInThread)
326 CloseHandle(pData->hWaveInThread);
327 if (pData->hWaveInHdrEvent)
328 CloseHandle(pData->hWaveInHdrEvent);
329 if (pData->hWaveInThreadEvent)
330 CloseHandle(pData->hWaveInThreadEvent);
332 free(pData);
333 pDevice->ExtraData = NULL;
334 return ALC_FALSE;
337 static void WinMMCloseCapture(ALCdevice *pDevice)
339 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
340 int i;
342 // Call waveOutReset to shutdown wave device
343 pData->bWaveInShutdown = AL_TRUE;
344 waveInReset(pData->hWaveInHandle);
346 // Wait for signal that all Wave Buffers have returned
347 WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE);
349 // Wait for signal that Wave Thread has been destroyed
350 WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE);
352 // Release the wave buffers
353 for (i=0;i<4;i++)
355 waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
356 free(pData->WaveInBuffer[i].lpData);
359 // Free Audio Buffer data
360 free(pData->pCapturedSampleData);
361 pData->pCapturedSampleData = NULL;
363 // Close the Wave device
364 waveInClose(pData->hWaveInHandle);
365 pData->hWaveInHandle = 0;
367 CloseHandle(pData->hWaveInThread);
368 pData->hWaveInThread = 0;
370 if (pData->hWaveInHdrEvent)
372 CloseHandle(pData->hWaveInHdrEvent);
373 pData->hWaveInHdrEvent = 0;
376 if (pData->hWaveInThreadEvent)
378 CloseHandle(pData->hWaveInThreadEvent);
379 pData->hWaveInThreadEvent = 0;
382 free(pData);
383 pDevice->ExtraData = NULL;
386 static void WinMMStartCapture(ALCdevice *pDevice)
388 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
389 waveInStart(pData->hWaveInHandle);
392 static void WinMMStopCapture(ALCdevice *pDevice)
394 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
395 waveInStop(pData->hWaveInHandle);
398 static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
400 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
401 ALuint ulSamples = (unsigned long)lSamples;
402 ALuint ulBytes, ulBytesToCopy;
403 ALuint ulCapturedSamples;
404 ALuint ulReadOffset;
405 ALuint frameSize = aluBytesFromFormat(pDevice->Format) *
406 aluChannelsFromFormat(pDevice->Format);
408 // Check that we have the requested numbers of Samples
409 ulCapturedSamples = (pData->ulWriteCapturedDataPos -
410 pData->ulReadCapturedDataPos) /
411 frameSize;
412 if(ulSamples > ulCapturedSamples)
414 alcSetError(pDevice, ALC_INVALID_VALUE);
415 return;
418 ulBytes = ulSamples * frameSize;
420 // Get Read Offset
421 ulReadOffset = (pData->ulReadCapturedDataPos % pData->ulCapturedDataSize);
423 // Check for wrap-around condition
424 if ((ulReadOffset + ulBytes) > pData->ulCapturedDataSize)
426 // Copy data from last Read position to end of data
427 ulBytesToCopy = pData->ulCapturedDataSize - ulReadOffset;
428 memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytesToCopy);
430 // Copy rest of the data from the start of the captured data
431 memcpy(((char *)pBuffer) + ulBytesToCopy, pData->pCapturedSampleData, ulBytes - ulBytesToCopy);
433 else
435 // Copy data from the read position in the captured data
436 memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytes);
439 // Update Read Position
440 pData->ulReadCapturedDataPos += ulBytes;
443 static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
445 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
446 ALCuint lCapturedBytes = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos);
447 return lCapturedBytes / (aluBytesFromFormat(pDevice->Format) *
448 aluChannelsFromFormat(pDevice->Format));
452 BackendFuncs WinMMFuncs = {
453 WinMMOpenPlayback,
454 WinMMClosePlayback,
455 NULL,
456 NULL,
457 WinMMOpenCapture,
458 WinMMCloseCapture,
459 WinMMStartCapture,
460 WinMMStopCapture,
461 WinMMCaptureSamples,
462 WinMMAvailableSamples
465 void alcWinMMInit(BackendFuncs *FuncList)
467 *FuncList = WinMMFuncs;
470 void alcWinMMDeinit()
472 ALuint lLoop;
474 for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
475 free(CaptureDeviceList[lLoop]);
476 free(CaptureDeviceList);
477 CaptureDeviceList = NULL;
479 NumCaptureDevices = 0;
482 void alcWinMMProbe(int type)
484 ALuint i;
486 if(type != CAPTURE_DEVICE_PROBE)
487 return;
489 ProbeDevices();
490 for(i = 0;i < NumCaptureDevices;i++)
492 if(CaptureDeviceList[i])
493 AppendCaptureDeviceList(CaptureDeviceList[i]);