rlm@0
|
1 /**
|
rlm@0
|
2 * OpenAL cross platform audio library
|
rlm@0
|
3 * Copyright (C) 1999-2007 by authors.
|
rlm@0
|
4 * This library is free software; you can redistribute it and/or
|
rlm@0
|
5 * modify it under the terms of the GNU Library General Public
|
rlm@0
|
6 * License as published by the Free Software Foundation; either
|
rlm@0
|
7 * version 2 of the License, or (at your option) any later version.
|
rlm@0
|
8 *
|
rlm@0
|
9 * This library is distributed in the hope that it will be useful,
|
rlm@0
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
rlm@0
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
rlm@0
|
12 * Library General Public License for more details.
|
rlm@0
|
13 *
|
rlm@0
|
14 * You should have received a copy of the GNU Library General Public
|
rlm@0
|
15 * License along with this library; if not, write to the
|
rlm@0
|
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
rlm@0
|
17 * Boston, MA 02111-1307, USA.
|
rlm@0
|
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
|
rlm@0
|
19 */
|
rlm@0
|
20
|
rlm@0
|
21 #include "config.h"
|
rlm@0
|
22
|
rlm@0
|
23 #define _WIN32_WINNT 0x0500
|
rlm@0
|
24 #include <stdlib.h>
|
rlm@0
|
25 #include <stdio.h>
|
rlm@0
|
26 #include <memory.h>
|
rlm@0
|
27
|
rlm@0
|
28 #include <windows.h>
|
rlm@0
|
29 #include <mmsystem.h>
|
rlm@0
|
30
|
rlm@0
|
31 #include "alMain.h"
|
rlm@0
|
32 #include "AL/al.h"
|
rlm@0
|
33 #include "AL/alc.h"
|
rlm@0
|
34
|
rlm@0
|
35
|
rlm@0
|
36 typedef struct {
|
rlm@0
|
37 // MMSYSTEM Device
|
rlm@0
|
38 volatile ALboolean bWaveShutdown;
|
rlm@0
|
39 HANDLE hWaveThreadEvent;
|
rlm@0
|
40 HANDLE hWaveThread;
|
rlm@0
|
41 DWORD ulWaveThreadID;
|
rlm@0
|
42 LONG lWaveBuffersCommitted;
|
rlm@0
|
43 WAVEHDR WaveBuffer[4];
|
rlm@0
|
44
|
rlm@0
|
45 union {
|
rlm@0
|
46 HWAVEIN In;
|
rlm@0
|
47 HWAVEOUT Out;
|
rlm@0
|
48 } hWaveHandle;
|
rlm@0
|
49
|
rlm@0
|
50 ALuint Frequency;
|
rlm@0
|
51
|
rlm@0
|
52 RingBuffer *pRing;
|
rlm@0
|
53 } WinMMData;
|
rlm@0
|
54
|
rlm@0
|
55
|
rlm@0
|
56 static const ALCchar woDefault[] = "WaveOut Default";
|
rlm@0
|
57
|
rlm@0
|
58 static ALCchar **PlaybackDeviceList;
|
rlm@0
|
59 static ALuint NumPlaybackDevices;
|
rlm@0
|
60 static ALCchar **CaptureDeviceList;
|
rlm@0
|
61 static ALuint NumCaptureDevices;
|
rlm@0
|
62
|
rlm@0
|
63
|
rlm@0
|
64 static void ProbePlaybackDevices(void)
|
rlm@0
|
65 {
|
rlm@0
|
66 ALuint i;
|
rlm@0
|
67
|
rlm@0
|
68 for(i = 0;i < NumPlaybackDevices;i++)
|
rlm@0
|
69 free(PlaybackDeviceList[i]);
|
rlm@0
|
70
|
rlm@0
|
71 NumPlaybackDevices = waveOutGetNumDevs();
|
rlm@0
|
72 PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices);
|
rlm@0
|
73 for(i = 0;i < NumPlaybackDevices;i++)
|
rlm@0
|
74 {
|
rlm@0
|
75 WAVEOUTCAPS WaveCaps;
|
rlm@0
|
76
|
rlm@0
|
77 PlaybackDeviceList[i] = NULL;
|
rlm@0
|
78 if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
rlm@0
|
79 {
|
rlm@0
|
80 char name[1024];
|
rlm@0
|
81 ALuint count, j;
|
rlm@0
|
82
|
rlm@0
|
83 count = 0;
|
rlm@0
|
84 do {
|
rlm@0
|
85 if(count == 0)
|
rlm@0
|
86 snprintf(name, sizeof(name), "%s", WaveCaps.szPname);
|
rlm@0
|
87 else
|
rlm@0
|
88 snprintf(name, sizeof(name), "%s #%d", WaveCaps.szPname, count+1);
|
rlm@0
|
89 count++;
|
rlm@0
|
90
|
rlm@0
|
91 for(j = 0;j < i;j++)
|
rlm@0
|
92 {
|
rlm@0
|
93 if(strcmp(name, PlaybackDeviceList[j]) == 0)
|
rlm@0
|
94 break;
|
rlm@0
|
95 }
|
rlm@0
|
96 } while(j != i);
|
rlm@0
|
97
|
rlm@0
|
98 PlaybackDeviceList[i] = strdup(name);
|
rlm@0
|
99 }
|
rlm@0
|
100 }
|
rlm@0
|
101 }
|
rlm@0
|
102
|
rlm@0
|
103 static void ProbeCaptureDevices(void)
|
rlm@0
|
104 {
|
rlm@0
|
105 ALuint i;
|
rlm@0
|
106
|
rlm@0
|
107 for(i = 0;i < NumCaptureDevices;i++)
|
rlm@0
|
108 free(CaptureDeviceList[i]);
|
rlm@0
|
109
|
rlm@0
|
110 NumCaptureDevices = waveInGetNumDevs();
|
rlm@0
|
111 CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices);
|
rlm@0
|
112 for(i = 0;i < NumCaptureDevices;i++)
|
rlm@0
|
113 {
|
rlm@0
|
114 WAVEINCAPS WaveInCaps;
|
rlm@0
|
115
|
rlm@0
|
116 CaptureDeviceList[i] = NULL;
|
rlm@0
|
117 if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
|
rlm@0
|
118 {
|
rlm@0
|
119 char name[1024];
|
rlm@0
|
120 ALuint count, j;
|
rlm@0
|
121
|
rlm@0
|
122 count = 0;
|
rlm@0
|
123 do {
|
rlm@0
|
124 if(count == 0)
|
rlm@0
|
125 snprintf(name, sizeof(name), "%s", WaveInCaps.szPname);
|
rlm@0
|
126 else
|
rlm@0
|
127 snprintf(name, sizeof(name), "%s #%d", WaveInCaps.szPname, count+1);
|
rlm@0
|
128 count++;
|
rlm@0
|
129
|
rlm@0
|
130 for(j = 0;j < i;j++)
|
rlm@0
|
131 {
|
rlm@0
|
132 if(strcmp(name, CaptureDeviceList[j]) == 0)
|
rlm@0
|
133 break;
|
rlm@0
|
134 }
|
rlm@0
|
135 } while(j != i);
|
rlm@0
|
136
|
rlm@0
|
137 CaptureDeviceList[i] = strdup(name);
|
rlm@0
|
138 }
|
rlm@0
|
139 }
|
rlm@0
|
140 }
|
rlm@0
|
141
|
rlm@0
|
142
|
rlm@0
|
143 /*
|
rlm@0
|
144 WaveOutProc
|
rlm@0
|
145
|
rlm@0
|
146 Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
|
rlm@0
|
147 returns to the application (for more data)
|
rlm@0
|
148 */
|
rlm@0
|
149 static void CALLBACK WaveOutProc(HWAVEOUT hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
|
rlm@0
|
150 {
|
rlm@0
|
151 ALCdevice *pDevice = (ALCdevice*)dwInstance;
|
rlm@0
|
152 WinMMData *pData = pDevice->ExtraData;
|
rlm@0
|
153
|
rlm@0
|
154 (void)hDevice;
|
rlm@0
|
155 (void)dwParam2;
|
rlm@0
|
156
|
rlm@0
|
157 if(uMsg != WOM_DONE)
|
rlm@0
|
158 return;
|
rlm@0
|
159
|
rlm@0
|
160 // Decrement number of buffers in use
|
rlm@0
|
161 InterlockedDecrement(&pData->lWaveBuffersCommitted);
|
rlm@0
|
162
|
rlm@0
|
163 if(pData->bWaveShutdown == AL_FALSE)
|
rlm@0
|
164 {
|
rlm@0
|
165 // Notify Wave Processor Thread that a Wave Header has returned
|
rlm@0
|
166 PostThreadMessage(pData->ulWaveThreadID, uMsg, 0, dwParam1);
|
rlm@0
|
167 }
|
rlm@0
|
168 else
|
rlm@0
|
169 {
|
rlm@0
|
170 if(pData->lWaveBuffersCommitted == 0)
|
rlm@0
|
171 {
|
rlm@0
|
172 // Post 'Quit' Message to WaveOut Processor Thread
|
rlm@0
|
173 PostThreadMessage(pData->ulWaveThreadID, WM_QUIT, 0, 0);
|
rlm@0
|
174 }
|
rlm@0
|
175 }
|
rlm@0
|
176 }
|
rlm@0
|
177
|
rlm@0
|
178 /*
|
rlm@0
|
179 PlaybackThreadProc
|
rlm@0
|
180
|
rlm@0
|
181 Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
|
rlm@0
|
182 audio data.
|
rlm@0
|
183 */
|
rlm@0
|
184 static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter)
|
rlm@0
|
185 {
|
rlm@0
|
186 ALCdevice *pDevice = (ALCdevice*)lpParameter;
|
rlm@0
|
187 WinMMData *pData = pDevice->ExtraData;
|
rlm@0
|
188 LPWAVEHDR pWaveHdr;
|
rlm@0
|
189 ALuint FrameSize;
|
rlm@0
|
190 MSG msg;
|
rlm@0
|
191
|
rlm@0
|
192 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
|
rlm@0
|
193
|
rlm@0
|
194 SetRTPriority();
|
rlm@0
|
195
|
rlm@0
|
196 while(GetMessage(&msg, NULL, 0, 0))
|
rlm@0
|
197 {
|
rlm@0
|
198 if(msg.message != WOM_DONE || pData->bWaveShutdown)
|
rlm@0
|
199 continue;
|
rlm@0
|
200
|
rlm@0
|
201 pWaveHdr = ((LPWAVEHDR)msg.lParam);
|
rlm@0
|
202
|
rlm@0
|
203 aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize);
|
rlm@0
|
204
|
rlm@0
|
205 // Send buffer back to play more data
|
rlm@0
|
206 waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR));
|
rlm@0
|
207 InterlockedIncrement(&pData->lWaveBuffersCommitted);
|
rlm@0
|
208 }
|
rlm@0
|
209
|
rlm@0
|
210 // Signal Wave Thread completed event
|
rlm@0
|
211 if(pData->hWaveThreadEvent)
|
rlm@0
|
212 SetEvent(pData->hWaveThreadEvent);
|
rlm@0
|
213
|
rlm@0
|
214 ExitThread(0);
|
rlm@0
|
215
|
rlm@0
|
216 return 0;
|
rlm@0
|
217 }
|
rlm@0
|
218
|
rlm@0
|
219 /*
|
rlm@0
|
220 WaveInProc
|
rlm@0
|
221
|
rlm@0
|
222 Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
|
rlm@0
|
223 returns to the application (with more data)
|
rlm@0
|
224 */
|
rlm@0
|
225 static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
|
rlm@0
|
226 {
|
rlm@0
|
227 ALCdevice *pDevice = (ALCdevice*)dwInstance;
|
rlm@0
|
228 WinMMData *pData = pDevice->ExtraData;
|
rlm@0
|
229
|
rlm@0
|
230 (void)hDevice;
|
rlm@0
|
231 (void)dwParam2;
|
rlm@0
|
232
|
rlm@0
|
233 if(uMsg != WIM_DATA)
|
rlm@0
|
234 return;
|
rlm@0
|
235
|
rlm@0
|
236 // Decrement number of buffers in use
|
rlm@0
|
237 InterlockedDecrement(&pData->lWaveBuffersCommitted);
|
rlm@0
|
238
|
rlm@0
|
239 if(pData->bWaveShutdown == AL_FALSE)
|
rlm@0
|
240 {
|
rlm@0
|
241 // Notify Wave Processor Thread that a Wave Header has returned
|
rlm@0
|
242 PostThreadMessage(pData->ulWaveThreadID,uMsg,0,dwParam1);
|
rlm@0
|
243 }
|
rlm@0
|
244 else
|
rlm@0
|
245 {
|
rlm@0
|
246 if(pData->lWaveBuffersCommitted == 0)
|
rlm@0
|
247 {
|
rlm@0
|
248 // Post 'Quit' Message to WaveIn Processor Thread
|
rlm@0
|
249 PostThreadMessage(pData->ulWaveThreadID,WM_QUIT,0,0);
|
rlm@0
|
250 }
|
rlm@0
|
251 }
|
rlm@0
|
252 }
|
rlm@0
|
253
|
rlm@0
|
254 /*
|
rlm@0
|
255 CaptureThreadProc
|
rlm@0
|
256
|
rlm@0
|
257 Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
|
rlm@0
|
258 audio data.
|
rlm@0
|
259 */
|
rlm@0
|
260 static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
|
rlm@0
|
261 {
|
rlm@0
|
262 ALCdevice *pDevice = (ALCdevice*)lpParameter;
|
rlm@0
|
263 WinMMData *pData = pDevice->ExtraData;
|
rlm@0
|
264 LPWAVEHDR pWaveHdr;
|
rlm@0
|
265 ALuint FrameSize;
|
rlm@0
|
266 MSG msg;
|
rlm@0
|
267
|
rlm@0
|
268 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
|
rlm@0
|
269
|
rlm@0
|
270 while(GetMessage(&msg, NULL, 0, 0))
|
rlm@0
|
271 {
|
rlm@0
|
272 if(msg.message != WIM_DATA || pData->bWaveShutdown)
|
rlm@0
|
273 continue;
|
rlm@0
|
274
|
rlm@0
|
275 pWaveHdr = ((LPWAVEHDR)msg.lParam);
|
rlm@0
|
276
|
rlm@0
|
277 WriteRingBuffer(pData->pRing, (ALubyte*)pWaveHdr->lpData,
|
rlm@0
|
278 pWaveHdr->dwBytesRecorded/FrameSize);
|
rlm@0
|
279
|
rlm@0
|
280 // Send buffer back to capture more data
|
rlm@0
|
281 waveInAddBuffer(pData->hWaveHandle.In,pWaveHdr,sizeof(WAVEHDR));
|
rlm@0
|
282 InterlockedIncrement(&pData->lWaveBuffersCommitted);
|
rlm@0
|
283 }
|
rlm@0
|
284
|
rlm@0
|
285 // Signal Wave Thread completed event
|
rlm@0
|
286 if(pData->hWaveThreadEvent)
|
rlm@0
|
287 SetEvent(pData->hWaveThreadEvent);
|
rlm@0
|
288
|
rlm@0
|
289 ExitThread(0);
|
rlm@0
|
290
|
rlm@0
|
291 return 0;
|
rlm@0
|
292 }
|
rlm@0
|
293
|
rlm@0
|
294
|
rlm@0
|
295 static ALCboolean WinMMOpenPlayback(ALCdevice *pDevice, const ALCchar *deviceName)
|
rlm@0
|
296 {
|
rlm@0
|
297 WAVEFORMATEX wfexFormat;
|
rlm@0
|
298 WinMMData *pData = NULL;
|
rlm@0
|
299 UINT lDeviceID = 0;
|
rlm@0
|
300 MMRESULT res;
|
rlm@0
|
301 ALuint i = 0;
|
rlm@0
|
302
|
rlm@0
|
303 // Find the Device ID matching the deviceName if valid
|
rlm@0
|
304 if(!deviceName || strcmp(deviceName, woDefault) == 0)
|
rlm@0
|
305 lDeviceID = WAVE_MAPPER;
|
rlm@0
|
306 else
|
rlm@0
|
307 {
|
rlm@0
|
308 if(!PlaybackDeviceList)
|
rlm@0
|
309 ProbePlaybackDevices();
|
rlm@0
|
310
|
rlm@0
|
311 for(i = 0;i < NumPlaybackDevices;i++)
|
rlm@0
|
312 {
|
rlm@0
|
313 if(PlaybackDeviceList[i] &&
|
rlm@0
|
314 strcmp(deviceName, PlaybackDeviceList[i]) == 0)
|
rlm@0
|
315 {
|
rlm@0
|
316 lDeviceID = i;
|
rlm@0
|
317 break;
|
rlm@0
|
318 }
|
rlm@0
|
319 }
|
rlm@0
|
320 if(i == NumPlaybackDevices)
|
rlm@0
|
321 return ALC_FALSE;
|
rlm@0
|
322 }
|
rlm@0
|
323
|
rlm@0
|
324 pData = calloc(1, sizeof(*pData));
|
rlm@0
|
325 if(!pData)
|
rlm@0
|
326 {
|
rlm@0
|
327 alcSetError(pDevice, ALC_OUT_OF_MEMORY);
|
rlm@0
|
328 return ALC_FALSE;
|
rlm@0
|
329 }
|
rlm@0
|
330 pDevice->ExtraData = pData;
|
rlm@0
|
331
|
rlm@0
|
332 if(pDevice->FmtChans != DevFmtMono)
|
rlm@0
|
333 {
|
rlm@0
|
334 if((pDevice->Flags&DEVICE_CHANNELS_REQUEST) &&
|
rlm@0
|
335 pDevice->FmtChans != DevFmtStereo)
|
rlm@0
|
336 {
|
rlm@0
|
337 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice->FmtChans));
|
rlm@0
|
338 pDevice->Flags &= ~DEVICE_CHANNELS_REQUEST;
|
rlm@0
|
339 }
|
rlm@0
|
340 pDevice->FmtChans = DevFmtStereo;
|
rlm@0
|
341 }
|
rlm@0
|
342 switch(pDevice->FmtType)
|
rlm@0
|
343 {
|
rlm@0
|
344 case DevFmtByte:
|
rlm@0
|
345 pDevice->FmtType = DevFmtUByte;
|
rlm@0
|
346 break;
|
rlm@0
|
347 case DevFmtUShort:
|
rlm@0
|
348 case DevFmtFloat:
|
rlm@0
|
349 pDevice->FmtType = DevFmtShort;
|
rlm@0
|
350 break;
|
rlm@0
|
351 case DevFmtUByte:
|
rlm@0
|
352 case DevFmtShort:
|
rlm@0
|
353 break;
|
rlm@0
|
354 }
|
rlm@0
|
355
|
rlm@0
|
356 memset(&wfexFormat, 0, sizeof(WAVEFORMATEX));
|
rlm@0
|
357 wfexFormat.wFormatTag = WAVE_FORMAT_PCM;
|
rlm@0
|
358 wfexFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans);
|
rlm@0
|
359 wfexFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8;
|
rlm@0
|
360 wfexFormat.nBlockAlign = wfexFormat.wBitsPerSample *
|
rlm@0
|
361 wfexFormat.nChannels / 8;
|
rlm@0
|
362 wfexFormat.nSamplesPerSec = pDevice->Frequency;
|
rlm@0
|
363 wfexFormat.nAvgBytesPerSec = wfexFormat.nSamplesPerSec *
|
rlm@0
|
364 wfexFormat.nBlockAlign;
|
rlm@0
|
365 wfexFormat.cbSize = 0;
|
rlm@0
|
366
|
rlm@0
|
367 if((res=waveOutOpen(&pData->hWaveHandle.Out, lDeviceID, &wfexFormat, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
|
rlm@0
|
368 {
|
rlm@0
|
369 ERR("waveOutOpen failed: %u\n", res);
|
rlm@0
|
370 goto failure;
|
rlm@0
|
371 }
|
rlm@0
|
372
|
rlm@0
|
373 pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
rlm@0
|
374 if(pData->hWaveThreadEvent == NULL)
|
rlm@0
|
375 {
|
rlm@0
|
376 ERR("CreateEvent failed: %lu\n", GetLastError());
|
rlm@0
|
377 goto failure;
|
rlm@0
|
378 }
|
rlm@0
|
379
|
rlm@0
|
380 pData->Frequency = pDevice->Frequency;
|
rlm@0
|
381
|
rlm@0
|
382 pDevice->szDeviceName = strdup((lDeviceID==WAVE_MAPPER) ? woDefault :
|
rlm@0
|
383 PlaybackDeviceList[lDeviceID]);
|
rlm@0
|
384 return ALC_TRUE;
|
rlm@0
|
385
|
rlm@0
|
386 failure:
|
rlm@0
|
387 if(pData->hWaveThreadEvent)
|
rlm@0
|
388 CloseHandle(pData->hWaveThreadEvent);
|
rlm@0
|
389
|
rlm@0
|
390 if(pData->hWaveHandle.Out)
|
rlm@0
|
391 waveOutClose(pData->hWaveHandle.Out);
|
rlm@0
|
392
|
rlm@0
|
393 free(pData);
|
rlm@0
|
394 pDevice->ExtraData = NULL;
|
rlm@0
|
395 return ALC_FALSE;
|
rlm@0
|
396 }
|
rlm@0
|
397
|
rlm@0
|
398 static void WinMMClosePlayback(ALCdevice *device)
|
rlm@0
|
399 {
|
rlm@0
|
400 WinMMData *pData = (WinMMData*)device->ExtraData;
|
rlm@0
|
401
|
rlm@0
|
402 // Close the Wave device
|
rlm@0
|
403 CloseHandle(pData->hWaveThreadEvent);
|
rlm@0
|
404 pData->hWaveThreadEvent = 0;
|
rlm@0
|
405
|
rlm@0
|
406 waveOutClose(pData->hWaveHandle.Out);
|
rlm@0
|
407 pData->hWaveHandle.Out = 0;
|
rlm@0
|
408
|
rlm@0
|
409 free(pData);
|
rlm@0
|
410 device->ExtraData = NULL;
|
rlm@0
|
411 }
|
rlm@0
|
412
|
rlm@0
|
413 static ALCboolean WinMMResetPlayback(ALCdevice *device)
|
rlm@0
|
414 {
|
rlm@0
|
415 WinMMData *pData = (WinMMData*)device->ExtraData;
|
rlm@0
|
416 ALbyte *BufferData;
|
rlm@0
|
417 ALint lBufferSize;
|
rlm@0
|
418 ALuint i;
|
rlm@0
|
419
|
rlm@0
|
420 pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &pData->ulWaveThreadID);
|
rlm@0
|
421 if(pData->hWaveThread == NULL)
|
rlm@0
|
422 return ALC_FALSE;
|
rlm@0
|
423
|
rlm@0
|
424 device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
|
rlm@0
|
425 pData->Frequency / device->Frequency);
|
rlm@0
|
426 if(device->Frequency != pData->Frequency)
|
rlm@0
|
427 {
|
rlm@0
|
428 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
|
rlm@0
|
429 ERR("WinMM does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, pData->Frequency);
|
rlm@0
|
430 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
|
rlm@0
|
431 device->Frequency = pData->Frequency;
|
rlm@0
|
432 }
|
rlm@0
|
433
|
rlm@0
|
434 SetDefaultWFXChannelOrder(device);
|
rlm@0
|
435
|
rlm@0
|
436 pData->lWaveBuffersCommitted = 0;
|
rlm@0
|
437
|
rlm@0
|
438 // Create 4 Buffers
|
rlm@0
|
439 lBufferSize = device->UpdateSize*device->NumUpdates / 4;
|
rlm@0
|
440 lBufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
rlm@0
|
441
|
rlm@0
|
442 BufferData = calloc(4, lBufferSize);
|
rlm@0
|
443 for(i = 0;i < 4;i++)
|
rlm@0
|
444 {
|
rlm@0
|
445 memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR));
|
rlm@0
|
446 pData->WaveBuffer[i].dwBufferLength = lBufferSize;
|
rlm@0
|
447 pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
|
rlm@0
|
448 (pData->WaveBuffer[i-1].lpData +
|
rlm@0
|
449 pData->WaveBuffer[i-1].dwBufferLength));
|
rlm@0
|
450 waveOutPrepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
rlm@0
|
451 waveOutWrite(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
rlm@0
|
452 InterlockedIncrement(&pData->lWaveBuffersCommitted);
|
rlm@0
|
453 }
|
rlm@0
|
454
|
rlm@0
|
455 return ALC_TRUE;
|
rlm@0
|
456 }
|
rlm@0
|
457
|
rlm@0
|
458 static void WinMMStopPlayback(ALCdevice *device)
|
rlm@0
|
459 {
|
rlm@0
|
460 WinMMData *pData = (WinMMData*)device->ExtraData;
|
rlm@0
|
461 void *buffer = NULL;
|
rlm@0
|
462 int i;
|
rlm@0
|
463
|
rlm@0
|
464 if(pData->hWaveThread == NULL)
|
rlm@0
|
465 return;
|
rlm@0
|
466
|
rlm@0
|
467 // Set flag to stop processing headers
|
rlm@0
|
468 pData->bWaveShutdown = AL_TRUE;
|
rlm@0
|
469
|
rlm@0
|
470 // Wait for signal that Wave Thread has been destroyed
|
rlm@0
|
471 WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE);
|
rlm@0
|
472
|
rlm@0
|
473 CloseHandle(pData->hWaveThread);
|
rlm@0
|
474 pData->hWaveThread = 0;
|
rlm@0
|
475
|
rlm@0
|
476 pData->bWaveShutdown = AL_FALSE;
|
rlm@0
|
477
|
rlm@0
|
478 // Release the wave buffers
|
rlm@0
|
479 for(i = 0;i < 4;i++)
|
rlm@0
|
480 {
|
rlm@0
|
481 waveOutUnprepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
rlm@0
|
482 if(i == 0) buffer = pData->WaveBuffer[i].lpData;
|
rlm@0
|
483 pData->WaveBuffer[i].lpData = NULL;
|
rlm@0
|
484 }
|
rlm@0
|
485 free(buffer);
|
rlm@0
|
486 }
|
rlm@0
|
487
|
rlm@0
|
488
|
rlm@0
|
489 static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
|
rlm@0
|
490 {
|
rlm@0
|
491 WAVEFORMATEX wfexCaptureFormat;
|
rlm@0
|
492 DWORD ulCapturedDataSize;
|
rlm@0
|
493 WinMMData *pData = NULL;
|
rlm@0
|
494 UINT lDeviceID = 0;
|
rlm@0
|
495 ALbyte *BufferData;
|
rlm@0
|
496 ALint lBufferSize;
|
rlm@0
|
497 MMRESULT res;
|
rlm@0
|
498 ALuint i;
|
rlm@0
|
499
|
rlm@0
|
500 if(!CaptureDeviceList)
|
rlm@0
|
501 ProbeCaptureDevices();
|
rlm@0
|
502
|
rlm@0
|
503 // Find the Device ID matching the deviceName if valid
|
rlm@0
|
504 if(deviceName)
|
rlm@0
|
505 {
|
rlm@0
|
506 for(i = 0;i < NumCaptureDevices;i++)
|
rlm@0
|
507 {
|
rlm@0
|
508 if(CaptureDeviceList[i] &&
|
rlm@0
|
509 strcmp(deviceName, CaptureDeviceList[i]) == 0)
|
rlm@0
|
510 {
|
rlm@0
|
511 lDeviceID = i;
|
rlm@0
|
512 break;
|
rlm@0
|
513 }
|
rlm@0
|
514 }
|
rlm@0
|
515 }
|
rlm@0
|
516 else
|
rlm@0
|
517 {
|
rlm@0
|
518 for(i = 0;i < NumCaptureDevices;i++)
|
rlm@0
|
519 {
|
rlm@0
|
520 if(CaptureDeviceList[i])
|
rlm@0
|
521 {
|
rlm@0
|
522 lDeviceID = i;
|
rlm@0
|
523 break;
|
rlm@0
|
524 }
|
rlm@0
|
525 }
|
rlm@0
|
526 }
|
rlm@0
|
527 if(i == NumCaptureDevices)
|
rlm@0
|
528 return ALC_FALSE;
|
rlm@0
|
529
|
rlm@0
|
530 pData = calloc(1, sizeof(*pData));
|
rlm@0
|
531 if(!pData)
|
rlm@0
|
532 {
|
rlm@0
|
533 alcSetError(pDevice, ALC_OUT_OF_MEMORY);
|
rlm@0
|
534 return ALC_FALSE;
|
rlm@0
|
535 }
|
rlm@0
|
536 pDevice->ExtraData = pData;
|
rlm@0
|
537
|
rlm@0
|
538 if((pDevice->FmtChans != DevFmtMono && pDevice->FmtChans != DevFmtStereo) ||
|
rlm@0
|
539 (pDevice->FmtType != DevFmtUByte && pDevice->FmtType != DevFmtShort))
|
rlm@0
|
540 {
|
rlm@0
|
541 alcSetError(pDevice, ALC_INVALID_ENUM);
|
rlm@0
|
542 goto failure;
|
rlm@0
|
543 }
|
rlm@0
|
544
|
rlm@0
|
545 memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
|
rlm@0
|
546 wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
|
rlm@0
|
547 wfexCaptureFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans);
|
rlm@0
|
548 wfexCaptureFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8;
|
rlm@0
|
549 wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
|
rlm@0
|
550 wfexCaptureFormat.nChannels / 8;
|
rlm@0
|
551 wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
|
rlm@0
|
552 wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
|
rlm@0
|
553 wfexCaptureFormat.nBlockAlign;
|
rlm@0
|
554 wfexCaptureFormat.cbSize = 0;
|
rlm@0
|
555
|
rlm@0
|
556 if((res=waveInOpen(&pData->hWaveHandle.In, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
|
rlm@0
|
557 {
|
rlm@0
|
558 ERR("waveInOpen failed: %u\n", res);
|
rlm@0
|
559 goto failure;
|
rlm@0
|
560 }
|
rlm@0
|
561
|
rlm@0
|
562 pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
rlm@0
|
563 if(pData->hWaveThreadEvent == NULL)
|
rlm@0
|
564 {
|
rlm@0
|
565 ERR("CreateEvent failed: %lu\n", GetLastError());
|
rlm@0
|
566 goto failure;
|
rlm@0
|
567 }
|
rlm@0
|
568
|
rlm@0
|
569 pData->Frequency = pDevice->Frequency;
|
rlm@0
|
570
|
rlm@0
|
571 // Allocate circular memory buffer for the captured audio
|
rlm@0
|
572 ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates;
|
rlm@0
|
573
|
rlm@0
|
574 // Make sure circular buffer is at least 100ms in size
|
rlm@0
|
575 if(ulCapturedDataSize < (wfexCaptureFormat.nSamplesPerSec / 10))
|
rlm@0
|
576 ulCapturedDataSize = wfexCaptureFormat.nSamplesPerSec / 10;
|
rlm@0
|
577
|
rlm@0
|
578 pData->pRing = CreateRingBuffer(wfexCaptureFormat.nBlockAlign, ulCapturedDataSize);
|
rlm@0
|
579 if(!pData->pRing)
|
rlm@0
|
580 goto failure;
|
rlm@0
|
581
|
rlm@0
|
582 pData->lWaveBuffersCommitted = 0;
|
rlm@0
|
583
|
rlm@0
|
584 // Create 4 Buffers of 50ms each
|
rlm@0
|
585 lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
|
rlm@0
|
586 lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
|
rlm@0
|
587
|
rlm@0
|
588 BufferData = calloc(4, lBufferSize);
|
rlm@0
|
589 if(!BufferData)
|
rlm@0
|
590 goto failure;
|
rlm@0
|
591
|
rlm@0
|
592 for(i = 0;i < 4;i++)
|
rlm@0
|
593 {
|
rlm@0
|
594 memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR));
|
rlm@0
|
595 pData->WaveBuffer[i].dwBufferLength = lBufferSize;
|
rlm@0
|
596 pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
|
rlm@0
|
597 (pData->WaveBuffer[i-1].lpData +
|
rlm@0
|
598 pData->WaveBuffer[i-1].dwBufferLength));
|
rlm@0
|
599 pData->WaveBuffer[i].dwFlags = 0;
|
rlm@0
|
600 pData->WaveBuffer[i].dwLoops = 0;
|
rlm@0
|
601 waveInPrepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
rlm@0
|
602 waveInAddBuffer(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
rlm@0
|
603 InterlockedIncrement(&pData->lWaveBuffersCommitted);
|
rlm@0
|
604 }
|
rlm@0
|
605
|
rlm@0
|
606 pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveThreadID);
|
rlm@0
|
607 if (pData->hWaveThread == NULL)
|
rlm@0
|
608 goto failure;
|
rlm@0
|
609
|
rlm@0
|
610 pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
|
rlm@0
|
611 return ALC_TRUE;
|
rlm@0
|
612
|
rlm@0
|
613 failure:
|
rlm@0
|
614 if(pData->hWaveThread)
|
rlm@0
|
615 CloseHandle(pData->hWaveThread);
|
rlm@0
|
616
|
rlm@0
|
617 for(i = 0;i < 4;i++)
|
rlm@0
|
618 {
|
rlm@0
|
619 if(pData->WaveBuffer[i].lpData)
|
rlm@0
|
620 {
|
rlm@0
|
621 waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
rlm@0
|
622 if(i == 0)
|
rlm@0
|
623 free(pData->WaveBuffer[i].lpData);
|
rlm@0
|
624 }
|
rlm@0
|
625 }
|
rlm@0
|
626
|
rlm@0
|
627 if(pData->pRing)
|
rlm@0
|
628 DestroyRingBuffer(pData->pRing);
|
rlm@0
|
629
|
rlm@0
|
630 if(pData->hWaveThreadEvent)
|
rlm@0
|
631 CloseHandle(pData->hWaveThreadEvent);
|
rlm@0
|
632
|
rlm@0
|
633 if(pData->hWaveHandle.In)
|
rlm@0
|
634 waveInClose(pData->hWaveHandle.In);
|
rlm@0
|
635
|
rlm@0
|
636 free(pData);
|
rlm@0
|
637 pDevice->ExtraData = NULL;
|
rlm@0
|
638 return ALC_FALSE;
|
rlm@0
|
639 }
|
rlm@0
|
640
|
rlm@0
|
641 static void WinMMCloseCapture(ALCdevice *pDevice)
|
rlm@0
|
642 {
|
rlm@0
|
643 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
rlm@0
|
644 void *buffer = NULL;
|
rlm@0
|
645 int i;
|
rlm@0
|
646
|
rlm@0
|
647 // Call waveOutReset to shutdown wave device
|
rlm@0
|
648 pData->bWaveShutdown = AL_TRUE;
|
rlm@0
|
649 waveInReset(pData->hWaveHandle.In);
|
rlm@0
|
650
|
rlm@0
|
651 // Wait for signal that Wave Thread has been destroyed
|
rlm@0
|
652 WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE);
|
rlm@0
|
653
|
rlm@0
|
654 CloseHandle(pData->hWaveThread);
|
rlm@0
|
655 pData->hWaveThread = 0;
|
rlm@0
|
656
|
rlm@0
|
657 // Release the wave buffers
|
rlm@0
|
658 for(i = 0;i < 4;i++)
|
rlm@0
|
659 {
|
rlm@0
|
660 waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
|
rlm@0
|
661 if(i == 0) buffer = pData->WaveBuffer[i].lpData;
|
rlm@0
|
662 pData->WaveBuffer[i].lpData = NULL;
|
rlm@0
|
663 }
|
rlm@0
|
664 free(buffer);
|
rlm@0
|
665
|
rlm@0
|
666 DestroyRingBuffer(pData->pRing);
|
rlm@0
|
667 pData->pRing = NULL;
|
rlm@0
|
668
|
rlm@0
|
669 // Close the Wave device
|
rlm@0
|
670 CloseHandle(pData->hWaveThreadEvent);
|
rlm@0
|
671 pData->hWaveThreadEvent = 0;
|
rlm@0
|
672
|
rlm@0
|
673 waveInClose(pData->hWaveHandle.In);
|
rlm@0
|
674 pData->hWaveHandle.In = 0;
|
rlm@0
|
675
|
rlm@0
|
676 free(pData);
|
rlm@0
|
677 pDevice->ExtraData = NULL;
|
rlm@0
|
678 }
|
rlm@0
|
679
|
rlm@0
|
680 static void WinMMStartCapture(ALCdevice *pDevice)
|
rlm@0
|
681 {
|
rlm@0
|
682 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
rlm@0
|
683 waveInStart(pData->hWaveHandle.In);
|
rlm@0
|
684 }
|
rlm@0
|
685
|
rlm@0
|
686 static void WinMMStopCapture(ALCdevice *pDevice)
|
rlm@0
|
687 {
|
rlm@0
|
688 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
rlm@0
|
689 waveInStop(pData->hWaveHandle.In);
|
rlm@0
|
690 }
|
rlm@0
|
691
|
rlm@0
|
692 static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
|
rlm@0
|
693 {
|
rlm@0
|
694 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
rlm@0
|
695 return RingBufferSize(pData->pRing);
|
rlm@0
|
696 }
|
rlm@0
|
697
|
rlm@0
|
698 static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
rlm@0
|
699 {
|
rlm@0
|
700 WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
rlm@0
|
701
|
rlm@0
|
702 if(WinMMAvailableSamples(pDevice) >= lSamples)
|
rlm@0
|
703 ReadRingBuffer(pData->pRing, pBuffer, lSamples);
|
rlm@0
|
704 else
|
rlm@0
|
705 alcSetError(pDevice, ALC_INVALID_VALUE);
|
rlm@0
|
706 }
|
rlm@0
|
707
|
rlm@0
|
708
|
rlm@0
|
709 static const BackendFuncs WinMMFuncs = {
|
rlm@0
|
710 WinMMOpenPlayback,
|
rlm@0
|
711 WinMMClosePlayback,
|
rlm@0
|
712 WinMMResetPlayback,
|
rlm@0
|
713 WinMMStopPlayback,
|
rlm@0
|
714 WinMMOpenCapture,
|
rlm@0
|
715 WinMMCloseCapture,
|
rlm@0
|
716 WinMMStartCapture,
|
rlm@0
|
717 WinMMStopCapture,
|
rlm@0
|
718 WinMMCaptureSamples,
|
rlm@0
|
719 WinMMAvailableSamples
|
rlm@0
|
720 };
|
rlm@0
|
721
|
rlm@0
|
722 ALCboolean alcWinMMInit(BackendFuncs *FuncList)
|
rlm@0
|
723 {
|
rlm@0
|
724 *FuncList = WinMMFuncs;
|
rlm@0
|
725 return ALC_TRUE;
|
rlm@0
|
726 }
|
rlm@0
|
727
|
rlm@0
|
728 void alcWinMMDeinit()
|
rlm@0
|
729 {
|
rlm@0
|
730 ALuint lLoop;
|
rlm@0
|
731
|
rlm@0
|
732 for(lLoop = 0;lLoop < NumPlaybackDevices;lLoop++)
|
rlm@0
|
733 free(PlaybackDeviceList[lLoop]);
|
rlm@0
|
734 free(PlaybackDeviceList);
|
rlm@0
|
735 PlaybackDeviceList = NULL;
|
rlm@0
|
736
|
rlm@0
|
737 NumPlaybackDevices = 0;
|
rlm@0
|
738
|
rlm@0
|
739
|
rlm@0
|
740 for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
|
rlm@0
|
741 free(CaptureDeviceList[lLoop]);
|
rlm@0
|
742 free(CaptureDeviceList);
|
rlm@0
|
743 CaptureDeviceList = NULL;
|
rlm@0
|
744
|
rlm@0
|
745 NumCaptureDevices = 0;
|
rlm@0
|
746 }
|
rlm@0
|
747
|
rlm@0
|
748 void alcWinMMProbe(enum DevProbe type)
|
rlm@0
|
749 {
|
rlm@0
|
750 ALuint i;
|
rlm@0
|
751
|
rlm@0
|
752 switch(type)
|
rlm@0
|
753 {
|
rlm@0
|
754 case DEVICE_PROBE:
|
rlm@0
|
755 ProbePlaybackDevices();
|
rlm@0
|
756 if(NumPlaybackDevices > 0)
|
rlm@0
|
757 AppendDeviceList(woDefault);
|
rlm@0
|
758 break;
|
rlm@0
|
759
|
rlm@0
|
760 case ALL_DEVICE_PROBE:
|
rlm@0
|
761 ProbePlaybackDevices();
|
rlm@0
|
762 if(NumPlaybackDevices > 0)
|
rlm@0
|
763 AppendAllDeviceList(woDefault);
|
rlm@0
|
764 for(i = 0;i < NumPlaybackDevices;i++)
|
rlm@0
|
765 {
|
rlm@0
|
766 if(PlaybackDeviceList[i])
|
rlm@0
|
767 AppendAllDeviceList(PlaybackDeviceList[i]);
|
rlm@0
|
768 }
|
rlm@0
|
769 break;
|
rlm@0
|
770
|
rlm@0
|
771 case CAPTURE_DEVICE_PROBE:
|
rlm@0
|
772 ProbeCaptureDevices();
|
rlm@0
|
773 for(i = 0;i < NumCaptureDevices;i++)
|
rlm@0
|
774 {
|
rlm@0
|
775 if(CaptureDeviceList[i])
|
rlm@0
|
776 AppendCaptureDeviceList(CaptureDeviceList[i]);
|
rlm@0
|
777 }
|
rlm@0
|
778 break;
|
rlm@0
|
779 }
|
rlm@0
|
780 }
|