2
0

mmdevapi.c 31 KB

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