rlm@0
|
1 /**
|
rlm@0
|
2 * OpenAL cross platform audio library
|
rlm@0
|
3 * Copyright (C) 2011 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 COBJMACROS
|
rlm@0
|
24 #define _WIN32_WINNT 0x0500
|
rlm@0
|
25 #include <stdlib.h>
|
rlm@0
|
26 #include <stdio.h>
|
rlm@0
|
27 #include <memory.h>
|
rlm@0
|
28
|
rlm@0
|
29 #include <mmdeviceapi.h>
|
rlm@0
|
30 #include <audioclient.h>
|
rlm@0
|
31 #include <cguid.h>
|
rlm@0
|
32 #include <mmreg.h>
|
rlm@0
|
33 #ifndef _WAVEFORMATEXTENSIBLE_
|
rlm@0
|
34 #include <ks.h>
|
rlm@0
|
35 #include <ksmedia.h>
|
rlm@0
|
36 #endif
|
rlm@0
|
37
|
rlm@0
|
38 #include "alMain.h"
|
rlm@0
|
39 #include "AL/al.h"
|
rlm@0
|
40 #include "AL/alc.h"
|
rlm@0
|
41
|
rlm@0
|
42
|
rlm@0
|
43 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
rlm@0
|
44 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
rlm@0
|
45
|
rlm@0
|
46 #define MONO SPEAKER_FRONT_CENTER
|
rlm@0
|
47 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
|
rlm@0
|
48 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
|
rlm@0
|
49 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
|
rlm@0
|
50 #define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
|
rlm@0
|
51 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
|
rlm@0
|
52 #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
|
rlm@0
|
53
|
rlm@0
|
54
|
rlm@0
|
55 typedef struct {
|
rlm@0
|
56 IMMDevice *mmdev;
|
rlm@0
|
57 IAudioClient *client;
|
rlm@0
|
58 HANDLE hNotifyEvent;
|
rlm@0
|
59
|
rlm@0
|
60 HANDLE MsgEvent;
|
rlm@0
|
61
|
rlm@0
|
62 volatile int killNow;
|
rlm@0
|
63 ALvoid *thread;
|
rlm@0
|
64 } MMDevApiData;
|
rlm@0
|
65
|
rlm@0
|
66
|
rlm@0
|
67 static const ALCchar mmDevice[] = "WASAPI Default";
|
rlm@0
|
68
|
rlm@0
|
69
|
rlm@0
|
70 static HANDLE ThreadHdl;
|
rlm@0
|
71 static DWORD ThreadID;
|
rlm@0
|
72
|
rlm@0
|
73 typedef struct {
|
rlm@0
|
74 HANDLE FinishedEvt;
|
rlm@0
|
75 HRESULT result;
|
rlm@0
|
76 } ThreadRequest;
|
rlm@0
|
77
|
rlm@0
|
78 #define WM_USER_OpenDevice (WM_USER+0)
|
rlm@0
|
79 #define WM_USER_ResetDevice (WM_USER+1)
|
rlm@0
|
80 #define WM_USER_StopDevice (WM_USER+2)
|
rlm@0
|
81 #define WM_USER_CloseDevice (WM_USER+3)
|
rlm@0
|
82
|
rlm@0
|
83 static HRESULT WaitForResponse(ThreadRequest *req)
|
rlm@0
|
84 {
|
rlm@0
|
85 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
|
rlm@0
|
86 return req->result;
|
rlm@0
|
87 ERR("Message response error: %lu\n", GetLastError());
|
rlm@0
|
88 return E_FAIL;
|
rlm@0
|
89 }
|
rlm@0
|
90
|
rlm@0
|
91
|
rlm@0
|
92 static ALuint MMDevApiProc(ALvoid *ptr)
|
rlm@0
|
93 {
|
rlm@0
|
94 ALCdevice *device = ptr;
|
rlm@0
|
95 MMDevApiData *data = device->ExtraData;
|
rlm@0
|
96 union {
|
rlm@0
|
97 IAudioRenderClient *iface;
|
rlm@0
|
98 void *ptr;
|
rlm@0
|
99 } render;
|
rlm@0
|
100 UINT32 written, len;
|
rlm@0
|
101 BYTE *buffer;
|
rlm@0
|
102 HRESULT hr;
|
rlm@0
|
103
|
rlm@0
|
104 hr = CoInitialize(NULL);
|
rlm@0
|
105 if(FAILED(hr))
|
rlm@0
|
106 {
|
rlm@0
|
107 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
|
rlm@0
|
108 aluHandleDisconnect(device);
|
rlm@0
|
109 return 0;
|
rlm@0
|
110 }
|
rlm@0
|
111
|
rlm@0
|
112 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr);
|
rlm@0
|
113 if(FAILED(hr))
|
rlm@0
|
114 {
|
rlm@0
|
115 ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr);
|
rlm@0
|
116 aluHandleDisconnect(device);
|
rlm@0
|
117 return 0;
|
rlm@0
|
118 }
|
rlm@0
|
119
|
rlm@0
|
120 SetRTPriority();
|
rlm@0
|
121
|
rlm@0
|
122 while(!data->killNow)
|
rlm@0
|
123 {
|
rlm@0
|
124 hr = IAudioClient_GetCurrentPadding(data->client, &written);
|
rlm@0
|
125 if(FAILED(hr))
|
rlm@0
|
126 {
|
rlm@0
|
127 ERR("Failed to get padding: 0x%08lx\n", hr);
|
rlm@0
|
128 aluHandleDisconnect(device);
|
rlm@0
|
129 break;
|
rlm@0
|
130 }
|
rlm@0
|
131
|
rlm@0
|
132 len = device->UpdateSize*device->NumUpdates - written;
|
rlm@0
|
133 if(len < device->UpdateSize)
|
rlm@0
|
134 {
|
rlm@0
|
135 DWORD res;
|
rlm@0
|
136 res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE);
|
rlm@0
|
137 if(res != WAIT_OBJECT_0)
|
rlm@0
|
138 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
|
rlm@0
|
139 continue;
|
rlm@0
|
140 }
|
rlm@0
|
141 len -= len%device->UpdateSize;
|
rlm@0
|
142
|
rlm@0
|
143 hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer);
|
rlm@0
|
144 if(SUCCEEDED(hr))
|
rlm@0
|
145 {
|
rlm@0
|
146 aluMixData(device, buffer, len);
|
rlm@0
|
147 hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0);
|
rlm@0
|
148 }
|
rlm@0
|
149 if(FAILED(hr))
|
rlm@0
|
150 {
|
rlm@0
|
151 ERR("Failed to buffer data: 0x%08lx\n", hr);
|
rlm@0
|
152 aluHandleDisconnect(device);
|
rlm@0
|
153 break;
|
rlm@0
|
154 }
|
rlm@0
|
155 }
|
rlm@0
|
156
|
rlm@0
|
157 IAudioRenderClient_Release(render.iface);
|
rlm@0
|
158
|
rlm@0
|
159 CoUninitialize();
|
rlm@0
|
160 return 0;
|
rlm@0
|
161 }
|
rlm@0
|
162
|
rlm@0
|
163
|
rlm@0
|
164 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
|
rlm@0
|
165 {
|
rlm@0
|
166 memset(out, 0, sizeof(*out));
|
rlm@0
|
167 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
|
rlm@0
|
168 *out = *(WAVEFORMATEXTENSIBLE*)in;
|
rlm@0
|
169 else if(in->wFormatTag == WAVE_FORMAT_PCM)
|
rlm@0
|
170 {
|
rlm@0
|
171 out->Format = *in;
|
rlm@0
|
172 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
rlm@0
|
173 out->Format.cbSize = sizeof(*out) - sizeof(*in);
|
rlm@0
|
174 if(out->Format.nChannels == 1)
|
rlm@0
|
175 out->dwChannelMask = MONO;
|
rlm@0
|
176 else if(out->Format.nChannels == 2)
|
rlm@0
|
177 out->dwChannelMask = STEREO;
|
rlm@0
|
178 else
|
rlm@0
|
179 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
|
rlm@0
|
180 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
rlm@0
|
181 }
|
rlm@0
|
182 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
|
rlm@0
|
183 {
|
rlm@0
|
184 out->Format = *in;
|
rlm@0
|
185 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
rlm@0
|
186 out->Format.cbSize = sizeof(*out) - sizeof(*in);
|
rlm@0
|
187 if(out->Format.nChannels == 1)
|
rlm@0
|
188 out->dwChannelMask = MONO;
|
rlm@0
|
189 else if(out->Format.nChannels == 2)
|
rlm@0
|
190 out->dwChannelMask = STEREO;
|
rlm@0
|
191 else
|
rlm@0
|
192 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
|
rlm@0
|
193 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
rlm@0
|
194 }
|
rlm@0
|
195 else
|
rlm@0
|
196 {
|
rlm@0
|
197 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
|
rlm@0
|
198 return ALC_FALSE;
|
rlm@0
|
199 }
|
rlm@0
|
200 return ALC_TRUE;
|
rlm@0
|
201 }
|
rlm@0
|
202
|
rlm@0
|
203 static HRESULT DoReset(ALCdevice *device)
|
rlm@0
|
204 {
|
rlm@0
|
205 MMDevApiData *data = device->ExtraData;
|
rlm@0
|
206 WAVEFORMATEXTENSIBLE OutputType;
|
rlm@0
|
207 WAVEFORMATEX *wfx = NULL;
|
rlm@0
|
208 REFERENCE_TIME min_per;
|
rlm@0
|
209 UINT32 buffer_len, min_len;
|
rlm@0
|
210 HRESULT hr;
|
rlm@0
|
211
|
rlm@0
|
212 hr = IAudioClient_GetMixFormat(data->client, &wfx);
|
rlm@0
|
213 if(FAILED(hr))
|
rlm@0
|
214 {
|
rlm@0
|
215 ERR("Failed to get mix format: 0x%08lx\n", hr);
|
rlm@0
|
216 return hr;
|
rlm@0
|
217 }
|
rlm@0
|
218
|
rlm@0
|
219 if(!MakeExtensible(&OutputType, wfx))
|
rlm@0
|
220 {
|
rlm@0
|
221 CoTaskMemFree(wfx);
|
rlm@0
|
222 return E_FAIL;
|
rlm@0
|
223 }
|
rlm@0
|
224 CoTaskMemFree(wfx);
|
rlm@0
|
225 wfx = NULL;
|
rlm@0
|
226
|
rlm@0
|
227 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
|
rlm@0
|
228 device->Frequency = OutputType.Format.nSamplesPerSec;
|
rlm@0
|
229 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
|
rlm@0
|
230 {
|
rlm@0
|
231 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
|
rlm@0
|
232 device->FmtChans = DevFmtMono;
|
rlm@0
|
233 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
|
rlm@0
|
234 device->FmtChans = DevFmtStereo;
|
rlm@0
|
235 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
|
rlm@0
|
236 device->FmtChans = DevFmtQuad;
|
rlm@0
|
237 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
|
rlm@0
|
238 device->FmtChans = DevFmtX51;
|
rlm@0
|
239 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
|
rlm@0
|
240 device->FmtChans = DevFmtX51Side;
|
rlm@0
|
241 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
|
rlm@0
|
242 device->FmtChans = DevFmtX61;
|
rlm@0
|
243 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
|
rlm@0
|
244 device->FmtChans = DevFmtX71;
|
rlm@0
|
245 else
|
rlm@0
|
246 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
|
rlm@0
|
247 }
|
rlm@0
|
248
|
rlm@0
|
249 switch(device->FmtChans)
|
rlm@0
|
250 {
|
rlm@0
|
251 case DevFmtMono:
|
rlm@0
|
252 OutputType.Format.nChannels = 1;
|
rlm@0
|
253 OutputType.dwChannelMask = MONO;
|
rlm@0
|
254 break;
|
rlm@0
|
255 case DevFmtStereo:
|
rlm@0
|
256 OutputType.Format.nChannels = 2;
|
rlm@0
|
257 OutputType.dwChannelMask = STEREO;
|
rlm@0
|
258 break;
|
rlm@0
|
259 case DevFmtQuad:
|
rlm@0
|
260 OutputType.Format.nChannels = 4;
|
rlm@0
|
261 OutputType.dwChannelMask = QUAD;
|
rlm@0
|
262 break;
|
rlm@0
|
263 case DevFmtX51:
|
rlm@0
|
264 OutputType.Format.nChannels = 6;
|
rlm@0
|
265 OutputType.dwChannelMask = X5DOT1;
|
rlm@0
|
266 break;
|
rlm@0
|
267 case DevFmtX51Side:
|
rlm@0
|
268 OutputType.Format.nChannels = 6;
|
rlm@0
|
269 OutputType.dwChannelMask = X5DOT1SIDE;
|
rlm@0
|
270 break;
|
rlm@0
|
271 case DevFmtX61:
|
rlm@0
|
272 OutputType.Format.nChannels = 7;
|
rlm@0
|
273 OutputType.dwChannelMask = X6DOT1;
|
rlm@0
|
274 break;
|
rlm@0
|
275 case DevFmtX71:
|
rlm@0
|
276 OutputType.Format.nChannels = 8;
|
rlm@0
|
277 OutputType.dwChannelMask = X7DOT1;
|
rlm@0
|
278 break;
|
rlm@0
|
279 }
|
rlm@0
|
280 switch(device->FmtType)
|
rlm@0
|
281 {
|
rlm@0
|
282 case DevFmtByte:
|
rlm@0
|
283 device->FmtType = DevFmtUByte;
|
rlm@0
|
284 /* fall-through */
|
rlm@0
|
285 case DevFmtUByte:
|
rlm@0
|
286 OutputType.Format.wBitsPerSample = 8;
|
rlm@0
|
287 OutputType.Samples.wValidBitsPerSample = 8;
|
rlm@0
|
288 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
rlm@0
|
289 break;
|
rlm@0
|
290 case DevFmtUShort:
|
rlm@0
|
291 device->FmtType = DevFmtShort;
|
rlm@0
|
292 /* fall-through */
|
rlm@0
|
293 case DevFmtShort:
|
rlm@0
|
294 OutputType.Format.wBitsPerSample = 16;
|
rlm@0
|
295 OutputType.Samples.wValidBitsPerSample = 16;
|
rlm@0
|
296 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
rlm@0
|
297 break;
|
rlm@0
|
298 case DevFmtFloat:
|
rlm@0
|
299 OutputType.Format.wBitsPerSample = 32;
|
rlm@0
|
300 OutputType.Samples.wValidBitsPerSample = 32;
|
rlm@0
|
301 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
rlm@0
|
302 break;
|
rlm@0
|
303 }
|
rlm@0
|
304 OutputType.Format.nSamplesPerSec = device->Frequency;
|
rlm@0
|
305
|
rlm@0
|
306 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
|
rlm@0
|
307 OutputType.Format.wBitsPerSample / 8;
|
rlm@0
|
308 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
|
rlm@0
|
309 OutputType.Format.nBlockAlign;
|
rlm@0
|
310
|
rlm@0
|
311 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
|
rlm@0
|
312 if(FAILED(hr))
|
rlm@0
|
313 {
|
rlm@0
|
314 ERR("Failed to check format support: 0x%08lx\n", hr);
|
rlm@0
|
315 hr = IAudioClient_GetMixFormat(data->client, &wfx);
|
rlm@0
|
316 }
|
rlm@0
|
317 if(FAILED(hr))
|
rlm@0
|
318 {
|
rlm@0
|
319 ERR("Failed to find a supported format: 0x%08lx\n", hr);
|
rlm@0
|
320 return hr;
|
rlm@0
|
321 }
|
rlm@0
|
322
|
rlm@0
|
323 if(wfx != NULL)
|
rlm@0
|
324 {
|
rlm@0
|
325 if(!MakeExtensible(&OutputType, wfx))
|
rlm@0
|
326 {
|
rlm@0
|
327 CoTaskMemFree(wfx);
|
rlm@0
|
328 return E_FAIL;
|
rlm@0
|
329 }
|
rlm@0
|
330 CoTaskMemFree(wfx);
|
rlm@0
|
331 wfx = NULL;
|
rlm@0
|
332
|
rlm@0
|
333 if(device->Frequency != OutputType.Format.nSamplesPerSec)
|
rlm@0
|
334 {
|
rlm@0
|
335 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
|
rlm@0
|
336 ERR("Failed to set %dhz, got %ldhz instead\n", device->Frequency, OutputType.Format.nSamplesPerSec);
|
rlm@0
|
337 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
|
rlm@0
|
338 device->Frequency = OutputType.Format.nSamplesPerSec;
|
rlm@0
|
339 }
|
rlm@0
|
340
|
rlm@0
|
341 if(!((device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) ||
|
rlm@0
|
342 (device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) ||
|
rlm@0
|
343 (device->FmtChans == DevFmtQuad && OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) ||
|
rlm@0
|
344 (device->FmtChans == DevFmtX51 && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) ||
|
rlm@0
|
345 (device->FmtChans == DevFmtX51Side && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) ||
|
rlm@0
|
346 (device->FmtChans == DevFmtX61 && OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) ||
|
rlm@0
|
347 (device->FmtChans == DevFmtX71 && OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)))
|
rlm@0
|
348 {
|
rlm@0
|
349 if((device->Flags&DEVICE_CHANNELS_REQUEST))
|
rlm@0
|
350 ERR("Failed to set %s, got %d channels (0x%08lx) instead\n", DevFmtChannelsString(device->FmtChans), OutputType.Format.nChannels, OutputType.dwChannelMask);
|
rlm@0
|
351 device->Flags &= ~DEVICE_CHANNELS_REQUEST;
|
rlm@0
|
352
|
rlm@0
|
353 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
|
rlm@0
|
354 device->FmtChans = DevFmtMono;
|
rlm@0
|
355 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
|
rlm@0
|
356 device->FmtChans = DevFmtStereo;
|
rlm@0
|
357 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
|
rlm@0
|
358 device->FmtChans = DevFmtQuad;
|
rlm@0
|
359 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
|
rlm@0
|
360 device->FmtChans = DevFmtX51;
|
rlm@0
|
361 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
|
rlm@0
|
362 device->FmtChans = DevFmtX51Side;
|
rlm@0
|
363 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
|
rlm@0
|
364 device->FmtChans = DevFmtX61;
|
rlm@0
|
365 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
|
rlm@0
|
366 device->FmtChans = DevFmtX71;
|
rlm@0
|
367 else
|
rlm@0
|
368 {
|
rlm@0
|
369 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
|
rlm@0
|
370 device->FmtChans = DevFmtStereo;
|
rlm@0
|
371 OutputType.Format.nChannels = 2;
|
rlm@0
|
372 OutputType.dwChannelMask = STEREO;
|
rlm@0
|
373 }
|
rlm@0
|
374 }
|
rlm@0
|
375
|
rlm@0
|
376 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
|
rlm@0
|
377 {
|
rlm@0
|
378 if(OutputType.Samples.wValidBitsPerSample == 0)
|
rlm@0
|
379 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
rlm@0
|
380 if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample ||
|
rlm@0
|
381 !((device->FmtType == DevFmtUByte && OutputType.Format.wBitsPerSample == 8) ||
|
rlm@0
|
382 (device->FmtType == DevFmtShort && OutputType.Format.wBitsPerSample == 16)))
|
rlm@0
|
383 {
|
rlm@0
|
384 ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample);
|
rlm@0
|
385 if(OutputType.Format.wBitsPerSample == 8)
|
rlm@0
|
386 device->FmtType = DevFmtUByte;
|
rlm@0
|
387 else if(OutputType.Format.wBitsPerSample == 16)
|
rlm@0
|
388 device->FmtType = DevFmtShort;
|
rlm@0
|
389 else
|
rlm@0
|
390 {
|
rlm@0
|
391 device->FmtType = DevFmtShort;
|
rlm@0
|
392 OutputType.Format.wBitsPerSample = 16;
|
rlm@0
|
393 }
|
rlm@0
|
394 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
rlm@0
|
395 }
|
rlm@0
|
396 }
|
rlm@0
|
397 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
|
rlm@0
|
398 {
|
rlm@0
|
399 if(OutputType.Samples.wValidBitsPerSample == 0)
|
rlm@0
|
400 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
rlm@0
|
401 if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample ||
|
rlm@0
|
402 !((device->FmtType == DevFmtFloat && OutputType.Format.wBitsPerSample == 32)))
|
rlm@0
|
403 {
|
rlm@0
|
404 ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample);
|
rlm@0
|
405 if(OutputType.Format.wBitsPerSample != 32)
|
rlm@0
|
406 {
|
rlm@0
|
407 device->FmtType = DevFmtFloat;
|
rlm@0
|
408 OutputType.Format.wBitsPerSample = 32;
|
rlm@0
|
409 }
|
rlm@0
|
410 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
rlm@0
|
411 }
|
rlm@0
|
412 }
|
rlm@0
|
413 else
|
rlm@0
|
414 {
|
rlm@0
|
415 ERR("Unhandled format sub-type\n");
|
rlm@0
|
416 device->FmtType = DevFmtShort;
|
rlm@0
|
417 OutputType.Format.wBitsPerSample = 16;
|
rlm@0
|
418 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
rlm@0
|
419 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
rlm@0
|
420 }
|
rlm@0
|
421 }
|
rlm@0
|
422
|
rlm@0
|
423 SetDefaultWFXChannelOrder(device);
|
rlm@0
|
424
|
rlm@0
|
425 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
|
rlm@0
|
426 if(SUCCEEDED(hr))
|
rlm@0
|
427 {
|
rlm@0
|
428 min_len = (min_per*device->Frequency + 10000000-1) / 10000000;
|
rlm@0
|
429 if(min_len < device->UpdateSize)
|
rlm@0
|
430 min_len *= (device->UpdateSize + min_len/2)/min_len;
|
rlm@0
|
431
|
rlm@0
|
432 device->NumUpdates = (device->NumUpdates*device->UpdateSize + min_len/2) /
|
rlm@0
|
433 min_len;
|
rlm@0
|
434 device->NumUpdates = maxu(device->NumUpdates, 2);
|
rlm@0
|
435 device->UpdateSize = min_len;
|
rlm@0
|
436
|
rlm@0
|
437 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
|
rlm@0
|
438 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
rlm@0
|
439 ((REFERENCE_TIME)device->UpdateSize*
|
rlm@0
|
440 device->NumUpdates*10000000 +
|
rlm@0
|
441 device->Frequency-1) / device->Frequency,
|
rlm@0
|
442 0, &OutputType.Format, NULL);
|
rlm@0
|
443 }
|
rlm@0
|
444 if(FAILED(hr))
|
rlm@0
|
445 {
|
rlm@0
|
446 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
|
rlm@0
|
447 return hr;
|
rlm@0
|
448 }
|
rlm@0
|
449
|
rlm@0
|
450 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
|
rlm@0
|
451 if(FAILED(hr))
|
rlm@0
|
452 {
|
rlm@0
|
453 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
|
rlm@0
|
454 return hr;
|
rlm@0
|
455 }
|
rlm@0
|
456
|
rlm@0
|
457 device->NumUpdates = buffer_len / device->UpdateSize;
|
rlm@0
|
458 if(device->NumUpdates <= 1)
|
rlm@0
|
459 {
|
rlm@0
|
460 device->NumUpdates = 1;
|
rlm@0
|
461 ERR("Audio client returned buffer_len < period*2; expect break up\n");
|
rlm@0
|
462 }
|
rlm@0
|
463
|
rlm@0
|
464 ResetEvent(data->hNotifyEvent);
|
rlm@0
|
465 hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent);
|
rlm@0
|
466 if(SUCCEEDED(hr))
|
rlm@0
|
467 hr = IAudioClient_Start(data->client);
|
rlm@0
|
468 if(FAILED(hr))
|
rlm@0
|
469 {
|
rlm@0
|
470 ERR("Failed to start audio client: 0x%08lx\n", hr);
|
rlm@0
|
471 return hr;
|
rlm@0
|
472 }
|
rlm@0
|
473
|
rlm@0
|
474 data->thread = StartThread(MMDevApiProc, device);
|
rlm@0
|
475 if(!data->thread)
|
rlm@0
|
476 {
|
rlm@0
|
477 IAudioClient_Stop(data->client);
|
rlm@0
|
478 ERR("Failed to start thread\n");
|
rlm@0
|
479 return E_FAIL;
|
rlm@0
|
480 }
|
rlm@0
|
481
|
rlm@0
|
482 return hr;
|
rlm@0
|
483 }
|
rlm@0
|
484
|
rlm@0
|
485
|
rlm@0
|
486 static DWORD CALLBACK MessageProc(void *ptr)
|
rlm@0
|
487 {
|
rlm@0
|
488 ThreadRequest *req = ptr;
|
rlm@0
|
489 IMMDeviceEnumerator *Enumerator;
|
rlm@0
|
490 MMDevApiData *data;
|
rlm@0
|
491 ALCdevice *device;
|
rlm@0
|
492 HRESULT hr;
|
rlm@0
|
493 MSG msg;
|
rlm@0
|
494
|
rlm@0
|
495 TRACE("Starting message thread\n");
|
rlm@0
|
496
|
rlm@0
|
497 hr = CoInitialize(NULL);
|
rlm@0
|
498 if(FAILED(hr))
|
rlm@0
|
499 {
|
rlm@0
|
500 WARN("Failed to initialize COM: 0x%08lx\n", hr);
|
rlm@0
|
501 req->result = hr;
|
rlm@0
|
502 SetEvent(req->FinishedEvt);
|
rlm@0
|
503 return 0;
|
rlm@0
|
504 }
|
rlm@0
|
505
|
rlm@0
|
506 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
|
rlm@0
|
507 if(FAILED(hr))
|
rlm@0
|
508 {
|
rlm@0
|
509 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
|
rlm@0
|
510 CoUninitialize();
|
rlm@0
|
511 req->result = hr;
|
rlm@0
|
512 SetEvent(req->FinishedEvt);
|
rlm@0
|
513 return 0;
|
rlm@0
|
514 }
|
rlm@0
|
515 Enumerator = ptr;
|
rlm@0
|
516 IMMDeviceEnumerator_Release(Enumerator);
|
rlm@0
|
517 Enumerator = NULL;
|
rlm@0
|
518
|
rlm@0
|
519 req->result = S_OK;
|
rlm@0
|
520 SetEvent(req->FinishedEvt);
|
rlm@0
|
521
|
rlm@0
|
522 TRACE("Starting message loop\n");
|
rlm@0
|
523 while(GetMessage(&msg, NULL, 0, 0))
|
rlm@0
|
524 {
|
rlm@0
|
525 TRACE("Got message %u\n", msg.message);
|
rlm@0
|
526 switch(msg.message)
|
rlm@0
|
527 {
|
rlm@0
|
528 case WM_USER_OpenDevice:
|
rlm@0
|
529 req = (ThreadRequest*)msg.wParam;
|
rlm@0
|
530 device = (ALCdevice*)msg.lParam;
|
rlm@0
|
531 data = device->ExtraData;
|
rlm@0
|
532
|
rlm@0
|
533 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
|
rlm@0
|
534 if(SUCCEEDED(hr))
|
rlm@0
|
535 {
|
rlm@0
|
536 Enumerator = ptr;
|
rlm@0
|
537 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
|
rlm@0
|
538 IMMDeviceEnumerator_Release(Enumerator);
|
rlm@0
|
539 Enumerator = NULL;
|
rlm@0
|
540 }
|
rlm@0
|
541 if(SUCCEEDED(hr))
|
rlm@0
|
542 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
|
rlm@0
|
543 if(SUCCEEDED(hr))
|
rlm@0
|
544 data->client = ptr;
|
rlm@0
|
545
|
rlm@0
|
546 if(FAILED(hr))
|
rlm@0
|
547 {
|
rlm@0
|
548 if(data->mmdev)
|
rlm@0
|
549 IMMDevice_Release(data->mmdev);
|
rlm@0
|
550 data->mmdev = NULL;
|
rlm@0
|
551 }
|
rlm@0
|
552
|
rlm@0
|
553 req->result = hr;
|
rlm@0
|
554 SetEvent(req->FinishedEvt);
|
rlm@0
|
555 continue;
|
rlm@0
|
556
|
rlm@0
|
557 case WM_USER_ResetDevice:
|
rlm@0
|
558 req = (ThreadRequest*)msg.wParam;
|
rlm@0
|
559 device = (ALCdevice*)msg.lParam;
|
rlm@0
|
560
|
rlm@0
|
561 req->result = DoReset(device);
|
rlm@0
|
562 SetEvent(req->FinishedEvt);
|
rlm@0
|
563 continue;
|
rlm@0
|
564
|
rlm@0
|
565 case WM_USER_StopDevice:
|
rlm@0
|
566 req = (ThreadRequest*)msg.wParam;
|
rlm@0
|
567 device = (ALCdevice*)msg.lParam;
|
rlm@0
|
568 data = device->ExtraData;
|
rlm@0
|
569
|
rlm@0
|
570 if(data->thread)
|
rlm@0
|
571 {
|
rlm@0
|
572 data->killNow = 1;
|
rlm@0
|
573 StopThread(data->thread);
|
rlm@0
|
574 data->thread = NULL;
|
rlm@0
|
575
|
rlm@0
|
576 data->killNow = 0;
|
rlm@0
|
577
|
rlm@0
|
578 IAudioClient_Stop(data->client);
|
rlm@0
|
579 }
|
rlm@0
|
580
|
rlm@0
|
581 req->result = S_OK;
|
rlm@0
|
582 SetEvent(req->FinishedEvt);
|
rlm@0
|
583 continue;
|
rlm@0
|
584
|
rlm@0
|
585 case WM_USER_CloseDevice:
|
rlm@0
|
586 req = (ThreadRequest*)msg.wParam;
|
rlm@0
|
587 device = (ALCdevice*)msg.lParam;
|
rlm@0
|
588 data = device->ExtraData;
|
rlm@0
|
589
|
rlm@0
|
590 IAudioClient_Release(data->client);
|
rlm@0
|
591 data->client = NULL;
|
rlm@0
|
592
|
rlm@0
|
593 IMMDevice_Release(data->mmdev);
|
rlm@0
|
594 data->mmdev = NULL;
|
rlm@0
|
595
|
rlm@0
|
596 req->result = S_OK;
|
rlm@0
|
597 SetEvent(req->FinishedEvt);
|
rlm@0
|
598 continue;
|
rlm@0
|
599
|
rlm@0
|
600 default:
|
rlm@0
|
601 ERR("Unexpected message: %u\n", msg.message);
|
rlm@0
|
602 continue;
|
rlm@0
|
603 }
|
rlm@0
|
604 }
|
rlm@0
|
605 TRACE("Message loop finished\n");
|
rlm@0
|
606
|
rlm@0
|
607 CoUninitialize();
|
rlm@0
|
608 return 0;
|
rlm@0
|
609 }
|
rlm@0
|
610
|
rlm@0
|
611
|
rlm@0
|
612 static BOOL MMDevApiLoad(void)
|
rlm@0
|
613 {
|
rlm@0
|
614 static HRESULT InitResult;
|
rlm@0
|
615 if(!ThreadHdl)
|
rlm@0
|
616 {
|
rlm@0
|
617 ThreadRequest req;
|
rlm@0
|
618 InitResult = E_FAIL;
|
rlm@0
|
619
|
rlm@0
|
620 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
|
rlm@0
|
621 if(req.FinishedEvt == NULL)
|
rlm@0
|
622 ERR("Failed to create event: %lu\n", GetLastError());
|
rlm@0
|
623 else
|
rlm@0
|
624 {
|
rlm@0
|
625 ThreadHdl = CreateThread(NULL, 0, MessageProc, &req, 0, &ThreadID);
|
rlm@0
|
626 if(ThreadHdl != NULL)
|
rlm@0
|
627 InitResult = WaitForResponse(&req);
|
rlm@0
|
628 CloseHandle(req.FinishedEvt);
|
rlm@0
|
629 }
|
rlm@0
|
630 }
|
rlm@0
|
631 return SUCCEEDED(InitResult);
|
rlm@0
|
632 }
|
rlm@0
|
633
|
rlm@0
|
634
|
rlm@0
|
635 static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
|
rlm@0
|
636 {
|
rlm@0
|
637 MMDevApiData *data = NULL;
|
rlm@0
|
638 HRESULT hr;
|
rlm@0
|
639
|
rlm@0
|
640 if(!deviceName)
|
rlm@0
|
641 deviceName = mmDevice;
|
rlm@0
|
642 else if(strcmp(deviceName, mmDevice) != 0)
|
rlm@0
|
643 return ALC_FALSE;
|
rlm@0
|
644
|
rlm@0
|
645 //Initialise requested device
|
rlm@0
|
646 data = calloc(1, sizeof(MMDevApiData));
|
rlm@0
|
647 if(!data)
|
rlm@0
|
648 {
|
rlm@0
|
649 alcSetError(device, ALC_OUT_OF_MEMORY);
|
rlm@0
|
650 return ALC_FALSE;
|
rlm@0
|
651 }
|
rlm@0
|
652 device->ExtraData = data;
|
rlm@0
|
653
|
rlm@0
|
654 hr = S_OK;
|
rlm@0
|
655 data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
rlm@0
|
656 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
rlm@0
|
657 if(data->hNotifyEvent == NULL || data->MsgEvent == NULL)
|
rlm@0
|
658 hr = E_FAIL;
|
rlm@0
|
659
|
rlm@0
|
660 if(SUCCEEDED(hr))
|
rlm@0
|
661 {
|
rlm@0
|
662 ThreadRequest req = { data->MsgEvent, 0 };
|
rlm@0
|
663
|
rlm@0
|
664 hr = E_FAIL;
|
rlm@0
|
665 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
|
rlm@0
|
666 hr = WaitForResponse(&req);
|
rlm@0
|
667 }
|
rlm@0
|
668
|
rlm@0
|
669 if(FAILED(hr))
|
rlm@0
|
670 {
|
rlm@0
|
671 if(data->hNotifyEvent != NULL)
|
rlm@0
|
672 CloseHandle(data->hNotifyEvent);
|
rlm@0
|
673 data->hNotifyEvent = NULL;
|
rlm@0
|
674 if(data->MsgEvent != NULL)
|
rlm@0
|
675 CloseHandle(data->MsgEvent);
|
rlm@0
|
676 data->MsgEvent = NULL;
|
rlm@0
|
677
|
rlm@0
|
678 free(data);
|
rlm@0
|
679 device->ExtraData = NULL;
|
rlm@0
|
680
|
rlm@0
|
681 ERR("Device init failed: 0x%08lx\n", hr);
|
rlm@0
|
682 return ALC_FALSE;
|
rlm@0
|
683 }
|
rlm@0
|
684
|
rlm@0
|
685 device->szDeviceName = strdup(deviceName);
|
rlm@0
|
686 return ALC_TRUE;
|
rlm@0
|
687 }
|
rlm@0
|
688
|
rlm@0
|
689 static void MMDevApiClosePlayback(ALCdevice *device)
|
rlm@0
|
690 {
|
rlm@0
|
691 MMDevApiData *data = device->ExtraData;
|
rlm@0
|
692 ThreadRequest req = { data->MsgEvent, 0 };
|
rlm@0
|
693
|
rlm@0
|
694 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
|
rlm@0
|
695 (void)WaitForResponse(&req);
|
rlm@0
|
696
|
rlm@0
|
697 CloseHandle(data->MsgEvent);
|
rlm@0
|
698 data->MsgEvent = NULL;
|
rlm@0
|
699
|
rlm@0
|
700 CloseHandle(data->hNotifyEvent);
|
rlm@0
|
701 data->hNotifyEvent = NULL;
|
rlm@0
|
702
|
rlm@0
|
703 free(data);
|
rlm@0
|
704 device->ExtraData = NULL;
|
rlm@0
|
705 }
|
rlm@0
|
706
|
rlm@0
|
707 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
|
rlm@0
|
708 {
|
rlm@0
|
709 MMDevApiData *data = device->ExtraData;
|
rlm@0
|
710 ThreadRequest req = { data->MsgEvent, 0 };
|
rlm@0
|
711 HRESULT hr = E_FAIL;
|
rlm@0
|
712
|
rlm@0
|
713 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
|
rlm@0
|
714 hr = WaitForResponse(&req);
|
rlm@0
|
715
|
rlm@0
|
716 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
|
rlm@0
|
717 }
|
rlm@0
|
718
|
rlm@0
|
719 static void MMDevApiStopPlayback(ALCdevice *device)
|
rlm@0
|
720 {
|
rlm@0
|
721 MMDevApiData *data = device->ExtraData;
|
rlm@0
|
722 ThreadRequest req = { data->MsgEvent, 0 };
|
rlm@0
|
723
|
rlm@0
|
724 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
|
rlm@0
|
725 (void)WaitForResponse(&req);
|
rlm@0
|
726 }
|
rlm@0
|
727
|
rlm@0
|
728
|
rlm@0
|
729 static const BackendFuncs MMDevApiFuncs = {
|
rlm@0
|
730 MMDevApiOpenPlayback,
|
rlm@0
|
731 MMDevApiClosePlayback,
|
rlm@0
|
732 MMDevApiResetPlayback,
|
rlm@0
|
733 MMDevApiStopPlayback,
|
rlm@0
|
734 NULL,
|
rlm@0
|
735 NULL,
|
rlm@0
|
736 NULL,
|
rlm@0
|
737 NULL,
|
rlm@0
|
738 NULL,
|
rlm@0
|
739 NULL
|
rlm@0
|
740 };
|
rlm@0
|
741
|
rlm@0
|
742
|
rlm@0
|
743 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
|
rlm@0
|
744 {
|
rlm@0
|
745 if(!MMDevApiLoad())
|
rlm@0
|
746 return ALC_FALSE;
|
rlm@0
|
747 *FuncList = MMDevApiFuncs;
|
rlm@0
|
748 return ALC_TRUE;
|
rlm@0
|
749 }
|
rlm@0
|
750
|
rlm@0
|
751 void alcMMDevApiDeinit(void)
|
rlm@0
|
752 {
|
rlm@0
|
753 if(ThreadHdl)
|
rlm@0
|
754 {
|
rlm@0
|
755 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
|
rlm@0
|
756 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
|
rlm@0
|
757 CloseHandle(ThreadHdl);
|
rlm@0
|
758 ThreadHdl = NULL;
|
rlm@0
|
759 }
|
rlm@0
|
760 }
|
rlm@0
|
761
|
rlm@0
|
762 void alcMMDevApiProbe(enum DevProbe type)
|
rlm@0
|
763 {
|
rlm@0
|
764 switch(type)
|
rlm@0
|
765 {
|
rlm@0
|
766 case DEVICE_PROBE:
|
rlm@0
|
767 AppendDeviceList(mmDevice);
|
rlm@0
|
768 break;
|
rlm@0
|
769 case ALL_DEVICE_PROBE:
|
rlm@0
|
770 AppendAllDeviceList(mmDevice);
|
rlm@0
|
771 break;
|
rlm@0
|
772 case CAPTURE_DEVICE_PROBE:
|
rlm@0
|
773 break;
|
rlm@0
|
774 }
|
rlm@0
|
775 }
|