Fix COM initialization with MMDevApi
[openal-soft/android.git] / Alc / mmdevapi.c
blob052ff6f362eca7ae7d3a68333d4faabee0d1b290
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 #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 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 static IMMDeviceEnumerator *Enumerator = NULL;
57 typedef struct {
58 IMMDevice *mmdev;
59 IAudioClient *client;
61 volatile int killNow;
62 ALvoid *thread;
63 } MMDevApiData;
66 static const ALCchar mmDevice[] = "WASAPI Default";
69 static void *MMDevApiLoad(void)
71 if(!Enumerator)
73 void *mme = NULL;
74 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
75 if(FAILED(hr))
76 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
77 if(SUCCEEDED(hr))
79 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &mme);
80 if(FAILED(hr))
81 CoUninitialize();
82 else
83 Enumerator = mme;
86 return Enumerator;
90 static ALuint MMDevApiProc(ALvoid *ptr)
92 ALCdevice *device = ptr;
93 MMDevApiData *data = device->ExtraData;
94 union {
95 IAudioRenderClient *iface;
96 void *ptr;
97 } render;
98 UINT32 written, len;
99 BYTE *buffer;
100 HRESULT hr;
102 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr);
103 if(FAILED(hr))
105 AL_PRINT("Failed to get AudioRenderClient service: 0x%08lx\n", hr);
106 aluHandleDisconnect(device);
107 return 0;
110 SetRTPriority();
112 while(!data->killNow)
114 hr = IAudioClient_GetCurrentPadding(data->client, &written);
115 if(FAILED(hr))
117 AL_PRINT("Failed to get padding: 0x%08lx\n", hr);
118 aluHandleDisconnect(device);
119 break;
122 len = device->UpdateSize*device->NumUpdates - written;
123 if(len < device->UpdateSize)
125 Sleep(10);
126 continue;
128 len -= len%device->UpdateSize;
130 hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer);
131 if(SUCCEEDED(hr))
133 aluMixData(device, buffer, len);
134 hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0);
136 if(FAILED(hr))
138 AL_PRINT("Failed to buffer data: 0x%08lx\n", hr);
139 aluHandleDisconnect(device);
140 break;
144 IAudioRenderClient_Release(render.iface);
145 return 0;
149 static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
151 MMDevApiData *data = NULL;
152 void *client = NULL;
153 HRESULT hr;
155 if(!MMDevApiLoad())
156 return ALC_FALSE;
158 if(!deviceName)
159 deviceName = mmDevice;
160 else if(strcmp(deviceName, mmDevice) != 0)
161 return ALC_FALSE;
163 //Initialise requested device
164 data = calloc(1, sizeof(MMDevApiData));
165 if(!data)
167 alcSetError(device, ALC_OUT_OF_MEMORY);
168 return ALC_FALSE;
171 //MMDevApi Init code
172 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
173 if(SUCCEEDED(hr))
174 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &client);
176 if(FAILED(hr))
178 if(data->mmdev)
179 IMMDevice_Release(data->mmdev);
180 data->mmdev = NULL;
181 free(data);
183 AL_PRINT("Device init failed: 0x%08lx\n", hr);
184 return ALC_FALSE;
186 data->client = client;
188 device->szDeviceName = strdup(deviceName);
189 device->ExtraData = data;
190 return ALC_TRUE;
193 static void MMDevApiClosePlayback(ALCdevice *device)
195 MMDevApiData *data = device->ExtraData;
197 IAudioClient_Release(data->client);
198 data->client = NULL;
200 IMMDevice_Release(data->mmdev);
201 data->mmdev = NULL;
203 free(data);
204 device->ExtraData = NULL;
207 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
209 MMDevApiData *data = device->ExtraData;
210 WAVEFORMATEXTENSIBLE OutputType;
211 WAVEFORMATEX *wfx = NULL;
212 HRESULT hr;
214 hr = IAudioClient_GetMixFormat(data->client, &wfx);
215 if(FAILED(hr))
217 AL_PRINT("Failed to get mix format: 0x%08lx\n", hr);
218 return ALC_FALSE;
221 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
222 device->Frequency = wfx->nSamplesPerSec;
223 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
225 if(wfx->wFormatTag == WAVE_FORMAT_PCM)
227 if(wfx->nChannels == 1)
228 device->FmtChans = DevFmtMono;
229 else if(wfx->nChannels == 2)
230 device->FmtChans = DevFmtStereo;
231 else
233 AL_PRINT("Unhandled PCM channels: %d\n", wfx->nChannels);
234 device->FmtChans = DevFmtStereo;
237 else if(wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
239 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)wfx;
241 if(wfe->Format.nChannels == 1 && wfe->dwChannelMask == MONO)
242 device->FmtChans = DevFmtMono;
243 else if(wfe->Format.nChannels == 2 && wfe->dwChannelMask == STEREO)
244 device->FmtChans = DevFmtStereo;
245 else if(wfe->Format.nChannels == 4 && wfe->dwChannelMask == QUAD)
246 device->FmtChans = DevFmtQuad;
247 else if(wfe->Format.nChannels == 6 && wfe->dwChannelMask == X5DOT1)
248 device->FmtChans = DevFmtX51;
249 else if(wfe->Format.nChannels == 7 && wfe->dwChannelMask == X6DOT1)
250 device->FmtChans = DevFmtX61;
251 else if(wfe->Format.nChannels == 8 && wfe->dwChannelMask == X7DOT1)
252 device->FmtChans = DevFmtX71;
253 else
255 AL_PRINT("Unhandled extensible channels: %d -- 0x%08lx\n", wfe->Format.nChannels, wfe->dwChannelMask);
256 device->FmtChans = DevFmtStereo;
261 if(wfx->wFormatTag == WAVE_FORMAT_PCM)
263 if(wfx->wBitsPerSample == 8)
264 device->FmtType = DevFmtUByte;
265 else if(wfx->wBitsPerSample == 16)
266 device->FmtType = DevFmtShort;
268 else if(wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
270 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)wfx;
272 if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
274 if(wfx->wBitsPerSample == 8)
275 device->FmtType = DevFmtUByte;
276 else if(wfx->wBitsPerSample == 16)
277 device->FmtType = DevFmtShort;
279 else if(IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
280 device->FmtType = DevFmtFloat;
283 CoTaskMemFree(wfx);
284 wfx = NULL;
286 SetDefaultWFXChannelOrder(device);
288 memset(&OutputType, 0, sizeof(OutputType));
289 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
291 switch(device->FmtType)
293 case DevFmtByte:
294 device->FmtType = DevFmtUByte;
295 /* fall-through */
296 case DevFmtUByte:
297 OutputType.Format.wBitsPerSample = 8;
298 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
299 break;
300 case DevFmtUShort:
301 device->FmtType = DevFmtShort;
302 /* fall-through */
303 case DevFmtShort:
304 OutputType.Format.wBitsPerSample = 16;
305 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
306 break;
307 case DevFmtFloat:
308 OutputType.Format.wBitsPerSample = 32;
309 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
310 break;
313 switch(device->FmtChans)
315 case DevFmtMono:
316 OutputType.Format.nChannels = 1;
317 OutputType.dwChannelMask = MONO;
318 break;
319 case DevFmtStereo:
320 OutputType.Format.nChannels = 2;
321 OutputType.dwChannelMask = STEREO;
322 break;
323 case DevFmtQuad:
324 OutputType.Format.nChannels = 4;
325 OutputType.dwChannelMask = QUAD;
326 break;
327 case DevFmtX51:
328 OutputType.Format.nChannels = 6;
329 OutputType.dwChannelMask = X5DOT1;
330 break;
331 case DevFmtX61:
332 OutputType.Format.nChannels = 7;
333 OutputType.dwChannelMask = X6DOT1;
334 break;
335 case DevFmtX71:
336 OutputType.Format.nChannels = 8;
337 OutputType.dwChannelMask = X7DOT1;
338 break;
341 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
342 OutputType.Format.nSamplesPerSec = device->Frequency;
343 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
344 OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format);
347 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED, 0,
348 (ALuint64)device->UpdateSize * 10000000 /
349 device->Frequency * device->NumUpdates,
350 0, &OutputType.Format, NULL);
351 if(FAILED(hr))
353 AL_PRINT("Failed to initialize audio client: 0x%08lx\n", hr);
354 return ALC_FALSE;
358 hr = IAudioClient_Start(data->client);
359 if(FAILED(hr))
361 AL_PRINT("Failed to start audio client\n");
362 return ALC_FALSE;
365 data->thread = StartThread(MMDevApiProc, device);
366 if(!data->thread)
368 IAudioClient_Stop(data->client);
369 AL_PRINT("Failed to start thread\n");
370 return ALC_FALSE;
373 return ALC_TRUE;
376 static void MMDevApiStopPlayback(ALCdevice *device)
378 MMDevApiData *data = device->ExtraData;
380 if(!data->thread)
381 return;
383 data->killNow = 1;
384 StopThread(data->thread);
385 data->thread = NULL;
387 data->killNow = 0;
389 IAudioClient_Stop(data->client);
393 static ALCboolean MMDevApiOpenCapture(ALCdevice *device, const ALCchar *deviceName)
395 (void)device;
396 (void)deviceName;
397 return ALC_FALSE;
401 static const BackendFuncs MMDevApiFuncs = {
402 MMDevApiOpenPlayback,
403 MMDevApiClosePlayback,
404 MMDevApiResetPlayback,
405 MMDevApiStopPlayback,
406 MMDevApiOpenCapture,
407 NULL,
408 NULL,
409 NULL,
410 NULL,
411 NULL
415 void alcMMDevApiInit(BackendFuncs *FuncList)
417 *FuncList = MMDevApiFuncs;
420 void alcMMDevApiDeinit(void)
422 if(Enumerator)
424 IMMDeviceEnumerator_Release(Enumerator);
425 Enumerator = NULL;
426 CoUninitialize();
430 void alcMMDevApiProbe(int type)
432 if(!MMDevApiLoad()) return;
434 if(type == DEVICE_PROBE)
435 AppendDeviceList(mmDevice);
436 else if(type == ALL_DEVICE_PROBE)
437 AppendAllDeviceList(mmDevice);