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