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
23 #define _WIN32_WINNT 0x0500
37 // MMSYSTEM Capture Device
38 ALboolean bWaveInShutdown
;
39 HANDLE hWaveInHdrEvent
;
40 HANDLE hWaveInThreadEvent
;
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
;
53 static ALCchar
**CaptureDeviceList
;
54 static ALuint NumCaptureDevices
;
56 static void ProbeDevices(void)
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
)
73 snprintf(name
, sizeof(name
), "WaveIn on %s", WaveInCaps
.szPname
);
74 CaptureDeviceList
[i
] = strdup(name
);
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
;
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
);
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);
121 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
124 DWORD WINAPI
CaptureThreadProc(LPVOID lpParameter
)
126 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
127 WinMMData
*pData
= pDevice
->ExtraData
;
128 ALuint ulOffset
, ulMaxSize
, ulSection
;
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
);
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
);
189 static ALCboolean
WinMMOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
196 static void WinMMClosePlayback(ALCdevice
*device
)
202 static ALCboolean
WinMMOpenCapture(ALCdevice
*pDevice
, const ALCchar
*deviceName
)
204 WAVEFORMATEX wfexCaptureFormat
;
205 WinMMData
*pData
= NULL
;
210 if(!CaptureDeviceList
)
213 // Find the Device ID matching the deviceName if valid
216 for(i
= 0;i
< NumCaptureDevices
;i
++)
218 if(CaptureDeviceList
[i
] &&
219 strcmp(deviceName
, CaptureDeviceList
[i
]) == 0)
228 for(i
= 0;i
< NumCaptureDevices
;i
++)
230 if(CaptureDeviceList
[i
])
237 if(i
== NumCaptureDevices
)
240 pData
= calloc(1, sizeof(*pData
));
243 alcSetError(pDevice
, ALC_OUT_OF_MEMORY
);
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
)
261 pData
->hWaveInHdrEvent
= CreateEvent(NULL
, AL_TRUE
, AL_FALSE
, "WaveInAllHeadersReturned");
262 if (pData
->hWaveInHdrEvent
== NULL
)
265 pData
->hWaveInThreadEvent
= CreateEvent(NULL
, AL_TRUE
, AL_FALSE
, "WaveInThreadDestroyed");
266 if (pData
->hWaveInThreadEvent
== NULL
)
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
);
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
)
309 pDevice
->szDeviceName
= strdup(CaptureDeviceList
[lDeviceID
]);
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
);
333 pDevice
->ExtraData
= NULL
;
337 static void WinMMCloseCapture(ALCdevice
*pDevice
)
339 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
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
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;
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
;
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
) /
412 if(ulSamples
> ulCapturedSamples
)
414 alcSetError(pDevice
, ALC_INVALID_VALUE
);
418 ulBytes
= ulSamples
* frameSize
;
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
);
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
= {
462 WinMMAvailableSamples
465 void alcWinMMInit(BackendFuncs
*FuncList
)
467 *FuncList
= WinMMFuncs
;
470 void alcWinMMDeinit()
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
)
486 if(type
!= CAPTURE_DEVICE_PROBE
)
490 for(i
= 0;i
< NumCaptureDevices
;i
++)
492 if(CaptureDeviceList
[i
])
493 AppendCaptureDeviceList(CaptureDeviceList
[i
]);