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
34 // MMSYSTEM Capture Device
35 ALboolean bWaveInShutdown
;
36 HANDLE hWaveInHdrEvent
;
37 HANDLE hWaveInThreadEvent
;
39 DWORD ulWaveInThreadID
;
40 ALint lWaveInBuffersCommitted
;
41 HWAVEIN hWaveInHandle
;
42 WAVEHDR WaveInBuffer
[4];
43 ALCchar
*pCapturedSampleData
;
44 ALuint ulCapturedDataSize
;
45 ALuint ulReadCapturedDataPos
;
46 ALuint ulWriteCapturedDataPos
;
50 static ALCchar
*CaptureDeviceList
[16];
55 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
56 returns to the application (with more data)
58 static void CALLBACK
WaveInProc(HWAVEIN hDevice
,UINT uMsg
,DWORD_PTR dwInstance
,DWORD_PTR dwParam1
,DWORD_PTR dwParam2
)
60 ALCdevice
*pDevice
= (ALCdevice
*)dwInstance
;
61 WinMMData
*pData
= pDevice
->ExtraData
;
66 if ((uMsg
==WIM_DATA
) && (pDevice
))
68 // Decrement number of buffers in use
69 pData
->lWaveInBuffersCommitted
--;
71 if (pData
->bWaveInShutdown
== AL_FALSE
)
73 // Notify Wave Processor Thread that a Wave Header has returned
74 PostThreadMessage(pData
->ulWaveInThreadID
,uMsg
,0,dwParam1
);
78 if (pData
->lWaveInBuffersCommitted
== 0)
80 // Signal Wave Buffers Returned event
81 if (pData
->hWaveInHdrEvent
)
82 SetEvent(pData
->hWaveInHdrEvent
);
84 // Post 'Quit' Message to WaveIn Processor Thread
85 PostThreadMessage(pData
->ulWaveInThreadID
,WM_QUIT
,0,0);
94 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
97 DWORD WINAPI
CaptureThreadProc(LPVOID lpParameter
)
99 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
100 WinMMData
*pData
= pDevice
->ExtraData
;
101 ALuint ulOffset
, ulMaxSize
, ulSection
;
105 while (GetMessage(&msg
, NULL
, 0, 0))
107 if ((msg
.message
==WIM_DATA
)&&(!pData
->bWaveInShutdown
))
109 SuspendContext(NULL
);
111 pWaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
113 // Calculate offset in local buffer to write data to
114 ulOffset
= pData
->ulWriteCapturedDataPos
% pData
->ulCapturedDataSize
;
116 if ((ulOffset
+ pWaveHdr
->dwBytesRecorded
) > pData
->ulCapturedDataSize
)
118 ulSection
= pData
->ulCapturedDataSize
- ulOffset
;
119 memcpy(pData
->pCapturedSampleData
+ ulOffset
, pWaveHdr
->lpData
, ulSection
);
120 memcpy(pData
->pCapturedSampleData
, pWaveHdr
->lpData
+ ulSection
, pWaveHdr
->dwBytesRecorded
- ulSection
);
124 memcpy(pData
->pCapturedSampleData
+ ulOffset
, pWaveHdr
->lpData
, pWaveHdr
->dwBytesRecorded
);
127 pData
->ulWriteCapturedDataPos
+= pWaveHdr
->dwBytesRecorded
;
129 if (pData
->ulWriteCapturedDataPos
> (pData
->ulReadCapturedDataPos
+ pData
->ulCapturedDataSize
))
131 // Application has not read enough audio data from the capture buffer so data has been
132 // overwritten. Reset ReadPosition.
133 pData
->ulReadCapturedDataPos
= pData
->ulWriteCapturedDataPos
- pData
->ulCapturedDataSize
;
136 // To prevent an over-flow prevent the offset values from getting too large
137 ulMaxSize
= pData
->ulCapturedDataSize
<< 4;
138 if ((pData
->ulReadCapturedDataPos
> ulMaxSize
) && (pData
->ulWriteCapturedDataPos
> ulMaxSize
))
140 pData
->ulReadCapturedDataPos
-= ulMaxSize
;
141 pData
->ulWriteCapturedDataPos
-= ulMaxSize
;
144 // Send buffer back to capture more data
145 waveInAddBuffer(pData
->hWaveInHandle
,pWaveHdr
,sizeof(WAVEHDR
));
146 pData
->lWaveInBuffersCommitted
++;
148 ProcessContext(NULL
);
152 // Signal Wave Thread completed event
153 if (pData
->hWaveInThreadEvent
)
154 SetEvent(pData
->hWaveInThreadEvent
);
162 static ALCboolean
WinMMOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
169 static void WinMMClosePlayback(ALCdevice
*device
)
175 static ALCboolean
WinMMOpenCapture(ALCdevice
*pDevice
, const ALCchar
*deviceName
, ALCuint frequency
, ALCenum format
, ALCsizei SampleSize
)
177 WAVEFORMATEX wfexCaptureFormat
;
178 WinMMData
*pData
= NULL
;
185 // Find the Device ID matching the deviceName if valid
188 for(i
= 0;CaptureDeviceList
[i
];i
++)
190 if (!strcmp(deviceName
, CaptureDeviceList
[i
]))
196 if(!CaptureDeviceList
[i
])
200 pData
= calloc(1, sizeof(*pData
));
203 SetALCError(ALC_OUT_OF_MEMORY
);
207 memset(&wfexCaptureFormat
, 0, sizeof(WAVEFORMATEX
));
208 wfexCaptureFormat
.wFormatTag
= WAVE_FORMAT_PCM
;
209 wfexCaptureFormat
.nChannels
= pDevice
->Channels
;
210 wfexCaptureFormat
.wBitsPerSample
= pDevice
->FrameSize
/ pDevice
->Channels
* 8;
211 wfexCaptureFormat
.nBlockAlign
= pDevice
->FrameSize
;
212 wfexCaptureFormat
.nSamplesPerSec
= frequency
;
213 wfexCaptureFormat
.nAvgBytesPerSec
= wfexCaptureFormat
.nSamplesPerSec
*
215 wfexCaptureFormat
.cbSize
= 0;
217 if (waveInOpen(&pData
->hWaveInHandle
, lDeviceID
, &wfexCaptureFormat
, (DWORD_PTR
)&WaveInProc
, (DWORD_PTR
)pDevice
, CALLBACK_FUNCTION
) != MMSYSERR_NOERROR
)
220 pData
->hWaveInHdrEvent
= CreateEvent(NULL
, AL_TRUE
, AL_FALSE
, "WaveInAllHeadersReturned");
221 if (pData
->hWaveInHdrEvent
== NULL
)
224 pData
->hWaveInThreadEvent
= CreateEvent(NULL
, AL_TRUE
, AL_FALSE
, "WaveInThreadDestroyed");
225 if (pData
->hWaveInThreadEvent
== NULL
)
228 pData
->hWaveInThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)CaptureThreadProc
, (LPVOID
)pDevice
, 0, &pData
->ulWaveInThreadID
);
229 if (pData
->hWaveInThread
== NULL
)
232 // Allocate circular memory buffer for the captured audio
233 pData
->ulCapturedDataSize
= SampleSize
* wfexCaptureFormat
.nBlockAlign
;
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))
239 pData
->ulCapturedDataSize
= wfexCaptureFormat
.nAvgBytesPerSec
/ 10;
240 pData
->ulCapturedDataSize
-= (pData
->ulCapturedDataSize
% wfexCaptureFormat
.nBlockAlign
);
243 pData
->pCapturedSampleData
= (ALCchar
*)malloc(pData
->ulCapturedDataSize
);
244 pData
->lWaveInBuffersCommitted
=0;
246 // Create 4 Buffers of 50ms each
247 lBufferSize
= wfexCaptureFormat
.nAvgBytesPerSec
/ 20;
248 lBufferSize
-= (lBufferSize
% wfexCaptureFormat
.nBlockAlign
);
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
++;
262 pData
->ulReadCapturedDataPos
= 0;
263 pData
->ulWriteCapturedDataPos
= 0;
265 strcpy(pDevice
->szDeviceName
, CaptureDeviceList
[lDeviceID
]);
267 pDevice
->ExtraData
= pData
;
271 if (pData
->hWaveInThreadEvent
)
272 CloseHandle(pData
->hWaveInThreadEvent
);
273 if (pData
->hWaveInHdrEvent
)
274 CloseHandle(pData
->hWaveInHdrEvent
);
275 if (pData
->hWaveInHandle
)
276 waveInClose(pData
->hWaveInHandle
);
282 static void WinMMCloseCapture(ALCdevice
*pDevice
)
284 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
287 // Call waveOutReset to shutdown wave device
288 pData
->bWaveInShutdown
= AL_TRUE
;
289 waveInReset(pData
->hWaveInHandle
);
291 // Wait for signal that all Wave Buffers have returned
292 WaitForSingleObjectEx(pData
->hWaveInHdrEvent
, 5000, FALSE
);
294 // Wait for signal that Wave Thread has been destroyed
295 WaitForSingleObjectEx(pData
->hWaveInThreadEvent
, 5000, FALSE
);
297 // Release the wave buffers
300 waveInUnprepareHeader(pData
->hWaveInHandle
, &pData
->WaveInBuffer
[i
], sizeof(WAVEHDR
));
301 free(pData
->WaveInBuffer
[i
].lpData
);
304 // Free Audio Buffer data
305 free(pData
->pCapturedSampleData
);
306 pData
->pCapturedSampleData
= NULL
;
308 // Close the Wave device
309 waveInClose(pData
->hWaveInHandle
);
310 pData
->hWaveInHandle
= 0;
312 CloseHandle(pData
->hWaveInThread
);
313 pData
->hWaveInThread
= 0;
315 if (pData
->hWaveInHdrEvent
)
317 CloseHandle(pData
->hWaveInHdrEvent
);
318 pData
->hWaveInHdrEvent
= 0;
321 if (pData
->hWaveInThreadEvent
)
323 CloseHandle(pData
->hWaveInThreadEvent
);
324 pData
->hWaveInThreadEvent
= 0;
328 pDevice
->ExtraData
= NULL
;
331 static void WinMMStartCapture(ALCdevice
*pDevice
)
333 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
334 waveInStart(pData
->hWaveInHandle
);
337 static void WinMMStopCapture(ALCdevice
*pDevice
)
339 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
340 waveInStop(pData
->hWaveInHandle
);
343 static void WinMMCaptureSamples(ALCdevice
*pDevice
, ALCvoid
*pBuffer
, ALCuint lSamples
)
345 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
346 ALuint ulSamples
= (unsigned long)lSamples
;
347 ALuint ulBytes
, ulBytesToCopy
;
348 ALuint ulCapturedSamples
;
351 // Check that we have the requested numbers of Samples
352 ulCapturedSamples
= (pData
->ulWriteCapturedDataPos
- pData
->ulReadCapturedDataPos
) / pDevice
->FrameSize
;
353 if(ulSamples
> ulCapturedSamples
)
355 SetALCError(ALC_INVALID_VALUE
);
359 ulBytes
= ulSamples
* pDevice
->FrameSize
;
362 ulReadOffset
= (pData
->ulReadCapturedDataPos
% pData
->ulCapturedDataSize
);
364 // Check for wrap-around condition
365 if ((ulReadOffset
+ ulBytes
) > pData
->ulCapturedDataSize
)
367 // Copy data from last Read position to end of data
368 ulBytesToCopy
= pData
->ulCapturedDataSize
- ulReadOffset
;
369 memcpy(pBuffer
, pData
->pCapturedSampleData
+ ulReadOffset
, ulBytesToCopy
);
371 // Copy rest of the data from the start of the captured data
372 memcpy(((char *)pBuffer
) + ulBytesToCopy
, pData
->pCapturedSampleData
, ulBytes
- ulBytesToCopy
);
376 // Copy data from the read position in the captured data
377 memcpy(pBuffer
, pData
->pCapturedSampleData
+ ulReadOffset
, ulBytes
);
380 // Update Read Position
381 pData
->ulReadCapturedDataPos
+= ulBytes
;
384 static ALCuint
WinMMAvailableSamples(ALCdevice
*pDevice
)
386 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
387 ALCuint lCapturedBytes
= (pData
->ulWriteCapturedDataPos
- pData
->ulReadCapturedDataPos
);
388 return lCapturedBytes
/ pDevice
->FrameSize
;
392 BackendFuncs WinMMFuncs
= {
400 WinMMAvailableSamples
403 void alcWinMMInit(BackendFuncs
*FuncList
)
408 *FuncList
= WinMMFuncs
;
410 lNumDevs
= waveInGetNumDevs();
411 for (lLoop
= 0; lLoop
< lNumDevs
; lLoop
++)
413 WAVEINCAPS WaveInCaps
;
414 if(waveInGetDevCaps(lLoop
, &WaveInCaps
, sizeof(WAVEINCAPS
)) == MMSYSERR_NOERROR
)
417 snprintf(name
, sizeof(name
), "WaveIn on %s", WaveInCaps
.szPname
);
418 CaptureDeviceList
[lLoop
] = AppendCaptureDeviceList(name
);