view Alc/backends/mmdevapi.c @ 0:f9476ff7637e

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