comparison ext/openal-soft/Alc/winmm.c @ 89:fa33cda75471

* Reverting back to 2543 as requested by sleek
author mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222
date Sat, 19 Jul 2008 11:38:52 +0000
parents 4a0efb7baf70
children
comparison
equal deleted inserted replaced
88:1c2842ebe393 89:fa33cda75471
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.
8 *
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.
13 *
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
19 */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <memory.h>
26
27 #include <windows.h>
28 #include <mmsystem.h>
29
30 #include "alMain.h"
31 #include "AL/al.h"
32 #include "AL/alc.h"
33
34
35 typedef struct {
36 // MMSYSTEM Capture Device
37 ALboolean bWaveInShutdown;
38 HANDLE hWaveInHdrEvent;
39 HANDLE hWaveInThreadEvent;
40 HANDLE hWaveInThread;
41 DWORD ulWaveInThreadID;
42 ALint lWaveInBuffersCommitted;
43 HWAVEIN hWaveInHandle;
44 WAVEHDR WaveInBuffer[4];
45 ALCchar *pCapturedSampleData;
46 ALuint ulCapturedDataSize;
47 ALuint ulReadCapturedDataPos;
48 ALuint ulWriteCapturedDataPos;
49 } WinMMData;
50
51
52 static ALCchar *CaptureDeviceList[16];
53
54 /*
55 WaveInProc
56
57 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
58 returns to the application (with more data)
59 */
60 static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
61 {
62 ALCdevice *pDevice = (ALCdevice *)dwInstance;
63 WinMMData *pData = pDevice->ExtraData;
64
65 (void)hDevice;
66 (void)dwParam2;
67
68 if ((uMsg==WIM_DATA))
69 {
70 // Decrement number of buffers in use
71 pData->lWaveInBuffersCommitted--;
72
73 if (pData->bWaveInShutdown == AL_FALSE)
74 {
75 // Notify Wave Processor Thread that a Wave Header has returned
76 PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1);
77 }
78 else
79 {
80 if (pData->lWaveInBuffersCommitted == 0)
81 {
82 // Signal Wave Buffers Returned event
83 if (pData->hWaveInHdrEvent)
84 SetEvent(pData->hWaveInHdrEvent);
85
86 // Post 'Quit' Message to WaveIn Processor Thread
87 PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0);
88 }
89 }
90 }
91 }
92
93 /*
94 CaptureThreadProc
95
96 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
97 audio data.
98 */
99 DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
100 {
101 ALCdevice *pDevice = (ALCdevice*)lpParameter;
102 WinMMData *pData = pDevice->ExtraData;
103 ALuint ulOffset, ulMaxSize, ulSection;
104 LPWAVEHDR pWaveHdr;
105 MSG msg;
106
107 while (GetMessage(&msg, NULL, 0, 0))
108 {
109 if ((msg.message==WIM_DATA)&&(!pData->bWaveInShutdown))
110 {
111 SuspendContext(NULL);
112
113 pWaveHdr = ((LPWAVEHDR)msg.lParam);
114
115 // Calculate offset in local buffer to write data to
116 ulOffset = pData->ulWriteCapturedDataPos % pData->ulCapturedDataSize;
117
118 if ((ulOffset + pWaveHdr->dwBytesRecorded) > pData->ulCapturedDataSize)
119 {
120 ulSection = pData->ulCapturedDataSize - ulOffset;
121 memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, ulSection);
122 memcpy(pData->pCapturedSampleData, pWaveHdr->lpData + ulSection, pWaveHdr->dwBytesRecorded - ulSection);
123 }
124 else
125 {
126 memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded);
127 }
128
129 pData->ulWriteCapturedDataPos += pWaveHdr->dwBytesRecorded;
130
131 if (pData->ulWriteCapturedDataPos > (pData->ulReadCapturedDataPos + pData->ulCapturedDataSize))
132 {
133 // Application has not read enough audio data from the capture buffer so data has been
134 // overwritten. Reset ReadPosition.
135 pData->ulReadCapturedDataPos = pData->ulWriteCapturedDataPos - pData->ulCapturedDataSize;
136 }
137
138 // To prevent an over-flow prevent the offset values from getting too large
139 ulMaxSize = pData->ulCapturedDataSize << 4;
140 if ((pData->ulReadCapturedDataPos > ulMaxSize) && (pData->ulWriteCapturedDataPos > ulMaxSize))
141 {
142 pData->ulReadCapturedDataPos -= ulMaxSize;
143 pData->ulWriteCapturedDataPos -= ulMaxSize;
144 }
145
146 // Send buffer back to capture more data
147 waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR));
148 pData->lWaveInBuffersCommitted++;
149
150 ProcessContext(NULL);
151 }
152 }
153
154 // Signal Wave Thread completed event
155 if (pData->hWaveInThreadEvent)
156 SetEvent(pData->hWaveInThreadEvent);
157
158 ExitThread(0);
159
160 return 0;
161 }
162
163
164 static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
165 {
166 (void)device;
167 (void)deviceName;
168 return ALC_FALSE;
169 }
170
171 static void WinMMClosePlayback(ALCdevice *device)
172 {
173 (void)device;
174 }
175
176
177 static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
178 {
179 WAVEFORMATEX wfexCaptureFormat;
180 WinMMData *pData = NULL;
181 ALint lDeviceID = 0;
182 ALint lBufferSize;
183 ALint i;
184
185 (void)format;
186
187 // Find the Device ID matching the deviceName if valid
188 if (deviceName)
189 {
190 for(i = 0;CaptureDeviceList[i];i++)
191 {
192 if (!strcmp(deviceName, CaptureDeviceList[i]))
193 {
194 lDeviceID = i;
195 break;
196 }
197 }
198 if(!CaptureDeviceList[i])
199 return ALC_FALSE;
200 }
201 pDevice->szDeviceName = CaptureDeviceList[lDeviceID];
202
203 pData = calloc(1, sizeof(*pData));
204 if(!pData)
205 {
206 SetALCError(ALC_OUT_OF_MEMORY);
207 return ALC_FALSE;
208 }
209
210 memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
211 wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
212 wfexCaptureFormat.nChannels = aluChannelsFromFormat(pDevice->Format);
213 wfexCaptureFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8;
214 wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
215 wfexCaptureFormat.nChannels / 8;
216 wfexCaptureFormat.nSamplesPerSec = frequency;
217 wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
218 wfexCaptureFormat.nBlockAlign;
219 wfexCaptureFormat.cbSize = 0;
220
221 if (waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
222 goto failure;
223
224 pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned");
225 if (pData->hWaveInHdrEvent == NULL)
226 goto failure;
227
228 pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed");
229 if (pData->hWaveInThreadEvent == NULL)
230 goto failure;
231
232 // Allocate circular memory buffer for the captured audio
233 pData->ulCapturedDataSize = SampleSize * wfexCaptureFormat.nBlockAlign;
234
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))
238 {
239 pData->ulCapturedDataSize = wfexCaptureFormat.nAvgBytesPerSec / 10;
240 pData->ulCapturedDataSize -= (pData->ulCapturedDataSize % wfexCaptureFormat.nBlockAlign);
241 }
242
243 pData->pCapturedSampleData = (ALCchar*)malloc(pData->ulCapturedDataSize);
244 pData->lWaveInBuffersCommitted=0;
245
246 // Create 4 Buffers of 50ms each
247 lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
248 lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
249
250 for (i=0;i<4;i++)
251 {
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++;
260 }
261
262 pData->ulReadCapturedDataPos = 0;
263 pData->ulWriteCapturedDataPos = 0;
264
265 pDevice->ExtraData = pData;
266
267 pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID);
268 if (pData->hWaveInThread == NULL)
269 goto failure;
270
271 return ALC_TRUE;
272
273 failure:
274 for (i=0;i<4;i++)
275 {
276 if(pData->WaveInBuffer[i].lpData)
277 {
278 waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
279 free(pData->WaveInBuffer[i].lpData);
280 }
281 }
282
283 free(pData->pCapturedSampleData);
284 if(pData->hWaveInHandle)
285 waveInClose(pData->hWaveInHandle);
286 if(pData->hWaveInThread)
287 CloseHandle(pData->hWaveInThread);
288 if (pData->hWaveInHdrEvent)
289 CloseHandle(pData->hWaveInHdrEvent);
290 if (pData->hWaveInThreadEvent)
291 CloseHandle(pData->hWaveInThreadEvent);
292
293 free(pData);
294 return ALC_FALSE;
295 }
296
297 static void WinMMCloseCapture(ALCdevice *pDevice)
298 {
299 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
300 int i;
301
302 // Call waveOutReset to shutdown wave device
303 pData->bWaveInShutdown = AL_TRUE;
304 waveInReset(pData->hWaveInHandle);
305
306 // Wait for signal that all Wave Buffers have returned
307 WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE);
308
309 // Wait for signal that Wave Thread has been destroyed
310 WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE);
311
312 // Release the wave buffers
313 for (i=0;i<4;i++)
314 {
315 waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
316 free(pData->WaveInBuffer[i].lpData);
317 }
318
319 // Free Audio Buffer data
320 free(pData->pCapturedSampleData);
321 pData->pCapturedSampleData = NULL;
322
323 // Close the Wave device
324 waveInClose(pData->hWaveInHandle);
325 pData->hWaveInHandle = 0;
326
327 CloseHandle(pData->hWaveInThread);
328 pData->hWaveInThread = 0;
329
330 if (pData->hWaveInHdrEvent)
331 {
332 CloseHandle(pData->hWaveInHdrEvent);
333 pData->hWaveInHdrEvent = 0;
334 }
335
336 if (pData->hWaveInThreadEvent)
337 {
338 CloseHandle(pData->hWaveInThreadEvent);
339 pData->hWaveInThreadEvent = 0;
340 }
341
342 free(pData);
343 pDevice->ExtraData = NULL;
344 }
345
346 static void WinMMStartCapture(ALCdevice *pDevice)
347 {
348 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
349 waveInStart(pData->hWaveInHandle);
350 }
351
352 static void WinMMStopCapture(ALCdevice *pDevice)
353 {
354 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
355 waveInStop(pData->hWaveInHandle);
356 }
357
358 static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
359 {
360 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
361 ALuint ulSamples = (unsigned long)lSamples;
362 ALuint ulBytes, ulBytesToCopy;
363 ALuint ulCapturedSamples;
364 ALuint ulReadOffset;
365 ALuint frameSize = aluBytesFromFormat(pDevice->Format) *
366 aluChannelsFromFormat(pDevice->Format);
367
368 // Check that we have the requested numbers of Samples
369 ulCapturedSamples = (pData->ulWriteCapturedDataPos -
370 pData->ulReadCapturedDataPos) /
371 frameSize;
372 if(ulSamples > ulCapturedSamples)
373 {
374 SetALCError(ALC_INVALID_VALUE);
375 return;
376 }
377
378 ulBytes = ulSamples * frameSize;
379
380 // Get Read Offset
381 ulReadOffset = (pData->ulReadCapturedDataPos % pData->ulCapturedDataSize);
382
383 // Check for wrap-around condition
384 if ((ulReadOffset + ulBytes) > pData->ulCapturedDataSize)
385 {
386 // Copy data from last Read position to end of data
387 ulBytesToCopy = pData->ulCapturedDataSize - ulReadOffset;
388 memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytesToCopy);
389
390 // Copy rest of the data from the start of the captured data
391 memcpy(((char *)pBuffer) + ulBytesToCopy, pData->pCapturedSampleData, ulBytes - ulBytesToCopy);
392 }
393 else
394 {
395 // Copy data from the read position in the captured data
396 memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytes);
397 }
398
399 // Update Read Position
400 pData->ulReadCapturedDataPos += ulBytes;
401 }
402
403 static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
404 {
405 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
406 ALCuint lCapturedBytes = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos);
407 return lCapturedBytes / (aluBytesFromFormat(pDevice->Format) *
408 aluChannelsFromFormat(pDevice->Format));
409 }
410
411
412 BackendFuncs WinMMFuncs = {
413 WinMMOpenPlayback,
414 WinMMClosePlayback,
415 WinMMOpenCapture,
416 WinMMCloseCapture,
417 WinMMStartCapture,
418 WinMMStopCapture,
419 WinMMCaptureSamples,
420 WinMMAvailableSamples
421 };
422
423 void alcWinMMInit(BackendFuncs *FuncList)
424 {
425 ALint lNumDevs;
426 ALint lLoop;
427
428 *FuncList = WinMMFuncs;
429
430 lNumDevs = waveInGetNumDevs();
431 for (lLoop = 0; lLoop < lNumDevs; lLoop++)
432 {
433 WAVEINCAPS WaveInCaps;
434 if(waveInGetDevCaps(lLoop, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
435 {
436 char name[128];
437 snprintf(name, sizeof(name), "WaveIn on %s", WaveInCaps.szPname);
438 CaptureDeviceList[lLoop] = AppendCaptureDeviceList(name);
439 }
440 }
441 }