Fix potential divide-by-0
[openal-soft/android.git] / Alc / backends / mmdevapi.c
blob541bea3d7c43d2254753cbb027b812b51e31a47c
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.
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
21 #include "config.h"
23 #define COBJMACROS
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <memory.h>
28 #include <mmdeviceapi.h>
29 #include <audioclient.h>
30 #include <cguid.h>
31 #include <mmreg.h>
32 #ifndef _WAVEFORMATEXTENSIBLE_
33 #include <ks.h>
34 #include <ksmedia.h>
35 #endif
37 #include "alMain.h"
38 #include "AL/al.h"
39 #include "AL/alc.h"
42 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
43 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
45 #define MONO SPEAKER_FRONT_CENTER
46 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
47 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
48 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
49 #define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
50 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
51 #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)
54 typedef struct {
55 IMMDevice *mmdev;
56 IAudioClient *client;
57 HANDLE hNotifyEvent;
59 HANDLE MsgEvent;
61 volatile int killNow;
62 ALvoid *thread;
63 } MMDevApiData;
66 static const ALCchar mmDevice[] = "WASAPI Default";
69 static HANDLE ThreadHdl;
70 static DWORD ThreadID;
72 typedef struct {
73 HANDLE FinishedEvt;
74 HRESULT result;
75 } ThreadRequest;
77 #define WM_USER_OpenDevice (WM_USER+0)
78 #define WM_USER_ResetDevice (WM_USER+1)
79 #define WM_USER_StopDevice (WM_USER+2)
80 #define WM_USER_CloseDevice (WM_USER+3)
82 static HRESULT WaitForResponse(ThreadRequest *req)
84 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
85 return req->result;
86 ERR("Message response error: %lu\n", GetLastError());
87 return E_FAIL;
91 static ALuint MMDevApiProc(ALvoid *ptr)
93 ALCdevice *device = ptr;
94 MMDevApiData *data = device->ExtraData;
95 union {
96 IAudioRenderClient *iface;
97 void *ptr;
98 } render;
99 UINT32 written, len;
100 BYTE *buffer;
101 HRESULT hr;
103 hr = CoInitialize(NULL);
104 if(FAILED(hr))
106 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
107 aluHandleDisconnect(device);
108 return 0;
111 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr);
112 if(FAILED(hr))
114 ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr);
115 aluHandleDisconnect(device);
116 return 0;
119 SetRTPriority();
121 while(!data->killNow)
123 hr = IAudioClient_GetCurrentPadding(data->client, &written);
124 if(FAILED(hr))
126 ERR("Failed to get padding: 0x%08lx\n", hr);
127 aluHandleDisconnect(device);
128 break;
131 len = device->UpdateSize*device->NumUpdates - written;
132 if(len < device->UpdateSize)
134 DWORD res;
135 res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE);
136 if(res != WAIT_OBJECT_0)
137 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
138 continue;
140 len -= len%device->UpdateSize;
142 hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer);
143 if(SUCCEEDED(hr))
145 aluMixData(device, buffer, len);
146 hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0);
148 if(FAILED(hr))
150 ERR("Failed to buffer data: 0x%08lx\n", hr);
151 aluHandleDisconnect(device);
152 break;
156 IAudioRenderClient_Release(render.iface);
158 CoUninitialize();
159 return 0;
163 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
165 memset(out, 0, sizeof(*out));
166 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
167 *out = *(WAVEFORMATEXTENSIBLE*)in;
168 else if(in->wFormatTag == WAVE_FORMAT_PCM)
170 out->Format = *in;
171 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
172 out->Format.cbSize = sizeof(*out) - sizeof(*in);
173 if(out->Format.nChannels == 1)
174 out->dwChannelMask = MONO;
175 else if(out->Format.nChannels == 2)
176 out->dwChannelMask = STEREO;
177 else
178 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
179 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
181 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
183 out->Format = *in;
184 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
185 out->Format.cbSize = sizeof(*out) - sizeof(*in);
186 if(out->Format.nChannels == 1)
187 out->dwChannelMask = MONO;
188 else if(out->Format.nChannels == 2)
189 out->dwChannelMask = STEREO;
190 else
191 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
192 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
194 else
196 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
197 return ALC_FALSE;
199 return ALC_TRUE;
202 static HRESULT DoReset(ALCdevice *device)
204 MMDevApiData *data = device->ExtraData;
205 WAVEFORMATEXTENSIBLE OutputType;
206 WAVEFORMATEX *wfx = NULL;
207 REFERENCE_TIME min_per;
208 UINT32 buffer_len, min_len;
209 HRESULT hr;
211 hr = IAudioClient_GetMixFormat(data->client, &wfx);
212 if(FAILED(hr))
214 ERR("Failed to get mix format: 0x%08lx\n", hr);
215 return hr;
218 if(!MakeExtensible(&OutputType, wfx))
220 CoTaskMemFree(wfx);
221 return E_FAIL;
223 CoTaskMemFree(wfx);
224 wfx = NULL;
226 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
227 device->Frequency = OutputType.Format.nSamplesPerSec;
228 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
230 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
231 device->FmtChans = DevFmtMono;
232 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
233 device->FmtChans = DevFmtStereo;
234 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
235 device->FmtChans = DevFmtQuad;
236 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
237 device->FmtChans = DevFmtX51;
238 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
239 device->FmtChans = DevFmtX51Side;
240 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
241 device->FmtChans = DevFmtX61;
242 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
243 device->FmtChans = DevFmtX71;
244 else
245 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
248 switch(device->FmtChans)
250 case DevFmtMono:
251 OutputType.Format.nChannels = 1;
252 OutputType.dwChannelMask = MONO;
253 break;
254 case DevFmtStereo:
255 OutputType.Format.nChannels = 2;
256 OutputType.dwChannelMask = STEREO;
257 break;
258 case DevFmtQuad:
259 OutputType.Format.nChannels = 4;
260 OutputType.dwChannelMask = QUAD;
261 break;
262 case DevFmtX51:
263 OutputType.Format.nChannels = 6;
264 OutputType.dwChannelMask = X5DOT1;
265 break;
266 case DevFmtX51Side:
267 OutputType.Format.nChannels = 6;
268 OutputType.dwChannelMask = X5DOT1SIDE;
269 break;
270 case DevFmtX61:
271 OutputType.Format.nChannels = 7;
272 OutputType.dwChannelMask = X6DOT1;
273 break;
274 case DevFmtX71:
275 OutputType.Format.nChannels = 8;
276 OutputType.dwChannelMask = X7DOT1;
277 break;
279 switch(device->FmtType)
281 case DevFmtByte:
282 device->FmtType = DevFmtUByte;
283 /* fall-through */
284 case DevFmtUByte:
285 OutputType.Format.wBitsPerSample = 8;
286 OutputType.Samples.wValidBitsPerSample = 8;
287 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
288 break;
289 case DevFmtUShort:
290 device->FmtType = DevFmtShort;
291 /* fall-through */
292 case DevFmtShort:
293 OutputType.Format.wBitsPerSample = 16;
294 OutputType.Samples.wValidBitsPerSample = 16;
295 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
296 break;
297 case DevFmtFloat:
298 OutputType.Format.wBitsPerSample = 32;
299 OutputType.Samples.wValidBitsPerSample = 32;
300 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
301 break;
303 OutputType.Format.nSamplesPerSec = device->Frequency;
305 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
306 OutputType.Format.wBitsPerSample / 8;
307 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
308 OutputType.Format.nBlockAlign;
310 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
311 if(FAILED(hr))
313 ERR("Failed to check format support: 0x%08lx\n", hr);
314 hr = IAudioClient_GetMixFormat(data->client, &wfx);
316 if(FAILED(hr))
318 ERR("Failed to find a supported format: 0x%08lx\n", hr);
319 return hr;
322 if(wfx != NULL)
324 if(!MakeExtensible(&OutputType, wfx))
326 CoTaskMemFree(wfx);
327 return E_FAIL;
329 CoTaskMemFree(wfx);
330 wfx = NULL;
332 if(device->Frequency != OutputType.Format.nSamplesPerSec)
334 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
335 ERR("Failed to set %dhz, got %ldhz instead\n", device->Frequency, OutputType.Format.nSamplesPerSec);
336 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
337 device->Frequency = OutputType.Format.nSamplesPerSec;
340 if(!((device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) ||
341 (device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) ||
342 (device->FmtChans == DevFmtQuad && OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) ||
343 (device->FmtChans == DevFmtX51 && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) ||
344 (device->FmtChans == DevFmtX51Side && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) ||
345 (device->FmtChans == DevFmtX61 && OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) ||
346 (device->FmtChans == DevFmtX71 && OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)))
348 if((device->Flags&DEVICE_CHANNELS_REQUEST))
349 ERR("Failed to set %s, got %d channels (0x%08lx) instead\n", DevFmtChannelsString(device->FmtChans), OutputType.Format.nChannels, OutputType.dwChannelMask);
350 device->Flags &= ~DEVICE_CHANNELS_REQUEST;
352 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
353 device->FmtChans = DevFmtMono;
354 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
355 device->FmtChans = DevFmtStereo;
356 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
357 device->FmtChans = DevFmtQuad;
358 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
359 device->FmtChans = DevFmtX51;
360 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
361 device->FmtChans = DevFmtX51Side;
362 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
363 device->FmtChans = DevFmtX61;
364 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
365 device->FmtChans = DevFmtX71;
366 else
368 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
369 device->FmtChans = DevFmtStereo;
370 OutputType.Format.nChannels = 2;
371 OutputType.dwChannelMask = STEREO;
375 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
377 if(OutputType.Samples.wValidBitsPerSample == 0)
378 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
379 if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample ||
380 !((device->FmtType == DevFmtUByte && OutputType.Format.wBitsPerSample == 8) ||
381 (device->FmtType == DevFmtShort && OutputType.Format.wBitsPerSample == 16)))
383 ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample);
384 if(OutputType.Format.wBitsPerSample == 8)
385 device->FmtType = DevFmtUByte;
386 else if(OutputType.Format.wBitsPerSample == 16)
387 device->FmtType = DevFmtShort;
388 else
390 device->FmtType = DevFmtShort;
391 OutputType.Format.wBitsPerSample = 16;
393 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
396 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
398 if(OutputType.Samples.wValidBitsPerSample == 0)
399 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
400 if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample ||
401 !((device->FmtType == DevFmtFloat && OutputType.Format.wBitsPerSample == 32)))
403 ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample);
404 if(OutputType.Format.wBitsPerSample != 32)
406 device->FmtType = DevFmtFloat;
407 OutputType.Format.wBitsPerSample = 32;
409 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
412 else
414 ERR("Unhandled format sub-type\n");
415 device->FmtType = DevFmtShort;
416 OutputType.Format.wBitsPerSample = 16;
417 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
418 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
422 SetDefaultWFXChannelOrder(device);
424 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
425 if(SUCCEEDED(hr))
427 min_len = (min_per*device->Frequency + 10000000-1) / 10000000;
428 if(min_len < device->UpdateSize)
429 min_len *= (device->UpdateSize + min_len/2)/min_len;
431 device->NumUpdates = (device->NumUpdates*device->UpdateSize + min_len/2) /
432 min_len;
433 device->NumUpdates = maxu(device->NumUpdates, 2);
434 device->UpdateSize = min_len;
436 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
437 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
438 ((REFERENCE_TIME)device->UpdateSize*
439 device->NumUpdates*10000000 +
440 device->Frequency-1) / device->Frequency,
441 0, &OutputType.Format, NULL);
443 if(FAILED(hr))
445 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
446 return hr;
449 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
450 if(FAILED(hr))
452 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
453 return hr;
456 device->NumUpdates = buffer_len / device->UpdateSize;
457 if(device->NumUpdates <= 1)
459 device->NumUpdates = 1;
460 ERR("Audio client returned buffer_len < period*2; expect break up\n");
463 ResetEvent(data->hNotifyEvent);
464 hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent);
465 if(SUCCEEDED(hr))
466 hr = IAudioClient_Start(data->client);
467 if(FAILED(hr))
469 ERR("Failed to start audio client: 0x%08lx\n", hr);
470 return hr;
473 data->thread = StartThread(MMDevApiProc, device);
474 if(!data->thread)
476 IAudioClient_Stop(data->client);
477 ERR("Failed to start thread\n");
478 return E_FAIL;
481 return hr;
485 static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
487 ThreadRequest *req = ptr;
488 IMMDeviceEnumerator *Enumerator;
489 MMDevApiData *data;
490 ALCdevice *device;
491 HRESULT hr;
492 MSG msg;
494 TRACE("Starting message thread\n");
496 hr = CoInitialize(NULL);
497 if(FAILED(hr))
499 WARN("Failed to initialize COM: 0x%08lx\n", hr);
500 req->result = hr;
501 SetEvent(req->FinishedEvt);
502 return 0;
505 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
506 if(FAILED(hr))
508 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
509 CoUninitialize();
510 req->result = hr;
511 SetEvent(req->FinishedEvt);
512 return 0;
514 Enumerator = ptr;
515 IMMDeviceEnumerator_Release(Enumerator);
516 Enumerator = NULL;
518 req->result = S_OK;
519 SetEvent(req->FinishedEvt);
521 TRACE("Starting message loop\n");
522 while(GetMessage(&msg, NULL, 0, 0))
524 TRACE("Got message %u\n", msg.message);
525 switch(msg.message)
527 case WM_USER_OpenDevice:
528 req = (ThreadRequest*)msg.wParam;
529 device = (ALCdevice*)msg.lParam;
530 data = device->ExtraData;
532 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
533 if(SUCCEEDED(hr))
535 Enumerator = ptr;
536 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
537 IMMDeviceEnumerator_Release(Enumerator);
538 Enumerator = NULL;
540 if(SUCCEEDED(hr))
541 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
542 if(SUCCEEDED(hr))
543 data->client = ptr;
545 if(FAILED(hr))
547 if(data->mmdev)
548 IMMDevice_Release(data->mmdev);
549 data->mmdev = NULL;
552 req->result = hr;
553 SetEvent(req->FinishedEvt);
554 continue;
556 case WM_USER_ResetDevice:
557 req = (ThreadRequest*)msg.wParam;
558 device = (ALCdevice*)msg.lParam;
560 req->result = DoReset(device);
561 SetEvent(req->FinishedEvt);
562 continue;
564 case WM_USER_StopDevice:
565 req = (ThreadRequest*)msg.wParam;
566 device = (ALCdevice*)msg.lParam;
567 data = device->ExtraData;
569 if(data->thread)
571 data->killNow = 1;
572 StopThread(data->thread);
573 data->thread = NULL;
575 data->killNow = 0;
577 IAudioClient_Stop(data->client);
580 req->result = S_OK;
581 SetEvent(req->FinishedEvt);
582 continue;
584 case WM_USER_CloseDevice:
585 req = (ThreadRequest*)msg.wParam;
586 device = (ALCdevice*)msg.lParam;
587 data = device->ExtraData;
589 IAudioClient_Release(data->client);
590 data->client = NULL;
592 IMMDevice_Release(data->mmdev);
593 data->mmdev = NULL;
595 req->result = S_OK;
596 SetEvent(req->FinishedEvt);
597 continue;
599 default:
600 ERR("Unexpected message: %u\n", msg.message);
601 continue;
604 TRACE("Message loop finished\n");
606 CoUninitialize();
607 return 0;
611 static BOOL MMDevApiLoad(void)
613 static HRESULT InitResult;
614 if(!ThreadHdl)
616 ThreadRequest req;
617 InitResult = E_FAIL;
619 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
620 if(req.FinishedEvt == NULL)
621 ERR("Failed to create event: %lu\n", GetLastError());
622 else
624 ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
625 if(ThreadHdl != NULL)
626 InitResult = WaitForResponse(&req);
627 CloseHandle(req.FinishedEvt);
630 return SUCCEEDED(InitResult);
634 static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
636 MMDevApiData *data = NULL;
637 HRESULT hr;
639 if(!deviceName)
640 deviceName = mmDevice;
641 else if(strcmp(deviceName, mmDevice) != 0)
642 return ALC_INVALID_VALUE;
644 //Initialise requested device
645 data = calloc(1, sizeof(MMDevApiData));
646 if(!data)
647 return ALC_OUT_OF_MEMORY;
648 device->ExtraData = data;
650 hr = S_OK;
651 data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
652 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
653 if(data->hNotifyEvent == NULL || data->MsgEvent == NULL)
654 hr = E_FAIL;
656 if(SUCCEEDED(hr))
658 ThreadRequest req = { data->MsgEvent, 0 };
660 hr = E_FAIL;
661 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
662 hr = WaitForResponse(&req);
665 if(FAILED(hr))
667 if(data->hNotifyEvent != NULL)
668 CloseHandle(data->hNotifyEvent);
669 data->hNotifyEvent = NULL;
670 if(data->MsgEvent != NULL)
671 CloseHandle(data->MsgEvent);
672 data->MsgEvent = NULL;
674 free(data);
675 device->ExtraData = NULL;
677 ERR("Device init failed: 0x%08lx\n", hr);
678 return ALC_INVALID_VALUE;
681 device->szDeviceName = strdup(deviceName);
682 return ALC_NO_ERROR;
685 static void MMDevApiClosePlayback(ALCdevice *device)
687 MMDevApiData *data = device->ExtraData;
688 ThreadRequest req = { data->MsgEvent, 0 };
690 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
691 (void)WaitForResponse(&req);
693 CloseHandle(data->MsgEvent);
694 data->MsgEvent = NULL;
696 CloseHandle(data->hNotifyEvent);
697 data->hNotifyEvent = NULL;
699 free(data);
700 device->ExtraData = NULL;
703 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
705 MMDevApiData *data = device->ExtraData;
706 ThreadRequest req = { data->MsgEvent, 0 };
707 HRESULT hr = E_FAIL;
709 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
710 hr = WaitForResponse(&req);
712 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
715 static void MMDevApiStopPlayback(ALCdevice *device)
717 MMDevApiData *data = device->ExtraData;
718 ThreadRequest req = { data->MsgEvent, 0 };
720 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
721 (void)WaitForResponse(&req);
725 static const BackendFuncs MMDevApiFuncs = {
726 MMDevApiOpenPlayback,
727 MMDevApiClosePlayback,
728 MMDevApiResetPlayback,
729 MMDevApiStopPlayback,
730 NULL,
731 NULL,
732 NULL,
733 NULL,
734 NULL,
735 NULL
739 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
741 if(!MMDevApiLoad())
742 return ALC_FALSE;
743 *FuncList = MMDevApiFuncs;
744 return ALC_TRUE;
747 void alcMMDevApiDeinit(void)
749 if(ThreadHdl)
751 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
752 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
753 CloseHandle(ThreadHdl);
754 ThreadHdl = NULL;
758 void alcMMDevApiProbe(enum DevProbe type)
760 switch(type)
762 case DEVICE_PROBE:
763 AppendDeviceList(mmDevice);
764 break;
765 case ALL_DEVICE_PROBE:
766 AppendAllDeviceList(mmDevice);
767 break;
768 case CAPTURE_DEVICE_PROBE:
769 break;