Better protect against sample overflow when converting float to short
[openal-soft.git] / Alc / winmm.c
blob61ff87b79b482e728fc84af90b9dccecc84403cb
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 RingBuffer *pRing;
47 } WinMMData;
50 static ALCchar **CaptureDeviceList;
51 static ALuint NumCaptureDevices;
53 static void ProbeDevices(void)
55 ALuint i;
57 for(i = 0;i < NumCaptureDevices;i++)
58 free(CaptureDeviceList[i]);
60 NumCaptureDevices = waveInGetNumDevs();
61 CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices);
62 for(i = 0;i < NumCaptureDevices;i++)
64 WAVEINCAPS WaveInCaps;
66 CaptureDeviceList[i] = NULL;
67 if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
69 char name[1024];
70 ALuint count, j;
72 count = 0;
73 do {
74 if(count == 0)
75 snprintf(name, sizeof(name), "%s via WaveIn", WaveInCaps.szPname);
76 else
77 snprintf(name, sizeof(name), "%s #%d via WaveIn", WaveInCaps.szPname, count+1);
78 count++;
80 for(j = 0;j < i;j++)
82 if(strcmp(name, CaptureDeviceList[j]) == 0)
83 break;
85 } while(j != i);
87 CaptureDeviceList[i] = strdup(name);
93 WaveInProc
95 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
96 returns to the application (with more data)
98 static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
100 ALCdevice *pDevice = (ALCdevice*)dwInstance;
101 WinMMData *pData = pDevice->ExtraData;
103 (void)hDevice;
104 (void)dwParam2;
106 if(uMsg != WIM_DATA)
107 return;
109 // Decrement number of buffers in use
110 pData->lWaveInBuffersCommitted--;
112 if(pData->bWaveInShutdown == AL_FALSE)
114 // Notify Wave Processor Thread that a Wave Header has returned
115 PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1);
117 else
119 if(pData->lWaveInBuffersCommitted == 0)
121 // Signal Wave Buffers Returned event
122 if(pData->hWaveInHdrEvent)
123 SetEvent(pData->hWaveInHdrEvent);
125 // Post 'Quit' Message to WaveIn Processor Thread
126 PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0);
132 CaptureThreadProc
134 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
135 audio data.
137 static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
139 ALCdevice *pDevice = (ALCdevice*)lpParameter;
140 WinMMData *pData = pDevice->ExtraData;
141 LPWAVEHDR pWaveHdr;
142 ALuint FrameSize;
143 MSG msg;
145 FrameSize = aluFrameSizeFromFormat(pDevice->Format);
147 while(GetMessage(&msg, NULL, 0, 0))
149 if(msg.message != WIM_DATA || pData->bWaveInShutdown)
150 continue;
152 pWaveHdr = ((LPWAVEHDR)msg.lParam);
154 WriteRingBuffer(pData->pRing, (ALubyte*)pWaveHdr->lpData,
155 pWaveHdr->dwBytesRecorded/FrameSize);
157 // Send buffer back to capture more data
158 waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR));
159 pData->lWaveInBuffersCommitted++;
162 // Signal Wave Thread completed event
163 if(pData->hWaveInThreadEvent)
164 SetEvent(pData->hWaveInThreadEvent);
166 ExitThread(0);
168 return 0;
172 static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
174 (void)device;
175 (void)deviceName;
176 return ALC_FALSE;
179 static void WinMMClosePlayback(ALCdevice *device)
181 (void)device;
185 static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
187 WAVEFORMATEX wfexCaptureFormat;
188 DWORD ulCapturedDataSize;
189 WinMMData *pData = NULL;
190 ALint lDeviceID = 0;
191 ALbyte *BufferData;
192 ALint lBufferSize;
193 MMRESULT res;
194 ALuint i;
196 if(!CaptureDeviceList)
197 ProbeDevices();
199 // Find the Device ID matching the deviceName if valid
200 if(deviceName)
202 for(i = 0;i < NumCaptureDevices;i++)
204 if(CaptureDeviceList[i] &&
205 strcmp(deviceName, CaptureDeviceList[i]) == 0)
207 lDeviceID = i;
208 break;
212 else
214 for(i = 0;i < NumCaptureDevices;i++)
216 if(CaptureDeviceList[i])
218 lDeviceID = i;
219 break;
223 if(i == NumCaptureDevices)
224 return ALC_FALSE;
226 pData = calloc(1, sizeof(*pData));
227 if(!pData)
229 alcSetError(pDevice, ALC_OUT_OF_MEMORY);
230 return ALC_FALSE;
233 memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
234 wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
235 wfexCaptureFormat.nChannels = aluChannelsFromFormat(pDevice->Format);
236 wfexCaptureFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8;
237 wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
238 wfexCaptureFormat.nChannels / 8;
239 wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
240 wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
241 wfexCaptureFormat.nBlockAlign;
242 wfexCaptureFormat.cbSize = 0;
244 if((res=waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
246 AL_PRINT("waveInOpen failed: %u\n", res);
247 goto failure;
250 pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned");
251 pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed");
252 if(pData->hWaveInHdrEvent == NULL || pData->hWaveInThreadEvent == NULL)
254 AL_PRINT("CreateEvent failed: %lu\n", GetLastError());
255 goto failure;
258 // Allocate circular memory buffer for the captured audio
259 ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates;
261 // Make sure circular buffer is at least 100ms in size
262 if(ulCapturedDataSize < (wfexCaptureFormat.nSamplesPerSec / 10))
263 ulCapturedDataSize = wfexCaptureFormat.nSamplesPerSec / 10;
265 pData->pRing = CreateRingBuffer(wfexCaptureFormat.nBlockAlign, ulCapturedDataSize);
266 if(!pData->pRing)
267 goto failure;
269 pData->lWaveInBuffersCommitted=0;
271 // Create 4 Buffers of 50ms each
272 lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
273 lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
275 BufferData = calloc(4, lBufferSize);
276 if(!BufferData)
277 goto failure;
279 for(i = 0;i < 4;i++)
281 memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR));
282 pData->WaveInBuffer[i].dwBufferLength = lBufferSize;
283 pData->WaveInBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
284 (pData->WaveInBuffer[i-1].lpData +
285 pData->WaveInBuffer[i-1].dwBufferLength));
286 pData->WaveInBuffer[i].dwFlags = 0;
287 pData->WaveInBuffer[i].dwLoops = 0;
288 waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
289 waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
290 pData->lWaveInBuffersCommitted++;
293 pDevice->ExtraData = pData;
295 pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID);
296 if (pData->hWaveInThread == NULL)
297 goto failure;
299 pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
300 return ALC_TRUE;
302 failure:
303 if(pData->hWaveInThread)
304 CloseHandle(pData->hWaveInThread);
306 for(i = 0;i < 4;i++)
308 if(pData->WaveInBuffer[i].lpData)
310 waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
311 if(i == 0)
312 free(pData->WaveInBuffer[i].lpData);
316 if(pData->pRing)
317 DestroyRingBuffer(pData->pRing);
319 if(pData->hWaveInThreadEvent)
320 CloseHandle(pData->hWaveInThreadEvent);
321 if(pData->hWaveInHdrEvent)
322 CloseHandle(pData->hWaveInHdrEvent);
324 if(pData->hWaveInHandle)
325 waveInClose(pData->hWaveInHandle);
327 free(pData);
328 pDevice->ExtraData = NULL;
329 return ALC_FALSE;
332 static void WinMMCloseCapture(ALCdevice *pDevice)
334 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
335 int i;
337 // Call waveOutReset to shutdown wave device
338 pData->bWaveInShutdown = AL_TRUE;
339 waveInReset(pData->hWaveInHandle);
341 // Wait for signal that all Wave Buffers have returned
342 WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE);
344 // Wait for signal that Wave Thread has been destroyed
345 WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE);
347 CloseHandle(pData->hWaveInThread);
348 pData->hWaveInThread = 0;
350 // Release the wave buffers
351 for(i = 0;i < 4;i++)
353 waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
354 if(i == 0)
355 free(pData->WaveInBuffer[i].lpData);
356 pData->WaveInBuffer[i].lpData = NULL;
359 DestroyRingBuffer(pData->pRing);
360 pData->pRing = NULL;
362 // Close the Wave device
363 CloseHandle(pData->hWaveInThreadEvent);
364 pData->hWaveInThreadEvent = 0;
366 CloseHandle(pData->hWaveInHdrEvent);
367 pData->hWaveInHdrEvent = 0;
369 waveInClose(pData->hWaveInHandle);
370 pData->hWaveInHandle = 0;
372 free(pData);
373 pDevice->ExtraData = NULL;
376 static void WinMMStartCapture(ALCdevice *pDevice)
378 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
379 waveInStart(pData->hWaveInHandle);
382 static void WinMMStopCapture(ALCdevice *pDevice)
384 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
385 waveInStop(pData->hWaveInHandle);
388 static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
390 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
391 return RingBufferSize(pData->pRing);
394 static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
396 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
398 if(WinMMAvailableSamples(pDevice) >= lSamples)
399 ReadRingBuffer(pData->pRing, pBuffer, lSamples);
400 else
401 alcSetError(pDevice, ALC_INVALID_VALUE);
404 static ALuint64 WinMMGetTime(ALCdevice *Device)
406 return Device->SamplesPlayed * 1000000000 / Device->Frequency;
410 static BackendFuncs WinMMFuncs = {
411 WinMMOpenPlayback,
412 WinMMClosePlayback,
413 NULL,
414 NULL,
415 WinMMOpenCapture,
416 WinMMCloseCapture,
417 WinMMStartCapture,
418 WinMMStopCapture,
419 WinMMCaptureSamples,
420 WinMMAvailableSamples,
421 WinMMGetTime
424 void alcWinMMInit(BackendFuncs *FuncList)
426 *FuncList = WinMMFuncs;
429 void alcWinMMDeinit()
431 ALuint lLoop;
433 for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
434 free(CaptureDeviceList[lLoop]);
435 free(CaptureDeviceList);
436 CaptureDeviceList = NULL;
438 NumCaptureDevices = 0;
441 void alcWinMMProbe(int type)
443 ALuint i;
445 if(type != CAPTURE_DEVICE_PROBE)
446 return;
448 ProbeDevices();
449 for(i = 0;i < NumCaptureDevices;i++)
451 if(CaptureDeviceList[i])
452 AppendCaptureDeviceList(CaptureDeviceList[i]);