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];
50 static ALCchar
**CaptureDeviceList
;
51 static ALuint NumCaptureDevices
;
53 static void ProbeDevices(void)
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
)
75 snprintf(name
, sizeof(name
), "%s via WaveIn", WaveInCaps
.szPname
);
77 snprintf(name
, sizeof(name
), "%s #%d via WaveIn", WaveInCaps
.szPname
, count
+1);
82 if(strcmp(name
, CaptureDeviceList
[j
]) == 0)
87 CaptureDeviceList
[i
] = strdup(name
);
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
;
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
);
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);
134 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
137 static DWORD WINAPI
CaptureThreadProc(LPVOID lpParameter
)
139 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
140 WinMMData
*pData
= pDevice
->ExtraData
;
145 FrameSize
= aluFrameSizeFromFormat(pDevice
->Format
);
147 while(GetMessage(&msg
, NULL
, 0, 0))
149 if(msg
.message
!= WIM_DATA
|| pData
->bWaveInShutdown
)
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
);
172 static ALCboolean
WinMMOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
179 static void WinMMClosePlayback(ALCdevice
*device
)
185 static ALCboolean
WinMMOpenCapture(ALCdevice
*pDevice
, const ALCchar
*deviceName
)
187 WAVEFORMATEX wfexCaptureFormat
;
188 DWORD ulCapturedDataSize
;
189 WinMMData
*pData
= NULL
;
196 if(!CaptureDeviceList
)
199 // Find the Device ID matching the deviceName if valid
202 for(i
= 0;i
< NumCaptureDevices
;i
++)
204 if(CaptureDeviceList
[i
] &&
205 strcmp(deviceName
, CaptureDeviceList
[i
]) == 0)
214 for(i
= 0;i
< NumCaptureDevices
;i
++)
216 if(CaptureDeviceList
[i
])
223 if(i
== NumCaptureDevices
)
226 pData
= calloc(1, sizeof(*pData
));
229 alcSetError(pDevice
, ALC_OUT_OF_MEMORY
);
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
);
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());
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
);
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
);
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
)
299 pDevice
->szDeviceName
= strdup(CaptureDeviceList
[lDeviceID
]);
303 if(pData
->hWaveInThread
)
304 CloseHandle(pData
->hWaveInThread
);
308 if(pData
->WaveInBuffer
[i
].lpData
)
310 waveInUnprepareHeader(pData
->hWaveInHandle
, &pData
->WaveInBuffer
[i
], sizeof(WAVEHDR
));
312 free(pData
->WaveInBuffer
[i
].lpData
);
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
);
328 pDevice
->ExtraData
= NULL
;
332 static void WinMMCloseCapture(ALCdevice
*pDevice
)
334 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
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
353 waveInUnprepareHeader(pData
->hWaveInHandle
, &pData
->WaveInBuffer
[i
], sizeof(WAVEHDR
));
355 free(pData
->WaveInBuffer
[i
].lpData
);
356 pData
->WaveInBuffer
[i
].lpData
= NULL
;
359 DestroyRingBuffer(pData
->pRing
);
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;
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
);
401 alcSetError(pDevice
, ALC_INVALID_VALUE
);
404 static ALuint64
WinMMGetTime(ALCdevice
*Device
)
406 return Device
->SamplesPlayed
* 1000000000 / Device
->Frequency
;
410 static BackendFuncs WinMMFuncs
= {
420 WinMMAvailableSamples
,
424 void alcWinMMInit(BackendFuncs
*FuncList
)
426 *FuncList
= WinMMFuncs
;
429 void alcWinMMDeinit()
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
)
445 if(type
!= CAPTURE_DEVICE_PROBE
)
449 for(i
= 0;i
< NumCaptureDevices
;i
++)
451 if(CaptureDeviceList
[i
])
452 AppendCaptureDeviceList(CaptureDeviceList
[i
]);