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
38 volatile ALboolean bWaveShutdown
;
40 HANDLE hWaveThreadEvent
;
43 ALint lWaveBuffersCommitted
;
44 WAVEHDR WaveBuffer
[4];
54 static ALCchar
**CaptureDeviceList
;
55 static ALuint NumCaptureDevices
;
57 static void ProbeCaptureDevices(void)
61 for(i
= 0;i
< NumCaptureDevices
;i
++)
62 free(CaptureDeviceList
[i
]);
64 NumCaptureDevices
= waveInGetNumDevs();
65 CaptureDeviceList
= realloc(CaptureDeviceList
, sizeof(ALCchar
*) * NumCaptureDevices
);
66 for(i
= 0;i
< NumCaptureDevices
;i
++)
68 WAVEINCAPS WaveInCaps
;
70 CaptureDeviceList
[i
] = NULL
;
71 if(waveInGetDevCaps(i
, &WaveInCaps
, sizeof(WAVEINCAPS
)) == MMSYSERR_NOERROR
)
79 snprintf(name
, sizeof(name
), "%s via WaveIn", WaveInCaps
.szPname
);
81 snprintf(name
, sizeof(name
), "%s #%d via WaveIn", WaveInCaps
.szPname
, count
+1);
86 if(strcmp(name
, CaptureDeviceList
[j
]) == 0)
91 CaptureDeviceList
[i
] = strdup(name
);
100 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
101 returns to the application (with more data)
103 static void CALLBACK
WaveInProc(HWAVEIN hDevice
,UINT uMsg
,DWORD_PTR dwInstance
,DWORD_PTR dwParam1
,DWORD_PTR dwParam2
)
105 ALCdevice
*pDevice
= (ALCdevice
*)dwInstance
;
106 WinMMData
*pData
= pDevice
->ExtraData
;
114 // Decrement number of buffers in use
115 pData
->lWaveBuffersCommitted
--;
117 if(pData
->bWaveShutdown
== AL_FALSE
)
119 // Notify Wave Processor Thread that a Wave Header has returned
120 PostThreadMessage(pData
->ulWaveThreadID
,uMsg
,0,dwParam1
);
124 if(pData
->lWaveBuffersCommitted
== 0)
126 // Signal Wave Buffers Returned event
127 if(pData
->hWaveHdrEvent
)
128 SetEvent(pData
->hWaveHdrEvent
);
130 // Post 'Quit' Message to WaveIn Processor Thread
131 PostThreadMessage(pData
->ulWaveThreadID
,WM_QUIT
,0,0);
139 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
142 static DWORD WINAPI
CaptureThreadProc(LPVOID lpParameter
)
144 ALCdevice
*pDevice
= (ALCdevice
*)lpParameter
;
145 WinMMData
*pData
= pDevice
->ExtraData
;
150 FrameSize
= aluFrameSizeFromFormat(pDevice
->Format
);
152 while(GetMessage(&msg
, NULL
, 0, 0))
154 if(msg
.message
!= WIM_DATA
|| pData
->bWaveShutdown
)
157 pWaveHdr
= ((LPWAVEHDR
)msg
.lParam
);
159 WriteRingBuffer(pData
->pRing
, (ALubyte
*)pWaveHdr
->lpData
,
160 pWaveHdr
->dwBytesRecorded
/FrameSize
);
162 // Send buffer back to capture more data
163 waveInAddBuffer(pData
->hWaveHandle
.In
,pWaveHdr
,sizeof(WAVEHDR
));
164 pData
->lWaveBuffersCommitted
++;
167 // Signal Wave Thread completed event
168 if(pData
->hWaveThreadEvent
)
169 SetEvent(pData
->hWaveThreadEvent
);
177 static ALCboolean
WinMMOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
184 static void WinMMClosePlayback(ALCdevice
*device
)
189 static ALCboolean
WinMMOpenCapture(ALCdevice
*pDevice
, const ALCchar
*deviceName
)
191 WAVEFORMATEX wfexCaptureFormat
;
192 DWORD ulCapturedDataSize
;
193 WinMMData
*pData
= NULL
;
200 if(!CaptureDeviceList
)
201 ProbeCaptureDevices();
203 // Find the Device ID matching the deviceName if valid
206 for(i
= 0;i
< NumCaptureDevices
;i
++)
208 if(CaptureDeviceList
[i
] &&
209 strcmp(deviceName
, CaptureDeviceList
[i
]) == 0)
218 for(i
= 0;i
< NumCaptureDevices
;i
++)
220 if(CaptureDeviceList
[i
])
227 if(i
== NumCaptureDevices
)
230 pData
= calloc(1, sizeof(*pData
));
233 alcSetError(pDevice
, ALC_OUT_OF_MEMORY
);
237 memset(&wfexCaptureFormat
, 0, sizeof(WAVEFORMATEX
));
238 wfexCaptureFormat
.wFormatTag
= WAVE_FORMAT_PCM
;
239 wfexCaptureFormat
.nChannels
= aluChannelsFromFormat(pDevice
->Format
);
240 wfexCaptureFormat
.wBitsPerSample
= aluBytesFromFormat(pDevice
->Format
) * 8;
241 wfexCaptureFormat
.nBlockAlign
= wfexCaptureFormat
.wBitsPerSample
*
242 wfexCaptureFormat
.nChannels
/ 8;
243 wfexCaptureFormat
.nSamplesPerSec
= pDevice
->Frequency
;
244 wfexCaptureFormat
.nAvgBytesPerSec
= wfexCaptureFormat
.nSamplesPerSec
*
245 wfexCaptureFormat
.nBlockAlign
;
246 wfexCaptureFormat
.cbSize
= 0;
248 if((res
=waveInOpen(&pData
->hWaveHandle
.In
, lDeviceID
, &wfexCaptureFormat
, (DWORD_PTR
)&WaveInProc
, (DWORD_PTR
)pDevice
, CALLBACK_FUNCTION
)) != MMSYSERR_NOERROR
)
250 AL_PRINT("waveInOpen failed: %u\n", res
);
254 pData
->hWaveHdrEvent
= CreateEvent(NULL
, AL_TRUE
, AL_FALSE
, "WaveInAllHeadersReturned");
255 pData
->hWaveThreadEvent
= CreateEvent(NULL
, AL_TRUE
, AL_FALSE
, "WaveInThreadDestroyed");
256 if(pData
->hWaveHdrEvent
== NULL
|| pData
->hWaveThreadEvent
== NULL
)
258 AL_PRINT("CreateEvent failed: %lu\n", GetLastError());
262 // Allocate circular memory buffer for the captured audio
263 ulCapturedDataSize
= pDevice
->UpdateSize
*pDevice
->NumUpdates
;
265 // Make sure circular buffer is at least 100ms in size
266 if(ulCapturedDataSize
< (wfexCaptureFormat
.nSamplesPerSec
/ 10))
267 ulCapturedDataSize
= wfexCaptureFormat
.nSamplesPerSec
/ 10;
269 pData
->pRing
= CreateRingBuffer(wfexCaptureFormat
.nBlockAlign
, ulCapturedDataSize
);
273 pData
->lWaveBuffersCommitted
= 0;
275 // Create 4 Buffers of 50ms each
276 lBufferSize
= wfexCaptureFormat
.nAvgBytesPerSec
/ 20;
277 lBufferSize
-= (lBufferSize
% wfexCaptureFormat
.nBlockAlign
);
279 BufferData
= calloc(4, lBufferSize
);
285 memset(&pData
->WaveBuffer
[i
], 0, sizeof(WAVEHDR
));
286 pData
->WaveBuffer
[i
].dwBufferLength
= lBufferSize
;
287 pData
->WaveBuffer
[i
].lpData
= ((i
==0) ? (LPSTR
)BufferData
:
288 (pData
->WaveBuffer
[i
-1].lpData
+
289 pData
->WaveBuffer
[i
-1].dwBufferLength
));
290 pData
->WaveBuffer
[i
].dwFlags
= 0;
291 pData
->WaveBuffer
[i
].dwLoops
= 0;
292 waveInPrepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
293 waveInAddBuffer(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
294 pData
->lWaveBuffersCommitted
++;
297 pDevice
->ExtraData
= pData
;
299 pData
->hWaveThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)CaptureThreadProc
, (LPVOID
)pDevice
, 0, &pData
->ulWaveThreadID
);
300 if (pData
->hWaveThread
== NULL
)
303 pDevice
->szDeviceName
= strdup(CaptureDeviceList
[lDeviceID
]);
307 if(pData
->hWaveThread
)
308 CloseHandle(pData
->hWaveThread
);
312 if(pData
->WaveBuffer
[i
].lpData
)
314 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
316 free(pData
->WaveBuffer
[i
].lpData
);
321 DestroyRingBuffer(pData
->pRing
);
323 if(pData
->hWaveThreadEvent
)
324 CloseHandle(pData
->hWaveThreadEvent
);
325 if(pData
->hWaveHdrEvent
)
326 CloseHandle(pData
->hWaveHdrEvent
);
328 if(pData
->hWaveHandle
.In
)
329 waveInClose(pData
->hWaveHandle
.In
);
332 pDevice
->ExtraData
= NULL
;
336 static void WinMMCloseCapture(ALCdevice
*pDevice
)
338 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
341 // Call waveOutReset to shutdown wave device
342 pData
->bWaveShutdown
= AL_TRUE
;
343 waveInReset(pData
->hWaveHandle
.In
);
345 // Wait for signal that all Wave Buffers have returned
346 WaitForSingleObjectEx(pData
->hWaveHdrEvent
, 5000, FALSE
);
348 // Wait for signal that Wave Thread has been destroyed
349 WaitForSingleObjectEx(pData
->hWaveThreadEvent
, 5000, FALSE
);
351 CloseHandle(pData
->hWaveThread
);
352 pData
->hWaveThread
= 0;
354 // Release the wave buffers
357 waveInUnprepareHeader(pData
->hWaveHandle
.In
, &pData
->WaveBuffer
[i
], sizeof(WAVEHDR
));
359 free(pData
->WaveBuffer
[i
].lpData
);
360 pData
->WaveBuffer
[i
].lpData
= NULL
;
363 DestroyRingBuffer(pData
->pRing
);
366 // Close the Wave device
367 CloseHandle(pData
->hWaveThreadEvent
);
368 pData
->hWaveThreadEvent
= 0;
370 CloseHandle(pData
->hWaveHdrEvent
);
371 pData
->hWaveHdrEvent
= 0;
373 waveInClose(pData
->hWaveHandle
.In
);
374 pData
->hWaveHandle
.In
= 0;
377 pDevice
->ExtraData
= NULL
;
380 static void WinMMStartCapture(ALCdevice
*pDevice
)
382 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
383 waveInStart(pData
->hWaveHandle
.In
);
386 static void WinMMStopCapture(ALCdevice
*pDevice
)
388 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
389 waveInStop(pData
->hWaveHandle
.In
);
392 static ALCuint
WinMMAvailableSamples(ALCdevice
*pDevice
)
394 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
395 return RingBufferSize(pData
->pRing
);
398 static void WinMMCaptureSamples(ALCdevice
*pDevice
, ALCvoid
*pBuffer
, ALCuint lSamples
)
400 WinMMData
*pData
= (WinMMData
*)pDevice
->ExtraData
;
402 if(WinMMAvailableSamples(pDevice
) >= lSamples
)
403 ReadRingBuffer(pData
->pRing
, pBuffer
, lSamples
);
405 alcSetError(pDevice
, ALC_INVALID_VALUE
);
409 static BackendFuncs WinMMFuncs
= {
419 WinMMAvailableSamples
422 void alcWinMMInit(BackendFuncs
*FuncList
)
424 *FuncList
= WinMMFuncs
;
427 void alcWinMMDeinit()
431 for(lLoop
= 0; lLoop
< NumCaptureDevices
; lLoop
++)
432 free(CaptureDeviceList
[lLoop
]);
433 free(CaptureDeviceList
);
434 CaptureDeviceList
= NULL
;
436 NumCaptureDevices
= 0;
439 void alcWinMMProbe(int type
)
443 if(type
== CAPTURE_DEVICE_PROBE
)
445 ProbeCaptureDevices();
446 for(i
= 0;i
< NumCaptureDevices
;i
++)
448 if(CaptureDeviceList
[i
])
449 AppendCaptureDeviceList(CaptureDeviceList
[i
]);