Mercurial > fife-parpg
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 } |