1 /*
2  * Copyright © 2013 Mozilla Foundation
3  *
4  * This program is made available under an ISC-style license.  See the
5  * accompanying file LICENSE for details.
6  */
7 #define _WIN32_WINNT 0x0600
8 #define NOMINMAX
9 
10 #include <initguid.h>
11 #include <windows.h>
12 #include <mmdeviceapi.h>
13 #include <windef.h>
14 #include <audioclient.h>
15 #include <devicetopology.h>
16 #include <process.h>
17 #include <avrt.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include <cmath>
23 #include <algorithm>
24 #include <memory>
25 #include <limits>
26 #include <atomic>
27 #include <vector>
28 
29 #include "cubeb/cubeb.h"
30 #include "cubeb-internal.h"
31 #include "cubeb_mixer.h"
32 #include "cubeb_resampler.h"
33 #include "cubeb_strings.h"
34 #include "cubeb_utils.h"
35 
36 // Windows 10 exposes the IAudioClient3 interface to create low-latency streams.
37 // Copy the interface definition from audioclient.h here to make the code simpler
38 // and so that we can still access IAudioClient3 via COM if cubeb was compiled
39 // against an older SDK.
40 #ifndef __IAudioClient3_INTERFACE_DEFINED__
41 #define __IAudioClient3_INTERFACE_DEFINED__
42 MIDL_INTERFACE("7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42")
43 IAudioClient3 : public IAudioClient
44 {
45 public:
46     virtual HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod(
47         /* [annotation][in] */
48         _In_  const WAVEFORMATEX *pFormat,
49         /* [annotation][out] */
50         _Out_  UINT32 *pDefaultPeriodInFrames,
51         /* [annotation][out] */
52         _Out_  UINT32 *pFundamentalPeriodInFrames,
53         /* [annotation][out] */
54         _Out_  UINT32 *pMinPeriodInFrames,
55         /* [annotation][out] */
56         _Out_  UINT32 *pMaxPeriodInFrames) = 0;
57 
58     virtual HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod(
59         /* [unique][annotation][out] */
60         _Out_  WAVEFORMATEX **ppFormat,
61         /* [annotation][out] */
62         _Out_  UINT32 *pCurrentPeriodInFrames) = 0;
63 
64     virtual HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream(
65         /* [annotation][in] */
66         _In_  DWORD StreamFlags,
67         /* [annotation][in] */
68         _In_  UINT32 PeriodInFrames,
69         /* [annotation][in] */
70         _In_  const WAVEFORMATEX *pFormat,
71         /* [annotation][in] */
72         _In_opt_  LPCGUID AudioSessionGuid) = 0;
73 };
74 #ifdef __CRT_UUID_DECL
75 // Required for MinGW
76 __CRT_UUID_DECL(IAudioClient3, 0x7ED4EE07, 0x8E67, 0x4CD4, 0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42)
77 #endif
78 #endif
79 // Copied from audioclient.h in the Windows 10 SDK
80 #ifndef AUDCLNT_E_ENGINE_PERIODICITY_LOCKED
81 #define AUDCLNT_E_ENGINE_PERIODICITY_LOCKED    AUDCLNT_ERR(0x028)
82 #endif
83 
84 #ifndef PKEY_Device_FriendlyName
85 DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,    0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);    // DEVPROP_TYPE_STRING
86 #endif
87 #ifndef PKEY_Device_InstanceId
88 DEFINE_PROPERTYKEY(PKEY_Device_InstanceId,      0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 0x00000100); //    VT_LPWSTR
89 #endif
90 
91 namespace {
92 
93 const int64_t LATENCY_NOT_AVAILABLE_YET = -1;
94 
95 struct com_heap_ptr_deleter {
96   void operator()(void * ptr) const noexcept {
97     CoTaskMemFree(ptr);
98   }
99 };
100 
101 template <typename T>
102 using com_heap_ptr = std::unique_ptr<T, com_heap_ptr_deleter>;
103 
104 template<typename T, size_t N>
105 constexpr size_t
106 ARRAY_LENGTH(T(&)[N])
107 {
108   return N;
109 }
110 
111 template <typename T>
112 class no_addref_release : public T {
113   ULONG STDMETHODCALLTYPE AddRef() = 0;
114   ULONG STDMETHODCALLTYPE Release() = 0;
115 };
116 
117 template <typename T>
118 class com_ptr {
119 public:
120   com_ptr() noexcept = default;
121 
122   com_ptr(com_ptr const & other) noexcept = delete;
123   com_ptr & operator=(com_ptr const & other) noexcept = delete;
124   T ** operator&() const noexcept = delete;
125 
126   ~com_ptr() noexcept {
127     release();
128   }
129 
130   com_ptr(com_ptr && other) noexcept
131     : ptr(other.ptr)
132   {
133     other.ptr = nullptr;
134   }
135 
136   com_ptr & operator=(com_ptr && other) noexcept {
137     if (ptr != other.ptr) {
138       release();
139       ptr = other.ptr;
140       other.ptr = nullptr;
141     }
142     return *this;
143   }
144 
145   explicit operator bool() const noexcept {
146     return nullptr != ptr;
147   }
148 
149   no_addref_release<T> * operator->() const noexcept {
150     return static_cast<no_addref_release<T> *>(ptr);
151   }
152 
153   T * get() const noexcept {
154     return ptr;
155   }
156 
157   T ** receive() noexcept {
158     XASSERT(ptr == nullptr);
159     return &ptr;
160   }
161 
162   void ** receive_vpp() noexcept {
163     return reinterpret_cast<void **>(receive());
164   }
165 
166   com_ptr & operator=(std::nullptr_t) noexcept {
167     release();
168     return *this;
169   }
170 
171   void reset(T * p = nullptr) noexcept {
172     release();
173     ptr = p;
174   }
175 
176 private:
177   void release() noexcept {
178     T * temp = ptr;
179 
180     if (temp) {
181       ptr = nullptr;
182       temp->Release();
183     }
184   }
185 
186   T * ptr = nullptr;
187 };
188 
189 extern cubeb_ops const wasapi_ops;
190 
191 int wasapi_stream_stop(cubeb_stream * stm);
192 int wasapi_stream_start(cubeb_stream * stm);
193 void close_wasapi_stream(cubeb_stream * stm);
194 int setup_wasapi_stream(cubeb_stream * stm);
195 ERole pref_to_role(cubeb_stream_prefs param);
196 static char const * wstr_to_utf8(wchar_t const * str);
197 static std::unique_ptr<wchar_t const []> utf8_to_wstr(char const * str);
198 
199 }
200 
201 class wasapi_collection_notification_client;
202 class monitor_device_notifications;
203 
204 struct cubeb {
205   cubeb_ops const * ops = &wasapi_ops;
206   cubeb_strings * device_ids;
207   /* Device enumerator to get notifications when the
208      device collection change. */
209   com_ptr<IMMDeviceEnumerator> device_collection_enumerator;
210   com_ptr<wasapi_collection_notification_client> collection_notification_client;
211   /* Collection changed for input (capture) devices. */
212   cubeb_device_collection_changed_callback input_collection_changed_callback = nullptr;
213   void * input_collection_changed_user_ptr = nullptr;
214   /* Collection changed for output (render) devices. */
215   cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr;
216   void * output_collection_changed_user_ptr = nullptr;
217   UINT64 performance_counter_frequency;
218 };
219 
220 class wasapi_endpoint_notification_client;
221 
222 /* We have three possible callbacks we can use with a stream:
223  * - input only
224  * - output only
225  * - synchronized input and output
226  *
227  * Returns true when we should continue to play, false otherwise.
228  */
229 typedef bool (*wasapi_refill_callback)(cubeb_stream * stm);
230 
231 struct cubeb_stream {
232   /* Note: Must match cubeb_stream layout in cubeb.c. */
233   cubeb * context = nullptr;
234   void * user_ptr = nullptr;
235   /**/
236 
237   /* Mixer pameters. We need to convert the input stream to this
238      samplerate/channel layout, as WASAPI does not resample nor upmix
239      itself. */
240   cubeb_stream_params input_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
241   cubeb_stream_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
242   /* Stream parameters. This is what the client requested,
243    * and what will be presented in the callback. */
244   cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
245   cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
246   /* A MMDevice role for this stream: either communication or console here. */
247   ERole role;
248   /* The input and output device, or NULL for default. */
249   std::unique_ptr<const wchar_t[]> input_device;
250   std::unique_ptr<const wchar_t[]> output_device;
251   /* The latency initially requested for this stream, in frames. */
252   unsigned latency = 0;
253   cubeb_state_callback state_callback = nullptr;
254   cubeb_data_callback data_callback = nullptr;
255   wasapi_refill_callback refill_callback = nullptr;
256   /* True when a loopback device is requested with no output device. In this
257      case a dummy output device is opened to drive the loopback, but should not
258      be exposed. */
259   bool has_dummy_output = false;
260   /* Lifetime considerations:
261      - client, render_client, audio_clock and audio_stream_volume are interface
262        pointer to the IAudioClient.
263      - The lifetime for device_enumerator and notification_client, resampler,
264        mix_buffer are the same as the cubeb_stream instance. */
265 
266   /* Main handle on the WASAPI stream. */
267   com_ptr<IAudioClient> output_client;
268   /* Interface pointer to use the event-driven interface. */
269   com_ptr<IAudioRenderClient> render_client;
270   /* Interface pointer to use the volume facilities. */
271   com_ptr<IAudioStreamVolume> audio_stream_volume;
272   /* Interface pointer to use the stream audio clock. */
273   com_ptr<IAudioClock> audio_clock;
274   /* Frames written to the stream since it was opened. Reset on device
275      change. Uses mix_params.rate. */
276   UINT64 frames_written = 0;
277   /* Frames written to the (logical) stream since it was first
278      created. Updated on device change. Uses stream_params.rate. */
279   UINT64 total_frames_written = 0;
280   /* Last valid reported stream position.  Used to ensure the position
281      reported by stream_get_position increases monotonically. */
282   UINT64 prev_position = 0;
283   /* Device enumerator to be able to be notified when the default
284      device change. */
285   com_ptr<IMMDeviceEnumerator> device_enumerator;
286   /* Device notification client, to be able to be notified when the default
287      audio device changes and route the audio to the new default audio output
288      device */
289   com_ptr<wasapi_endpoint_notification_client> notification_client;
290   /* Main andle to the WASAPI capture stream. */
291   com_ptr<IAudioClient> input_client;
292   /* Interface to use the event driven capture interface */
293   com_ptr<IAudioCaptureClient> capture_client;
294   /* This event is set by the stream_stop and stream_destroy
295      function, so the render loop can exit properly. */
296   HANDLE shutdown_event = 0;
297   /* Set by OnDefaultDeviceChanged when a stream reconfiguration is required.
298      The reconfiguration is handled by the render loop thread. */
299   HANDLE reconfigure_event = 0;
300   /* This is set by WASAPI when we should refill the stream. */
301   HANDLE refill_event = 0;
302   /* This is set by WASAPI when we should read from the input stream. In
303    * practice, we read from the input stream in the output callback, so
304    * this is not used, but it is necessary to start getting input data. */
305   HANDLE input_available_event = 0;
306   /* Each cubeb_stream has its own thread. */
307   HANDLE thread = 0;
308   /* The lock protects all members that are touched by the render thread or
309      change during a device reset, including: audio_clock, audio_stream_volume,
310      client, frames_written, mix_params, total_frames_written, prev_position. */
311   owned_critical_section stream_reset_lock;
312   /* Maximum number of frames that can be passed down in a callback. */
313   uint32_t input_buffer_frame_count = 0;
314   /* Maximum number of frames that can be requested in a callback. */
315   uint32_t output_buffer_frame_count = 0;
316   /* Resampler instance. Resampling will only happen if necessary. */
317   std::unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler = { nullptr, cubeb_resampler_destroy };
318   /* Mixer interfaces */
319   std::unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> output_mixer = { nullptr, cubeb_mixer_destroy };
320   std::unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> input_mixer = { nullptr, cubeb_mixer_destroy };
321   /* A buffer for up/down mixing multi-channel audio output. */
322   std::vector<BYTE> mix_buffer;
323   /* WASAPI input works in "packets". We re-linearize the audio packets
324    * into this buffer before handing it to the resampler. */
325   std::unique_ptr<auto_array_wrapper> linear_input_buffer;
326   /* Bytes per sample. This multiplied by the number of channels is the number
327    * of bytes per frame. */
328   size_t bytes_per_sample = 0;
329   /* WAVEFORMATEXTENSIBLE sub-format: either PCM or float. */
330   GUID waveformatextensible_sub_format = GUID_NULL;
331   /* Stream volume.  Set via stream_set_volume and used to reset volume on
332      device changes. */
333   float volume = 1.0;
334   /* True if the stream is draining. */
335   bool draining = false;
336   /* True when we've destroyed the stream. This pointer is leaked on stream
337    * destruction if we could not join the thread. */
338   std::atomic<std::atomic<bool>*> emergency_bailout { nullptr };
339   /* Synchronizes render thread start to ensure safe access to emergency_bailout. */
340   HANDLE thread_ready_event = 0;
341   /* This needs an active audio input stream to be known, and is updated in the
342    * first audio input callback. */
343   std::atomic<int64_t> input_latency_hns { LATENCY_NOT_AVAILABLE_YET };
344 };
345 
346 class monitor_device_notifications {
347 public:
348   monitor_device_notifications(cubeb * context)
349   : cubeb_context(context)
350   {
351     create_thread();
352   }
353 
354   ~monitor_device_notifications()
355   {
356     SetEvent(begin_shutdown);
357     WaitForSingleObject(shutdown_complete, INFINITE);
358     CloseHandle(thread);
359 
360     CloseHandle(input_changed);
361     CloseHandle(output_changed);
362     CloseHandle(begin_shutdown);
363     CloseHandle(shutdown_complete);
364   }
365 
366   void notify(EDataFlow flow)
367   {
368     XASSERT(cubeb_context);
369     if (flow == eCapture && cubeb_context->input_collection_changed_callback) {
370       bool res = SetEvent(input_changed);
371       if (!res) {
372         LOG("Failed to set input changed event");
373       }
374       return;
375     }
376     if (flow == eRender && cubeb_context->output_collection_changed_callback) {
377       bool res = SetEvent(output_changed);
378       if (!res) {
379         LOG("Failed to set output changed event");
380       }
381     }
382   }
383 private:
384   static unsigned int __stdcall
385   thread_proc(LPVOID args)
386   {
387     XASSERT(args);
388     auto mdn = static_cast<monitor_device_notifications*>(args);
389     mdn->notification_thread_loop();
390     SetEvent(mdn->shutdown_complete);
391     return 0;
392   }
393 
394   void notification_thread_loop()
395   {
396     struct auto_com {
397       auto_com() {
398         HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
399         XASSERT(SUCCEEDED(hr));
400       }
401       ~auto_com() {
402         CoUninitialize();
403       }
404     } com;
405 
406     HANDLE wait_array[3] = {
407       input_changed,
408       output_changed,
409       begin_shutdown,
410     };
411 
412     while (true) {
413       Sleep(200);
414 
415       DWORD wait_result = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
416                                                  wait_array,
417                                                  FALSE,
418                                                  INFINITE);
419       if (wait_result == WAIT_OBJECT_0) { // input changed
420         cubeb_context->input_collection_changed_callback(cubeb_context,
421           cubeb_context->input_collection_changed_user_ptr);
422       } else if (wait_result == WAIT_OBJECT_0 + 1) { // output changed
423         cubeb_context->output_collection_changed_callback(cubeb_context,
424           cubeb_context->output_collection_changed_user_ptr);
425       } else if (wait_result == WAIT_OBJECT_0 + 2) { // shutdown
426         break;
427       } else {
428         LOG("Unexpected result %lu", wait_result);
429       }
430     } // loop
431   }
432 
433   void create_thread()
434   {
435     output_changed = CreateEvent(nullptr, 0, 0, nullptr);
436     if (!output_changed) {
437       LOG("Failed to create output changed event.");
438       return;
439     }
440 
441     input_changed = CreateEvent(nullptr, 0, 0, nullptr);
442     if (!input_changed) {
443       LOG("Failed to create input changed event.");
444       return;
445     }
446 
447     begin_shutdown = CreateEvent(nullptr, 0, 0, nullptr);
448     if (!begin_shutdown) {
449       LOG("Failed to create begin_shutdown event.");
450       return;
451     }
452 
453     shutdown_complete = CreateEvent(nullptr, 0, 0, nullptr);
454     if (!shutdown_complete) {
455       LOG("Failed to create shutdown_complete event.");
456       return;
457     }
458 
459     thread = (HANDLE) _beginthreadex(nullptr,
460                                      256 * 1024,
461                                      thread_proc,
462                                      this,
463                                      STACK_SIZE_PARAM_IS_A_RESERVATION,
464                                      nullptr);
465     if (!thread) {
466       LOG("Failed to create thread.");
467       return;
468     }
469   }
470 
471   HANDLE thread = INVALID_HANDLE_VALUE;
472   HANDLE output_changed = INVALID_HANDLE_VALUE;
473   HANDLE input_changed = INVALID_HANDLE_VALUE;
474   HANDLE begin_shutdown = INVALID_HANDLE_VALUE;
475   HANDLE shutdown_complete = INVALID_HANDLE_VALUE;
476 
477   cubeb * cubeb_context = nullptr;
478 };
479 
480 class wasapi_collection_notification_client : public IMMNotificationClient
481 {
482 public:
483   /* The implementation of MSCOM was copied from MSDN. */
484   ULONG STDMETHODCALLTYPE
485   AddRef()
486   {
487     return InterlockedIncrement(&ref_count);
488   }
489 
490   ULONG STDMETHODCALLTYPE
491   Release()
492   {
493     ULONG ulRef = InterlockedDecrement(&ref_count);
494     if (0 == ulRef) {
495       delete this;
496     }
497     return ulRef;
498   }
499 
500   HRESULT STDMETHODCALLTYPE
501   QueryInterface(REFIID riid, VOID **ppvInterface)
502   {
503     if (__uuidof(IUnknown) == riid) {
504       AddRef();
505       *ppvInterface = (IUnknown*)this;
506     } else if (__uuidof(IMMNotificationClient) == riid) {
507       AddRef();
508       *ppvInterface = (IMMNotificationClient*)this;
509     } else {
510       *ppvInterface = NULL;
511       return E_NOINTERFACE;
512     }
513     return S_OK;
514   }
515 
516   wasapi_collection_notification_client(cubeb * context)
517     : ref_count(1)
518     , cubeb_context(context)
519     , monitor_notifications(context)
520   {
521     XASSERT(cubeb_context);
522   }
523 
524   virtual ~wasapi_collection_notification_client()
525   { }
526 
527   HRESULT STDMETHODCALLTYPE
528   OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
529   {
530     LOG("collection: Audio device default changed, id = %S.", device_id);
531     return S_OK;
532   }
533 
534   /* The remaining methods are not implemented, they simply log when called (if
535      log is enabled), for debugging. */
536   HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
537   {
538     LOG("collection: Audio device added.");
539     return S_OK;
540   };
541 
542   HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
543   {
544     LOG("collection: Audio device removed.");
545     return S_OK;
546   }
547 
548   HRESULT STDMETHODCALLTYPE
549   OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
550   {
551     XASSERT(cubeb_context->output_collection_changed_callback ||
552             cubeb_context->input_collection_changed_callback);
553     LOG("collection: Audio device state changed, id = %S, state = %lu.", device_id, new_state);
554     EDataFlow flow;
555     HRESULT hr = GetDataFlow(device_id, &flow);
556     if (FAILED(hr)) {
557       return hr;
558     }
559     monitor_notifications.notify(flow);
560     return S_OK;
561   }
562 
563   HRESULT STDMETHODCALLTYPE
564   OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
565   {
566     //Audio device property value changed.
567     return S_OK;
568   }
569 
570 private:
571   HRESULT GetDataFlow(LPCWSTR device_id, EDataFlow * flow)
572   {
573     com_ptr<IMMDevice> device;
574     com_ptr<IMMEndpoint> endpoint;
575 
576     HRESULT hr = cubeb_context->device_collection_enumerator
577                    ->GetDevice(device_id, device.receive());
578     if (FAILED(hr)) {
579       LOG("collection: Could not get device: %lx", hr);
580       return hr;
581     }
582 
583     hr = device->QueryInterface(IID_PPV_ARGS(endpoint.receive()));
584     if (FAILED(hr)) {
585       LOG("collection: Could not get endpoint: %lx", hr);
586       return hr;
587     }
588 
589     return endpoint->GetDataFlow(flow);
590   }
591 
592   /* refcount for this instance, necessary to implement MSCOM semantics. */
593   LONG ref_count;
594 
595   cubeb * cubeb_context = nullptr;
596   monitor_device_notifications monitor_notifications;
597 };
598 
599 class wasapi_endpoint_notification_client : public IMMNotificationClient
600 {
601 public:
602   /* The implementation of MSCOM was copied from MSDN. */
603   ULONG STDMETHODCALLTYPE
604   AddRef()
605   {
606     return InterlockedIncrement(&ref_count);
607   }
608 
609   ULONG STDMETHODCALLTYPE
610   Release()
611   {
612     ULONG ulRef = InterlockedDecrement(&ref_count);
613     if (0 == ulRef) {
614       delete this;
615     }
616     return ulRef;
617   }
618 
619   HRESULT STDMETHODCALLTYPE
620   QueryInterface(REFIID riid, VOID **ppvInterface)
621   {
622     if (__uuidof(IUnknown) == riid) {
623       AddRef();
624       *ppvInterface = (IUnknown*)this;
625     } else if (__uuidof(IMMNotificationClient) == riid) {
626       AddRef();
627       *ppvInterface = (IMMNotificationClient*)this;
628     } else {
629       *ppvInterface = NULL;
630       return E_NOINTERFACE;
631     }
632     return S_OK;
633   }
634 
635   wasapi_endpoint_notification_client(HANDLE event, ERole role)
636     : ref_count(1)
637     , reconfigure_event(event)
638     , role(role)
639   { }
640 
641   virtual ~wasapi_endpoint_notification_client()
642   { }
643 
644   HRESULT STDMETHODCALLTYPE
645   OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
646   {
647     LOG("endpoint: Audio device default changed.");
648 
649     /* we only support a single stream type for now. */
650     if (flow != eRender && role != this->role) {
651       return S_OK;
652     }
653 
654     BOOL ok = SetEvent(reconfigure_event);
655     if (!ok) {
656       LOG("endpoint: SetEvent on reconfigure_event failed: %lx", GetLastError());
657     }
658 
659     return S_OK;
660   }
661 
662   /* The remaining methods are not implemented, they simply log when called (if
663      log is enabled), for debugging. */
664   HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
665   {
666     LOG("endpoint: Audio device added.");
667     return S_OK;
668   };
669 
670   HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
671   {
672     LOG("endpoint: Audio device removed.");
673     return S_OK;
674   }
675 
676   HRESULT STDMETHODCALLTYPE
677   OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
678   {
679     LOG("endpoint: Audio device state changed.");
680     return S_OK;
681   }
682 
683   HRESULT STDMETHODCALLTYPE
684   OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
685   {
686     //Audio device property value changed.
687     return S_OK;
688   }
689 private:
690   /* refcount for this instance, necessary to implement MSCOM semantics. */
691   LONG ref_count;
692   HANDLE reconfigure_event;
693   ERole role;
694 };
695 
696 namespace {
697 
698 char const *
699 intern_device_id(cubeb * ctx, wchar_t const * id)
700 {
701   XASSERT(id);
702 
703   char const * tmp = wstr_to_utf8(id);
704   if (!tmp)
705     return nullptr;
706 
707   char const * interned = cubeb_strings_intern(ctx->device_ids, tmp);
708 
709   free((void *) tmp);
710 
711   return interned;
712 }
713 
714 bool has_input(cubeb_stream * stm)
715 {
716   return stm->input_stream_params.rate != 0;
717 }
718 
719 bool has_output(cubeb_stream * stm)
720 {
721   return stm->output_stream_params.rate != 0;
722 }
723 
724 double stream_to_mix_samplerate_ratio(cubeb_stream_params & stream, cubeb_stream_params & mixer)
725 {
726   return double(stream.rate) / mixer.rate;
727 }
728 
729 /* Convert the channel layout into the corresponding KSAUDIO_CHANNEL_CONFIG.
730    See more: https://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx */
731 
732 cubeb_channel_layout
733 mask_to_channel_layout(WAVEFORMATEX const * fmt)
734 {
735   cubeb_channel_layout mask = 0;
736 
737   if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
738     WAVEFORMATEXTENSIBLE const * ext = reinterpret_cast<WAVEFORMATEXTENSIBLE const *>(fmt);
739     mask = ext->dwChannelMask;
740   } else if (fmt->wFormatTag == WAVE_FORMAT_PCM ||
741              fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
742     if (fmt->nChannels == 1) {
743       mask = CHANNEL_FRONT_CENTER;
744     } else if (fmt->nChannels == 2) {
745       mask = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT;
746     }
747   }
748   return mask;
749 }
750 
751 uint32_t
752 get_rate(cubeb_stream * stm)
753 {
754   return has_input(stm) ? stm->input_stream_params.rate
755                         : stm->output_stream_params.rate;
756 }
757 
758 uint32_t
759 hns_to_frames(uint32_t rate, REFERENCE_TIME hns)
760 {
761   return std::ceil((hns - 1) / 10000000.0 * rate);
762 }
763 
764 uint32_t
765 hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns)
766 {
767   return hns_to_frames(get_rate(stm), hns);
768 }
769 
770 REFERENCE_TIME
771 frames_to_hns(cubeb_stream * stm, uint32_t frames)
772 {
773   return std::ceil(frames * 10000000.0 / get_rate(stm));
774 }
775 
776 /* This returns the size of a frame in the stream, before the eventual upmix
777    occurs. */
778 static size_t
779 frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames)
780 {
781   // This is called only when we has a output client.
782   XASSERT(has_output(stm));
783   return stm->output_stream_params.channels * stm->bytes_per_sample * frames;
784 }
785 
786 /* This function handles the processing of the input and output audio,
787  * converting it to rate and channel layout specified at initialization.
788  * It then calls the data callback, via the resampler. */
789 long
790 refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
791        void * output_buffer, long output_frames_needed)
792 {
793   XASSERT(!stm->draining);
794   /* If we need to upmix after resampling, resample into the mix buffer to
795      avoid a copy. Avoid exposing output if it is a dummy stream. */
796   void * dest = nullptr;
797   if (has_output(stm) && !stm->has_dummy_output) {
798     if (stm->output_mixer) {
799       dest = stm->mix_buffer.data();
800     } else {
801       dest = output_buffer;
802     }
803   }
804 
805   long out_frames = cubeb_resampler_fill(stm->resampler.get(),
806                                          input_buffer,
807                                          &input_frames_count,
808                                          dest,
809                                          output_frames_needed);
810   /* TODO: Report out_frames < 0 as an error via the API. */
811   XASSERT(out_frames >= 0);
812 
813   {
814     auto_lock lock(stm->stream_reset_lock);
815     stm->frames_written += out_frames;
816   }
817 
818   /* Go in draining mode if we got fewer frames than requested. If the stream
819      has no output we still expect the callback to return number of frames read
820      from input, otherwise we stop. */
821   if ((out_frames < output_frames_needed) ||
822       (!has_output(stm) && out_frames < input_frames_count)) {
823     LOG("start draining.");
824     stm->draining = true;
825   }
826 
827   /* If this is not true, there will be glitches.
828      It is alright to have produced less frames if we are draining, though. */
829   XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm) || stm->has_dummy_output);
830 
831   // We don't bother mixing dummy output as it will be silenced, otherwise mix output if needed
832   if (!stm->has_dummy_output && has_output(stm) && stm->output_mixer) {
833     XASSERT(dest == stm->mix_buffer.data());
834     size_t dest_size =
835       out_frames * stm->output_stream_params.channels * stm->bytes_per_sample;
836     XASSERT(dest_size <= stm->mix_buffer.size());
837     size_t output_buffer_size =
838       out_frames * stm->output_mix_params.channels * stm->bytes_per_sample;
839     int ret = cubeb_mixer_mix(stm->output_mixer.get(),
840                               out_frames,
841                               dest,
842                               dest_size,
843                               output_buffer,
844                               output_buffer_size);
845     if (ret < 0) {
846       LOG("Error remixing content (%d)", ret);
847     }
848   }
849 
850   return out_frames;
851 }
852 
853 int wasapi_stream_reset_default_device(cubeb_stream * stm);
854 
855 /* This helper grabs all the frames available from a capture client, put them in
856  * linear_input_buffer. linear_input_buffer should be cleared before the
857  * callback exits. This helper does not work with exclusive mode streams. */
858 bool get_input_buffer(cubeb_stream * stm)
859 {
860   XASSERT(has_input(stm));
861 
862   HRESULT hr;
863   BYTE * input_packet = NULL;
864   DWORD flags;
865   UINT64 dev_pos;
866   UINT64 pc_position;
867   UINT32 next;
868   /* Get input packets until we have captured enough frames, and put them in a
869    * contiguous buffer. */
870   uint32_t offset = 0;
871   // If the input stream is event driven we should only ever expect to read a
872   // single packet each time. However, if we're pulling from the stream we may
873   // need to grab multiple packets worth of frames that have accumulated (so
874   // need a loop).
875   for (hr = stm->capture_client->GetNextPacketSize(&next);
876        next > 0;
877        hr = stm->capture_client->GetNextPacketSize(&next)) {
878     if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
879       // Application can recover from this error. More info
880       // https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx
881       LOG("Device invalidated error, reset default device");
882       wasapi_stream_reset_default_device(stm);
883       return true;
884     }
885 
886     if (FAILED(hr)) {
887       LOG("cannot get next packet size: %lx", hr);
888       return false;
889     }
890 
891     UINT32 frames;
892     hr = stm->capture_client->GetBuffer(&input_packet,
893                                         &frames,
894                                         &flags,
895                                         &dev_pos,
896                                         &pc_position);
897 
898     if (FAILED(hr)) {
899       LOG("GetBuffer failed for capture: %lx", hr);
900       return false;
901     }
902     XASSERT(frames == next);
903 
904     if (stm->context->performance_counter_frequency) {
905       LARGE_INTEGER now;
906       UINT64 now_hns;
907       // See https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudiocaptureclient-getbuffer, section "Remarks".
908       QueryPerformanceCounter(&now);
909       now_hns = 10000000 * now.QuadPart / stm->context->performance_counter_frequency;
910       if (now_hns >= pc_position) {
911         stm->input_latency_hns = now_hns - pc_position;
912       }
913     }
914 
915     UINT32 input_stream_samples = frames * stm->input_stream_params.channels;
916     // We do not explicitly handle the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
917     // flag. There a two primary (non exhaustive) scenarios we anticipate this
918     // flag being set in:
919     //   - The first GetBuffer after Start has this flag undefined. In this
920     //     case the flag may be set but is meaningless and can be ignored.
921     //   - If a glitch is introduced into the input. This should not happen
922     //     for event based inputs, and should be mitigated by using a dummy
923     //     stream to drive input in the case of input only loopback. Without
924     //     a dummy output, input only loopback would glitch on silence. However,
925     //     the dummy input should push silence to the loopback and prevent
926     //     discontinuities. See https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/
927     // As the first scenario can be ignored, and we anticipate the second
928     // scenario is mitigated, we ignore the flag.
929     // For more info: https://msdn.microsoft.com/en-us/library/windows/desktop/dd370859(v=vs.85).aspx,
930     // https://msdn.microsoft.com/en-us/library/windows/desktop/dd371458(v=vs.85).aspx
931     if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
932       LOG("insert silence: ps=%u", frames);
933       stm->linear_input_buffer->push_silence(input_stream_samples);
934     } else {
935       if (stm->input_mixer) {
936         bool ok = stm->linear_input_buffer->reserve(
937           stm->linear_input_buffer->length() + input_stream_samples);
938         XASSERT(ok);
939         size_t input_packet_size =
940           frames * stm->input_mix_params.channels *
941           cubeb_sample_size(stm->input_mix_params.format);
942         size_t linear_input_buffer_size =
943           input_stream_samples *
944           cubeb_sample_size(stm->input_stream_params.format);
945         cubeb_mixer_mix(stm->input_mixer.get(),
946                         frames,
947                         input_packet,
948                         input_packet_size,
949                         stm->linear_input_buffer->end(),
950                         linear_input_buffer_size);
951         stm->linear_input_buffer->set_length(
952           stm->linear_input_buffer->length() + input_stream_samples);
953       } else {
954         stm->linear_input_buffer->push(
955           input_packet, input_stream_samples);
956       }
957     }
958     hr = stm->capture_client->ReleaseBuffer(frames);
959     if (FAILED(hr)) {
960       LOG("FAILED to release intput buffer");
961       return false;
962     }
963     offset += input_stream_samples;
964   }
965 
966   XASSERT(stm->linear_input_buffer->length() >= offset);
967 
968   return true;
969 }
970 
971 /* Get an output buffer from the render_client. It has to be released before
972  * exiting the callback. */
973 bool get_output_buffer(cubeb_stream * stm, void *& buffer, size_t & frame_count)
974 {
975   UINT32 padding_out;
976   HRESULT hr;
977 
978   XASSERT(has_output(stm));
979 
980   hr = stm->output_client->GetCurrentPadding(&padding_out);
981   if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
982       // Application can recover from this error. More info
983       // https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx
984       LOG("Device invalidated error, reset default device");
985       wasapi_stream_reset_default_device(stm);
986       return true;
987   }
988 
989   if (FAILED(hr)) {
990     LOG("Failed to get padding: %lx", hr);
991     return false;
992   }
993 
994   XASSERT(padding_out <= stm->output_buffer_frame_count);
995 
996   if (stm->draining) {
997     if (padding_out == 0) {
998       LOG("Draining finished.");
999       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
1000       return false;
1001     }
1002     LOG("Draining.");
1003     return true;
1004   }
1005 
1006   frame_count = stm->output_buffer_frame_count - padding_out;
1007   BYTE * output_buffer;
1008 
1009   hr = stm->render_client->GetBuffer(frame_count, &output_buffer);
1010   if (FAILED(hr)) {
1011     LOG("cannot get render buffer");
1012     return false;
1013   }
1014 
1015   buffer = output_buffer;
1016 
1017   return true;
1018 }
1019 
1020 /**
1021  * This function gets input data from a input device, and pass it along with an
1022  * output buffer to the resamplers.  */
1023 bool
1024 refill_callback_duplex(cubeb_stream * stm)
1025 {
1026   HRESULT hr;
1027   void * output_buffer = nullptr;
1028   size_t output_frames = 0;
1029   size_t input_frames;
1030   bool rv;
1031 
1032   XASSERT(has_input(stm) && has_output(stm));
1033 
1034   rv = get_input_buffer(stm);
1035   if (!rv) {
1036     return rv;
1037   }
1038 
1039   input_frames = stm->linear_input_buffer->length() / stm->input_stream_params.channels;
1040   if (!input_frames) {
1041     return true;
1042   }
1043 
1044   rv = get_output_buffer(stm, output_buffer, output_frames);
1045   if (!rv) {
1046     hr = stm->render_client->ReleaseBuffer(output_frames, 0);
1047     return rv;
1048   }
1049 
1050   /* This can only happen when debugging, and having breakpoints set in the
1051    * callback in a way that it makes the stream underrun. */
1052   if (output_frames == 0) {
1053     return true;
1054   }
1055 
1056   /* Wait for draining is not important on duplex. */
1057   if (stm->draining) {
1058     return false;
1059   }
1060 
1061   if (stm->has_dummy_output) {
1062     ALOGV("Duplex callback (dummy output): input frames: %Iu, output frames: %Iu",
1063           input_frames, output_frames);
1064 
1065     // We don't want to expose the dummy output to the callback so don't pass
1066     // the output buffer (it will be released later with silence in it)
1067     refill(stm,
1068            stm->linear_input_buffer->data(),
1069            input_frames,
1070            nullptr,
1071            0);
1072   } else {
1073     ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
1074           input_frames, output_frames);
1075 
1076     refill(stm,
1077            stm->linear_input_buffer->data(),
1078            input_frames,
1079            output_buffer,
1080            output_frames);
1081   }
1082 
1083   stm->linear_input_buffer->clear();
1084 
1085   if (stm->has_dummy_output) {
1086     // If output is a dummy output, make sure it's silent
1087     hr = stm->render_client->ReleaseBuffer(output_frames, AUDCLNT_BUFFERFLAGS_SILENT);
1088   } else {
1089     hr = stm->render_client->ReleaseBuffer(output_frames, 0);
1090   }
1091   if (FAILED(hr)) {
1092     LOG("failed to release buffer: %lx", hr);
1093     return false;
1094   }
1095   return true;
1096 }
1097 
1098 bool
1099 refill_callback_input(cubeb_stream * stm)
1100 {
1101   bool rv;
1102   size_t input_frames;
1103 
1104   XASSERT(has_input(stm) && !has_output(stm));
1105 
1106   rv = get_input_buffer(stm);
1107   if (!rv) {
1108     return rv;
1109   }
1110 
1111   input_frames = stm->linear_input_buffer->length() / stm->input_stream_params.channels;
1112   if (!input_frames) {
1113     return true;
1114   }
1115 
1116   ALOGV("Input callback: input frames: %Iu", input_frames);
1117 
1118   long read = refill(stm,
1119                      stm->linear_input_buffer->data(),
1120                      input_frames,
1121                      nullptr,
1122                      0);
1123 
1124   XASSERT(read >= 0);
1125 
1126   stm->linear_input_buffer->clear();
1127 
1128   return !stm->draining;
1129 }
1130 
1131 bool
1132 refill_callback_output(cubeb_stream * stm)
1133 {
1134   bool rv;
1135   HRESULT hr;
1136   void * output_buffer = nullptr;
1137   size_t output_frames = 0;
1138 
1139   XASSERT(!has_input(stm) && has_output(stm));
1140 
1141   rv = get_output_buffer(stm, output_buffer, output_frames);
1142   if (!rv) {
1143     return rv;
1144   }
1145 
1146   if (stm->draining || output_frames == 0) {
1147     return true;
1148   }
1149 
1150   long got = refill(stm,
1151                     nullptr,
1152                     0,
1153                     output_buffer,
1154                     output_frames);
1155 
1156   ALOGV("Output callback: output frames requested: %Iu, got %ld",
1157         output_frames, got);
1158 
1159   XASSERT(got >= 0);
1160   XASSERT(size_t(got) == output_frames || stm->draining);
1161 
1162   hr = stm->render_client->ReleaseBuffer(got, 0);
1163   if (FAILED(hr)) {
1164     LOG("failed to release buffer: %lx", hr);
1165     return false;
1166   }
1167 
1168   return size_t(got) == output_frames || stm->draining;
1169 }
1170 
1171 static unsigned int __stdcall
1172 wasapi_stream_render_loop(LPVOID stream)
1173 {
1174   cubeb_stream * stm = static_cast<cubeb_stream *>(stream);
1175   std::atomic<bool> * emergency_bailout = stm->emergency_bailout;
1176 
1177   // Signal wasapi_stream_start that we've copied emergency_bailout.
1178   BOOL ok = SetEvent(stm->thread_ready_event);
1179   if (!ok) {
1180     LOG("thread_ready SetEvent failed: %lx", GetLastError());
1181     return 0;
1182   }
1183 
1184   bool is_playing = true;
1185   HANDLE wait_array[4] = {
1186     stm->shutdown_event,
1187     stm->reconfigure_event,
1188     stm->refill_event,
1189     stm->input_available_event
1190   };
1191   HANDLE mmcss_handle = NULL;
1192   HRESULT hr = 0;
1193   DWORD mmcss_task_index = 0;
1194   struct auto_com {
1195     auto_com() {
1196       HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
1197       XASSERT(SUCCEEDED(hr));
1198     }
1199     ~auto_com() {
1200       CoUninitialize();
1201     }
1202   } com;
1203 
1204   /* We could consider using "Pro Audio" here for WebAudio and
1205      maybe WebRTC. */
1206   mmcss_handle = AvSetMmThreadCharacteristicsA("Audio", &mmcss_task_index);
1207   if (!mmcss_handle) {
1208     /* This is not fatal, but we might glitch under heavy load. */
1209     LOG("Unable to use mmcss to bump the render thread priority: %lx", GetLastError());
1210   }
1211 
1212   /* WaitForMultipleObjects timeout can trigger in cases where we don't want to
1213      treat it as a timeout, such as across a system sleep/wake cycle.  Trigger
1214      the timeout error handling only when the timeout_limit is reached, which is
1215      reset on each successful loop. */
1216   unsigned timeout_count = 0;
1217   const unsigned timeout_limit = 3;
1218   while (is_playing) {
1219     // We want to check the emergency bailout variable before a
1220     // and after the WaitForMultipleObject, because the handles WaitForMultipleObjects
1221     // is going to wait on might have been closed already.
1222     if (*emergency_bailout) {
1223       delete emergency_bailout;
1224       return 0;
1225     }
1226     DWORD waitResult = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
1227                                               wait_array,
1228                                               FALSE,
1229                                               1000);
1230     if (*emergency_bailout) {
1231       delete emergency_bailout;
1232       return 0;
1233     }
1234     if (waitResult != WAIT_TIMEOUT) {
1235       timeout_count = 0;
1236     }
1237     switch (waitResult) {
1238     case WAIT_OBJECT_0: { /* shutdown */
1239       is_playing = false;
1240       /* We don't check if the drain is actually finished here, we just want to
1241          shutdown. */
1242       if (stm->draining) {
1243         stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
1244       }
1245       continue;
1246     }
1247     case WAIT_OBJECT_0 + 1: { /* reconfigure */
1248       XASSERT(stm->output_client || stm->input_client);
1249       LOG("Reconfiguring the stream");
1250       /* Close the stream */
1251       if (stm->output_client) {
1252         stm->output_client->Stop();
1253         LOG("Output stopped.");
1254       }
1255       if (stm->input_client) {
1256         stm->input_client->Stop();
1257         LOG("Input stopped.");
1258       }
1259       {
1260         auto_lock lock(stm->stream_reset_lock);
1261         close_wasapi_stream(stm);
1262         LOG("Stream closed.");
1263         /* Reopen a stream and start it immediately. This will automatically pick the
1264            new default device for this role. */
1265         int r = setup_wasapi_stream(stm);
1266         if (r != CUBEB_OK) {
1267           LOG("Error setting up the stream during reconfigure.");
1268           /* Don't destroy the stream here, since we expect the caller to do
1269              so after the error has propagated via the state callback. */
1270           is_playing = false;
1271           hr = E_FAIL;
1272           continue;
1273         }
1274         LOG("Stream setup successfuly.");
1275       }
1276       XASSERT(stm->output_client || stm->input_client);
1277       if (stm->output_client) {
1278         hr = stm->output_client->Start();
1279         if (FAILED(hr)) {
1280           LOG("Error starting output after reconfigure, error: %lx", hr);
1281           is_playing = false;
1282           continue;
1283         }
1284         LOG("Output started after reconfigure.");
1285       }
1286       if (stm->input_client) {
1287         hr = stm->input_client->Start();
1288         if (FAILED(hr)) {
1289           LOG("Error starting input after reconfiguring, error: %lx", hr);
1290           is_playing = false;
1291           continue;
1292         }
1293         LOG("Input started after reconfigure.");
1294       }
1295       break;
1296     }
1297     case WAIT_OBJECT_0 + 2:  /* refill */
1298       XASSERT((has_input(stm) && has_output(stm)) ||
1299               (!has_input(stm) && has_output(stm)));
1300       is_playing = stm->refill_callback(stm);
1301       break;
1302     case WAIT_OBJECT_0 + 3: /* input available */
1303       if (has_input(stm) && has_output(stm)) { continue; }
1304       is_playing = stm->refill_callback(stm);
1305       break;
1306     case WAIT_TIMEOUT:
1307       XASSERT(stm->shutdown_event == wait_array[0]);
1308       if (++timeout_count >= timeout_limit) {
1309         LOG("Render loop reached the timeout limit.");
1310         is_playing = false;
1311         hr = E_FAIL;
1312       }
1313       break;
1314     default:
1315       LOG("case %lu not handled in render loop.", waitResult);
1316       abort();
1317     }
1318   }
1319 
1320   if (FAILED(hr)) {
1321     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
1322   }
1323 
1324   if (mmcss_handle) {
1325     AvRevertMmThreadCharacteristics(mmcss_handle);
1326   }
1327 
1328   return 0;
1329 }
1330 
1331 void wasapi_destroy(cubeb * context);
1332 
1333 HRESULT register_notification_client(cubeb_stream * stm)
1334 {
1335   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
1336                                 NULL, CLSCTX_INPROC_SERVER,
1337                                 IID_PPV_ARGS(stm->device_enumerator.receive()));
1338   if (FAILED(hr)) {
1339     LOG("Could not get device enumerator: %lx", hr);
1340     return hr;
1341   }
1342 
1343   stm->notification_client.reset(new wasapi_endpoint_notification_client(stm->reconfigure_event, stm->role));
1344 
1345   hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client.get());
1346   if (FAILED(hr)) {
1347     LOG("Could not register endpoint notification callback: %lx", hr);
1348     stm->notification_client = nullptr;
1349     stm->device_enumerator = nullptr;
1350   }
1351 
1352   return hr;
1353 }
1354 
1355 HRESULT unregister_notification_client(cubeb_stream * stm)
1356 {
1357   XASSERT(stm);
1358   HRESULT hr;
1359 
1360   if (!stm->device_enumerator) {
1361     return S_OK;
1362   }
1363 
1364   hr = stm->device_enumerator->UnregisterEndpointNotificationCallback(stm->notification_client.get());
1365   if (FAILED(hr)) {
1366     // We can't really do anything here, we'll probably leak the
1367     // notification client, but we can at least release the enumerator.
1368     stm->device_enumerator = nullptr;
1369     return S_OK;
1370   }
1371 
1372   stm->notification_client = nullptr;
1373   stm->device_enumerator = nullptr;
1374 
1375   return S_OK;
1376 }
1377 
1378 HRESULT get_endpoint(com_ptr<IMMDevice> & device, LPCWSTR devid)
1379 {
1380   com_ptr<IMMDeviceEnumerator> enumerator;
1381   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
1382                                 NULL, CLSCTX_INPROC_SERVER,
1383                                 IID_PPV_ARGS(enumerator.receive()));
1384   if (FAILED(hr)) {
1385     LOG("Could not get device enumerator: %lx", hr);
1386     return hr;
1387   }
1388 
1389   hr = enumerator->GetDevice(devid, device.receive());
1390   if (FAILED(hr)) {
1391     LOG("Could not get device: %lx", hr);
1392     return hr;
1393   }
1394 
1395   return S_OK;
1396 }
1397 
1398 HRESULT register_collection_notification_client(cubeb * context)
1399 {
1400   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
1401                                 NULL, CLSCTX_INPROC_SERVER,
1402                                 IID_PPV_ARGS(context->device_collection_enumerator.receive()));
1403   if (FAILED(hr)) {
1404     LOG("Could not get device enumerator: %lx", hr);
1405     return hr;
1406   }
1407 
1408   context->collection_notification_client.reset(new wasapi_collection_notification_client(context));
1409 
1410   hr = context->device_collection_enumerator->RegisterEndpointNotificationCallback(
1411                                                 context->collection_notification_client.get());
1412   if (FAILED(hr)) {
1413     LOG("Could not register endpoint notification callback: %lx", hr);
1414     context->collection_notification_client.reset();
1415     context->device_collection_enumerator.reset();
1416   }
1417 
1418   return hr;
1419 }
1420 
1421 HRESULT unregister_collection_notification_client(cubeb * context)
1422 {
1423   HRESULT hr = context->device_collection_enumerator->
1424     UnregisterEndpointNotificationCallback(context->collection_notification_client.get());
1425   if (FAILED(hr)) {
1426     return hr;
1427   }
1428 
1429   context->collection_notification_client = nullptr;
1430   context->device_collection_enumerator = nullptr;
1431 
1432   return hr;
1433 }
1434 
1435 HRESULT get_default_endpoint(com_ptr<IMMDevice> & device, EDataFlow direction, ERole role)
1436 {
1437   com_ptr<IMMDeviceEnumerator> enumerator;
1438   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
1439                                 NULL, CLSCTX_INPROC_SERVER,
1440                                 IID_PPV_ARGS(enumerator.receive()));
1441   if (FAILED(hr)) {
1442     LOG("Could not get device enumerator: %lx", hr);
1443     return hr;
1444   }
1445   hr = enumerator->GetDefaultAudioEndpoint(direction, role, device.receive());
1446   if (FAILED(hr)) {
1447     LOG("Could not get default audio endpoint: %lx", hr);
1448     return hr;
1449   }
1450 
1451   return ERROR_SUCCESS;
1452 }
1453 
1454 double
1455 current_stream_delay(cubeb_stream * stm)
1456 {
1457   stm->stream_reset_lock.assert_current_thread_owns();
1458 
1459   /* If the default audio endpoint went away during playback and we weren't
1460      able to configure a new one, it's possible the caller may call this
1461      before the error callback has propogated back. */
1462   if (!stm->audio_clock) {
1463     return 0;
1464   }
1465 
1466   UINT64 freq;
1467   HRESULT hr = stm->audio_clock->GetFrequency(&freq);
1468   if (FAILED(hr)) {
1469     LOG("GetFrequency failed: %lx", hr);
1470     return 0;
1471   }
1472 
1473   UINT64 pos;
1474   hr = stm->audio_clock->GetPosition(&pos, NULL);
1475   if (FAILED(hr)) {
1476     LOG("GetPosition failed: %lx", hr);
1477     return 0;
1478   }
1479 
1480   double cur_pos = static_cast<double>(pos) / freq;
1481   double max_pos = static_cast<double>(stm->frames_written)  / stm->output_mix_params.rate;
1482   double delay = max_pos - cur_pos;
1483   XASSERT(delay >= 0);
1484 
1485   return delay;
1486 }
1487 
1488 int
1489 stream_set_volume(cubeb_stream * stm, float volume)
1490 {
1491   stm->stream_reset_lock.assert_current_thread_owns();
1492 
1493   if (!stm->audio_stream_volume) {
1494     return CUBEB_ERROR;
1495   }
1496 
1497   uint32_t channels;
1498   HRESULT hr = stm->audio_stream_volume->GetChannelCount(&channels);
1499   if (FAILED(hr)) {
1500     LOG("could not get the channel count: %lx", hr);
1501     return CUBEB_ERROR;
1502   }
1503 
1504   /* up to 9.1 for now */
1505   if (channels > 10) {
1506     return CUBEB_ERROR_NOT_SUPPORTED;
1507   }
1508 
1509   float volumes[10];
1510   for (uint32_t i = 0; i < channels; i++) {
1511     volumes[i] = volume;
1512   }
1513 
1514   hr = stm->audio_stream_volume->SetAllVolumes(channels,  volumes);
1515   if (FAILED(hr)) {
1516     LOG("could not set the channels volume: %lx", hr);
1517     return CUBEB_ERROR;
1518   }
1519 
1520   return CUBEB_OK;
1521 }
1522 } // namespace anonymous
1523 
1524 extern "C" {
1525 int wasapi_init(cubeb ** context, char const * context_name)
1526 {
1527   /* We don't use the device yet, but need to make sure we can initialize one
1528      so that this backend is not incorrectly enabled on platforms that don't
1529      support WASAPI. */
1530   com_ptr<IMMDevice> device;
1531   HRESULT hr = get_default_endpoint(device, eRender, eConsole);
1532   if (FAILED(hr)) {
1533     XASSERT(hr != CO_E_NOTINITIALIZED);
1534     LOG("It wasn't able to find a default rendering device: %lx", hr);
1535     hr = get_default_endpoint(device, eCapture, eConsole);
1536     if (FAILED(hr)) {
1537       LOG("It wasn't able to find a default capture device: %lx", hr);
1538       return CUBEB_ERROR;
1539     }
1540   }
1541 
1542   cubeb * ctx = new cubeb();
1543 
1544   ctx->ops = &wasapi_ops;
1545   if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
1546     delete ctx;
1547     return CUBEB_ERROR;
1548   }
1549 
1550   LARGE_INTEGER frequency;
1551   if (QueryPerformanceFrequency(&frequency)) {
1552     LOG("Failed getting performance counter frequency, latency reporting will be inacurate");
1553     ctx->performance_counter_frequency = 0;
1554   } else {
1555     ctx->performance_counter_frequency = frequency.QuadPart;
1556   }
1557 
1558   *context = ctx;
1559 
1560   return CUBEB_OK;
1561 }
1562 }
1563 
1564 namespace {
1565 bool stop_and_join_render_thread(cubeb_stream * stm)
1566 {
1567   bool rv = true;
1568   LOG("Stop and join render thread.");
1569   if (!stm->thread) {
1570     LOG("No thread present.");
1571     return true;
1572   }
1573 
1574   // If we've already leaked the thread, just return,
1575   // there is not much we can do.
1576   if (!stm->emergency_bailout.load()) {
1577     return false;
1578   }
1579 
1580   BOOL ok = SetEvent(stm->shutdown_event);
1581   if (!ok) {
1582     LOG("Destroy SetEvent failed: %lx", GetLastError());
1583   }
1584 
1585   /* Wait five seconds for the rendering thread to return. It's supposed to
1586    * check its event loop very often, five seconds is rather conservative. */
1587   DWORD r = WaitForSingleObject(stm->thread, 5000);
1588   if (r != WAIT_OBJECT_0) {
1589     /* Something weird happened, leak the thread and continue the shutdown
1590      * process. */
1591     *(stm->emergency_bailout) = true;
1592     // We give the ownership to the rendering thread.
1593     stm->emergency_bailout = nullptr;
1594     LOG("Destroy WaitForSingleObject on thread failed: %lx, %lx", r, GetLastError());
1595     rv = false;
1596   }
1597 
1598   // Only attempts to close and null out the thread and event if the
1599   // WaitForSingleObject above succeeded, so that calling this function again
1600   // attemps to clean up the thread and event each time.
1601   if (rv) {
1602     LOG("Closing thread.");
1603     CloseHandle(stm->thread);
1604     stm->thread = NULL;
1605 
1606     CloseHandle(stm->shutdown_event);
1607     stm->shutdown_event = 0;
1608   }
1609 
1610   return rv;
1611 }
1612 
1613 void wasapi_destroy(cubeb * context)
1614 {
1615   if (context->device_ids) {
1616     cubeb_strings_destroy(context->device_ids);
1617   }
1618 
1619   delete context;
1620 }
1621 
1622 char const * wasapi_get_backend_id(cubeb * context)
1623 {
1624   return "wasapi";
1625 }
1626 
1627 int
1628 wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
1629 {
1630   XASSERT(ctx && max_channels);
1631 
1632   com_ptr<IMMDevice> device;
1633   HRESULT hr = get_default_endpoint(device, eRender, eConsole);
1634   if (FAILED(hr)) {
1635     return CUBEB_ERROR;
1636   }
1637 
1638   com_ptr<IAudioClient> client;
1639   hr = device->Activate(__uuidof(IAudioClient),
1640                         CLSCTX_INPROC_SERVER,
1641                         NULL, client.receive_vpp());
1642   if (FAILED(hr)) {
1643     return CUBEB_ERROR;
1644   }
1645 
1646   WAVEFORMATEX * tmp = nullptr;
1647   hr = client->GetMixFormat(&tmp);
1648   if (FAILED(hr)) {
1649     return CUBEB_ERROR;
1650   }
1651   com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
1652 
1653   *max_channels = mix_format->nChannels;
1654 
1655   return CUBEB_OK;
1656 }
1657 
1658 int
1659 wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
1660 {
1661   if (params.format != CUBEB_SAMPLE_FLOAT32NE && params.format != CUBEB_SAMPLE_S16NE) {
1662     return CUBEB_ERROR_INVALID_FORMAT;
1663   }
1664 
1665   ERole role = pref_to_role(params.prefs);
1666 
1667   com_ptr<IMMDevice> device;
1668   HRESULT hr = get_default_endpoint(device, eRender, role);
1669   if (FAILED(hr)) {
1670     LOG("Could not get default endpoint: %lx", hr);
1671     return CUBEB_ERROR;
1672   }
1673 
1674   com_ptr<IAudioClient> client;
1675   hr = device->Activate(__uuidof(IAudioClient),
1676                         CLSCTX_INPROC_SERVER,
1677                         NULL, client.receive_vpp());
1678   if (FAILED(hr)) {
1679     LOG("Could not activate device for latency: %lx", hr);
1680     return CUBEB_ERROR;
1681   }
1682 
1683   REFERENCE_TIME minimum_period;
1684   REFERENCE_TIME default_period;
1685   hr = client->GetDevicePeriod(&default_period, &minimum_period);
1686   if (FAILED(hr)) {
1687     LOG("Could not get device period: %lx", hr);
1688     return CUBEB_ERROR;
1689   }
1690 
1691   LOG("default device period: %I64d, minimum device period: %I64d", default_period, minimum_period);
1692 
1693   /* If we're on Windows 10, we can use IAudioClient3 to get minimal latency.
1694      Otherwise, according to the docs, the best latency we can achieve is by
1695      synchronizing the stream and the engine.
1696      http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
1697 
1698   #ifdef _WIN32_WINNT_WIN10
1699     *latency_frames = hns_to_frames(params.rate, minimum_period);
1700   #else
1701     *latency_frames = hns_to_frames(params.rate, default_period);
1702   #endif
1703 
1704   LOG("Minimum latency in frames: %u", *latency_frames);
1705 
1706   return CUBEB_OK;
1707 }
1708 
1709 int
1710 wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
1711 {
1712   com_ptr<IMMDevice> device;
1713   HRESULT hr = get_default_endpoint(device, eRender, eConsole);
1714   if (FAILED(hr)) {
1715     return CUBEB_ERROR;
1716   }
1717 
1718   com_ptr<IAudioClient> client;
1719   hr = device->Activate(__uuidof(IAudioClient),
1720                         CLSCTX_INPROC_SERVER,
1721                         NULL, client.receive_vpp());
1722   if (FAILED(hr)) {
1723     return CUBEB_ERROR;
1724   }
1725 
1726   WAVEFORMATEX * tmp = nullptr;
1727   hr = client->GetMixFormat(&tmp);
1728   if (FAILED(hr)) {
1729     return CUBEB_ERROR;
1730   }
1731   com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
1732 
1733   *rate = mix_format->nSamplesPerSec;
1734 
1735   LOG("Preferred sample rate for output: %u", *rate);
1736 
1737   return CUBEB_OK;
1738 }
1739 
1740 void wasapi_stream_destroy(cubeb_stream * stm);
1741 
1742 static void
1743 waveformatex_update_derived_properties(WAVEFORMATEX * format)
1744 {
1745   format->nBlockAlign = format->wBitsPerSample * format->nChannels / 8;
1746   format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign;
1747   if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
1748     WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(format);
1749     format_pcm->Samples.wValidBitsPerSample = format->wBitsPerSample;
1750   }
1751 }
1752 
1753 /* Based on the mix format and the stream format, try to find a way to play
1754    what the user requested. */
1755 static void
1756 handle_channel_layout(cubeb_stream * stm,  EDataFlow direction, com_heap_ptr<WAVEFORMATEX> & mix_format, const cubeb_stream_params * stream_params)
1757 {
1758   com_ptr<IAudioClient> & audio_client = (direction == eRender) ? stm->output_client : stm->input_client;
1759   XASSERT(audio_client);
1760   /* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1],
1761      so the reinterpret_cast below should be safe. In practice, this is not
1762      true, and we just want to bail out and let the rest of the code find a good
1763      conversion path instead of trying to make WASAPI do it by itself.
1764      [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/
1765   if (mix_format->wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
1766     return;
1767   }
1768 
1769   WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
1770 
1771   /* Stash a copy of the original mix format in case we need to restore it later. */
1772   WAVEFORMATEXTENSIBLE hw_mix_format = *format_pcm;
1773 
1774   /* Get the channel mask by the channel layout.
1775      If the layout is not supported, we will get a closest settings below. */
1776   format_pcm->dwChannelMask = stream_params->layout;
1777   mix_format->nChannels = stream_params->channels;
1778   waveformatex_update_derived_properties(mix_format.get());
1779 
1780   /* Check if wasapi will accept our channel layout request. */
1781   WAVEFORMATEX * closest;
1782   HRESULT hr = audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
1783                                                mix_format.get(),
1784                                                &closest);
1785   if (hr == S_FALSE) {
1786     /* Channel layout not supported, but WASAPI gives us a suggestion. Use it,
1787        and handle the eventual upmix/downmix ourselves. Ignore the subformat of
1788        the suggestion, since it seems to always be IEEE_FLOAT. */
1789     LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
1790     XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
1791     WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
1792     format_pcm->dwChannelMask = closest_pcm->dwChannelMask;
1793     mix_format->nChannels = closest->nChannels;
1794     waveformatex_update_derived_properties(mix_format.get());
1795   } else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
1796     /* Not supported, no suggestion. This should not happen, but it does in the
1797        field with some sound cards. We restore the mix format, and let the rest
1798        of the code figure out the right conversion path. */
1799     XASSERT(mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
1800     *reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get()) = hw_mix_format;
1801   } else if (hr == S_OK) {
1802     LOG("Requested format accepted by WASAPI.");
1803   } else {
1804     LOG("IsFormatSupported unhandled error: %lx", hr);
1805   }
1806 }
1807 
1808 static bool
1809 initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
1810                          cubeb_stream * stm,
1811                          const com_heap_ptr<WAVEFORMATEX> & mix_format,
1812                          DWORD flags,
1813                          EDataFlow direction)
1814 {
1815   com_ptr<IAudioClient3> audio_client3;
1816   audio_client->QueryInterface<IAudioClient3>(audio_client3.receive());
1817   if (!audio_client3) {
1818     LOG("Could not get IAudioClient3 interface");
1819     return false;
1820   }
1821 
1822   if (flags & AUDCLNT_STREAMFLAGS_LOOPBACK) {
1823     // IAudioClient3 doesn't work with loopback streams, and will return error
1824     // 88890021: AUDCLNT_E_INVALID_STREAM_FLAG
1825     LOG("Audio stream is loopback, not using IAudioClient3");
1826     return false;
1827   }
1828 
1829   // IAudioClient3 doesn't support AUDCLNT_STREAMFLAGS_NOPERSIST, and will return
1830   // AUDCLNT_E_INVALID_STREAM_FLAG. This is undocumented.
1831   flags = flags ^ AUDCLNT_STREAMFLAGS_NOPERSIST;
1832 
1833   // Some people have reported glitches with capture streams:
1834   // http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html
1835   if (direction == eCapture) {
1836     LOG("Audio stream is capture, not using IAudioClient3");
1837     return false;
1838   }
1839 
1840   // Possibly initialize a shared-mode stream using IAudioClient3. Initializing
1841   // a stream this way lets you request lower latencies, but also locks the global
1842   // WASAPI engine at that latency.
1843   // - If we request a shared-mode stream, streams created with IAudioClient will
1844   //   have their latency adjusted to match. When  the shared-mode stream is
1845   //   closed, they'll go back to normal.
1846   // - If there's already a shared-mode stream running, then we cannot request
1847   //   the engine change to a different latency - we have to match it.
1848   // - It's antisocial to lock the WASAPI engine at its default latency. If we
1849   //   would do this, then stop and use IAudioClient instead.
1850 
1851   HRESULT hr;
1852   uint32_t default_period = 0, fundamental_period = 0, min_period = 0, max_period = 0;
1853   hr = audio_client3->GetSharedModeEnginePeriod(mix_format.get(), &default_period, &fundamental_period, &min_period, &max_period);
1854   if (FAILED(hr)) {
1855     LOG("Could not get shared mode engine period: error: %lx", hr);
1856     return false;
1857   }
1858   uint32_t requested_latency = stm->latency;
1859   if (requested_latency >= default_period) {
1860     LOG("Requested latency %i greater than default latency %i, not using IAudioClient3", requested_latency, default_period);
1861     return false;
1862   }
1863   LOG("Got shared mode engine period: default=%i fundamental=%i min=%i max=%i", default_period, fundamental_period, min_period, max_period);
1864   // Snap requested latency to a valid value
1865   uint32_t old_requested_latency = requested_latency;
1866   if (requested_latency < min_period) {
1867     requested_latency = min_period;
1868   }
1869   requested_latency -= (requested_latency - min_period) % fundamental_period;
1870   if (requested_latency != old_requested_latency) {
1871     LOG("Requested latency %i was adjusted to %i", old_requested_latency, requested_latency);
1872   }
1873 
1874   hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency, mix_format.get(), NULL);
1875   if (SUCCEEDED(hr)) {
1876     return true;
1877   }
1878   else if (hr == AUDCLNT_E_ENGINE_PERIODICITY_LOCKED) {
1879     LOG("Got AUDCLNT_E_ENGINE_PERIODICITY_LOCKED, adjusting latency request");
1880   } else {
1881     LOG("Could not initialize shared stream with IAudioClient3: error: %lx", hr);
1882     return false;
1883   }
1884 
1885   uint32_t current_period = 0;
1886   WAVEFORMATEX* current_format = nullptr;
1887   // We have to pass a valid WAVEFORMATEX** and not nullptr, otherwise
1888   // GetCurrentSharedModeEnginePeriod will return E_POINTER
1889   hr = audio_client3->GetCurrentSharedModeEnginePeriod(&current_format, &current_period);
1890   CoTaskMemFree(current_format);
1891   if (FAILED(hr)) {
1892     LOG("Could not get current shared mode engine period: error: %lx", hr);
1893     return false;
1894   }
1895 
1896   if (current_period >= default_period) {
1897     LOG("Current shared mode engine period %i too high, not using IAudioClient", current_period);
1898     return false;
1899   }
1900 
1901   hr = audio_client3->InitializeSharedAudioStream(flags, current_period, mix_format.get(), NULL);
1902   if (SUCCEEDED(hr)) {
1903     LOG("Current shared mode engine period is %i instead of requested %i", current_period, requested_latency);
1904     return true;
1905   }
1906 
1907   LOG("Could not initialize shared stream with IAudioClient3: error: %lx", hr);
1908   return false;
1909 }
1910 
1911 #define DIRECTION_NAME (direction == eCapture ? "capture" : "render")
1912 
1913 template<typename T>
1914 int setup_wasapi_stream_one_side(cubeb_stream * stm,
1915                                  cubeb_stream_params * stream_params,
1916                                  wchar_t const * devid,
1917                                  EDataFlow direction,
1918                                  REFIID riid,
1919                                  com_ptr<IAudioClient> & audio_client,
1920                                  uint32_t * buffer_frame_count,
1921                                  HANDLE & event,
1922                                  T & render_or_capture_client,
1923                                  cubeb_stream_params * mix_params)
1924 {
1925   com_ptr<IMMDevice> device;
1926   HRESULT hr;
1927   bool is_loopback = stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK;
1928   if (is_loopback && direction != eCapture) {
1929     LOG("Loopback pref can only be used with capture streams!\n");
1930     return CUBEB_ERROR;
1931   }
1932 
1933   stm->stream_reset_lock.assert_current_thread_owns();
1934   bool try_again = false;
1935   // This loops until we find a device that works, or we've exhausted all
1936   // possibilities.
1937   do {
1938     if (devid) {
1939       hr = get_endpoint(device, devid);
1940       if (FAILED(hr)) {
1941         LOG("Could not get %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
1942         return CUBEB_ERROR;
1943       }
1944     } else {
1945       // If caller has requested loopback but not specified a device, look for
1946       // the default render device. Otherwise look for the default device
1947       // appropriate to the direction.
1948       hr = get_default_endpoint(device, is_loopback ? eRender : direction, pref_to_role(stream_params->prefs));
1949       if (FAILED(hr)) {
1950         if (is_loopback) {
1951           LOG("Could not get default render endpoint for loopback, error: %lx\n", hr);
1952         } else {
1953           LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
1954         }
1955         return CUBEB_ERROR;
1956       }
1957     }
1958 
1959     /* Get a client. We will get all other interfaces we need from
1960      * this pointer. */
1961 #if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
1962     hr = device->Activate(__uuidof(IAudioClient3),
1963                           CLSCTX_INPROC_SERVER,
1964                           NULL, audio_client.receive_vpp());
1965     if (hr == E_NOINTERFACE) {
1966 #endif
1967       hr = device->Activate(__uuidof(IAudioClient),
1968                             CLSCTX_INPROC_SERVER,
1969                             NULL, audio_client.receive_vpp());
1970 #if 0
1971     }
1972 #endif
1973 
1974     if (FAILED(hr)) {
1975       LOG("Could not activate the device to get an audio"
1976           " client for %s: error: %lx\n", DIRECTION_NAME, hr);
1977       // A particular device can't be activated because it has been
1978       // unplugged, try fall back to the default audio device.
1979       if (devid && hr == AUDCLNT_E_DEVICE_INVALIDATED) {
1980         LOG("Trying again with the default %s audio device.", DIRECTION_NAME);
1981         devid = nullptr;
1982         device = nullptr;
1983         try_again = true;
1984       } else {
1985         return CUBEB_ERROR;
1986       }
1987     } else {
1988       try_again = false;
1989     }
1990   } while (try_again);
1991 
1992   /* We have to distinguish between the format the mixer uses,
1993    * and the format the stream we want to play uses. */
1994   WAVEFORMATEX * tmp = nullptr;
1995   hr = audio_client->GetMixFormat(&tmp);
1996   if (FAILED(hr)) {
1997     LOG("Could not fetch current mix format from the audio"
1998         " client for %s: error: %lx", DIRECTION_NAME, hr);
1999     return CUBEB_ERROR;
2000   }
2001   com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
2002 
2003   mix_format->wBitsPerSample = stm->bytes_per_sample * 8;
2004   if (mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
2005     WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
2006     format_pcm->SubFormat = stm->waveformatextensible_sub_format;
2007   }
2008   waveformatex_update_derived_properties(mix_format.get());
2009 
2010   /* Set channel layout only when there're more than two channels. Otherwise,
2011    * use the default setting retrieved from the stream format of the audio
2012    * engine's internal processing by GetMixFormat. */
2013   if (mix_format->nChannels > 2) {
2014     handle_channel_layout(stm, direction, mix_format, stream_params);
2015   }
2016 
2017   mix_params->format = stream_params->format;
2018   mix_params->rate = mix_format->nSamplesPerSec;
2019   mix_params->channels = mix_format->nChannels;
2020   mix_params->layout = mask_to_channel_layout(mix_format.get());
2021 
2022   LOG("Setup requested=[f=%d r=%u c=%u l=%u] mix=[f=%d r=%u c=%u l=%u]",
2023       stream_params->format, stream_params->rate, stream_params->channels,
2024       stream_params->layout,
2025       mix_params->format, mix_params->rate, mix_params->channels,
2026       mix_params->layout);
2027 
2028   DWORD flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
2029 
2030   // Check if a loopback device should be requested. Note that event callbacks
2031   // do not work with loopback devices, so only request these if not looping.
2032   if (is_loopback) {
2033     flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
2034   } else {
2035     flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
2036   }
2037 
2038 #if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
2039   if (initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction)) {
2040     LOG("Initialized with IAudioClient3");
2041   } else {
2042 #endif
2043     hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
2044                                   flags,
2045                                   frames_to_hns(stm, stm->latency),
2046                                   0,
2047                                   mix_format.get(),
2048                                   NULL);
2049 #if 0
2050   }
2051 #endif
2052   if (FAILED(hr)) {
2053     LOG("Unable to initialize audio client for %s: %lx.", DIRECTION_NAME, hr);
2054     return CUBEB_ERROR;
2055   }
2056 
2057   hr = audio_client->GetBufferSize(buffer_frame_count);
2058   if (FAILED(hr)) {
2059     LOG("Could not get the buffer size from the client"
2060         " for %s %lx.", DIRECTION_NAME, hr);
2061     return CUBEB_ERROR;
2062   }
2063 
2064   // Events are used if not looping back
2065   if (!is_loopback) {
2066     hr = audio_client->SetEventHandle(event);
2067     if (FAILED(hr)) {
2068       LOG("Could set the event handle for the %s client %lx.",
2069           DIRECTION_NAME, hr);
2070       return CUBEB_ERROR;
2071     }
2072   }
2073 
2074   hr = audio_client->GetService(riid, render_or_capture_client.receive_vpp());
2075   if (FAILED(hr)) {
2076     LOG("Could not get the %s client %lx.", DIRECTION_NAME, hr);
2077     return CUBEB_ERROR;
2078   }
2079 
2080   return CUBEB_OK;
2081 }
2082 
2083 #undef DIRECTION_NAME
2084 
2085 int setup_wasapi_stream(cubeb_stream * stm)
2086 {
2087   int rv;
2088 
2089   stm->stream_reset_lock.assert_current_thread_owns();
2090 
2091   XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first.");
2092 
2093   if (has_input(stm)) {
2094     LOG("(%p) Setup capture: device=%p", stm, stm->input_device.get());
2095     rv = setup_wasapi_stream_one_side(stm,
2096                                       &stm->input_stream_params,
2097                                       stm->input_device.get(),
2098                                       eCapture,
2099                                       __uuidof(IAudioCaptureClient),
2100                                       stm->input_client,
2101                                       &stm->input_buffer_frame_count,
2102                                       stm->input_available_event,
2103                                       stm->capture_client,
2104                                       &stm->input_mix_params);
2105     if (rv != CUBEB_OK) {
2106       LOG("Failure to open the input side.");
2107       return rv;
2108     }
2109 
2110     // We initializing an input stream, buffer ahead two buffers worth of silence.
2111     // This delays the input side slightly, but allow to not glitch when no input
2112     // is available when calling into the resampler to call the callback: the input
2113     // refill event will be set shortly after to compensate for this lack of data.
2114     // In debug, four buffers are used, to avoid tripping up assertions down the line.
2115 #if !defined(DEBUG)
2116     const int silent_buffer_count = 2;
2117 #else
2118     const int silent_buffer_count = 6;
2119 #endif
2120     stm->linear_input_buffer->push_silence(stm->input_buffer_frame_count *
2121                                            stm->input_stream_params.channels *
2122                                            silent_buffer_count);
2123   }
2124 
2125   // If we don't have an output device but are requesting a loopback device,
2126   // we attempt to open that same device in output mode in order to drive the
2127   // loopback via the output events.
2128   stm->has_dummy_output = false;
2129   if (!has_output(stm) && stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) {
2130     stm->output_stream_params.rate = stm->input_stream_params.rate;
2131     stm->output_stream_params.channels = stm->input_stream_params.channels;
2132     stm->output_stream_params.layout = stm->input_stream_params.layout;
2133     if (stm->input_device) {
2134       size_t len = wcslen(stm->input_device.get());
2135       std::unique_ptr<wchar_t[]> tmp(new wchar_t[len + 1]);
2136       if (wcsncpy_s(tmp.get(), len + 1, stm->input_device.get(), len) != 0) {
2137         LOG("Failed to copy device identifier while copying input stream"
2138             " configuration to output stream configuration to drive loopback.");
2139         return CUBEB_ERROR;
2140       }
2141       stm->output_device = move(tmp);
2142     }
2143     stm->has_dummy_output = true;
2144   }
2145 
2146   if (has_output(stm)) {
2147     LOG("(%p) Setup render: device=%p", stm, stm->output_device.get());
2148     rv = setup_wasapi_stream_one_side(stm,
2149                                       &stm->output_stream_params,
2150                                       stm->output_device.get(),
2151                                       eRender,
2152                                       __uuidof(IAudioRenderClient),
2153                                       stm->output_client,
2154                                       &stm->output_buffer_frame_count,
2155                                       stm->refill_event,
2156                                       stm->render_client,
2157                                       &stm->output_mix_params);
2158     if (rv != CUBEB_OK) {
2159       LOG("Failure to open the output side.");
2160       return rv;
2161     }
2162 
2163     HRESULT hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume),
2164                                                 stm->audio_stream_volume.receive_vpp());
2165     if (FAILED(hr)) {
2166       LOG("Could not get the IAudioStreamVolume: %lx", hr);
2167       return CUBEB_ERROR;
2168     }
2169 
2170     XASSERT(stm->frames_written == 0);
2171     hr = stm->output_client->GetService(__uuidof(IAudioClock),
2172                                         stm->audio_clock.receive_vpp());
2173     if (FAILED(hr)) {
2174       LOG("Could not get the IAudioClock: %lx", hr);
2175       return CUBEB_ERROR;
2176     }
2177 
2178     /* Restore the stream volume over a device change. */
2179     if (stream_set_volume(stm, stm->volume) != CUBEB_OK) {
2180       LOG("Could not set the volume.");
2181       return CUBEB_ERROR;
2182     }
2183   }
2184 
2185   /* If we have both input and output, we resample to
2186    * the highest sample rate available. */
2187   int32_t target_sample_rate;
2188   if (has_input(stm) && has_output(stm)) {
2189     XASSERT(stm->input_stream_params.rate == stm->output_stream_params.rate);
2190     target_sample_rate = stm->input_stream_params.rate;
2191   } else if (has_input(stm)) {
2192     target_sample_rate = stm->input_stream_params.rate;
2193   } else {
2194     XASSERT(has_output(stm));
2195     target_sample_rate = stm->output_stream_params.rate;
2196   }
2197 
2198   LOG("Target sample rate: %d", target_sample_rate);
2199 
2200   /* If we are playing/capturing a mono stream, we only resample one channel,
2201    and copy it over, so we are always resampling the number
2202    of channels of the stream, not the number of channels
2203    that WASAPI wants. */
2204   cubeb_stream_params input_params = stm->input_mix_params;
2205   input_params.channels = stm->input_stream_params.channels;
2206   cubeb_stream_params output_params = stm->output_mix_params;
2207   output_params.channels = stm->output_stream_params.channels;
2208 
2209   stm->resampler.reset(
2210     cubeb_resampler_create(stm,
2211                            has_input(stm) ? &input_params : nullptr,
2212                            has_output(stm) ? &output_params : nullptr,
2213                            target_sample_rate,
2214                            stm->data_callback,
2215                            stm->user_ptr,
2216                            CUBEB_RESAMPLER_QUALITY_DESKTOP));
2217   if (!stm->resampler) {
2218     LOG("Could not get a resampler");
2219     return CUBEB_ERROR;
2220   }
2221 
2222   XASSERT(has_input(stm) || has_output(stm));
2223 
2224   if (has_input(stm) && has_output(stm)) {
2225     stm->refill_callback = refill_callback_duplex;
2226   } else if (has_input(stm)) {
2227     stm->refill_callback = refill_callback_input;
2228   } else if (has_output(stm)) {
2229     stm->refill_callback = refill_callback_output;
2230   }
2231 
2232   // Create input mixer.
2233   if (has_input(stm) &&
2234       ((stm->input_mix_params.layout != CUBEB_LAYOUT_UNDEFINED &&
2235         stm->input_mix_params.layout != stm->input_stream_params.layout) ||
2236        (stm->input_mix_params.channels != stm->input_stream_params.channels))) {
2237     if (stm->input_mix_params.layout == CUBEB_LAYOUT_UNDEFINED) {
2238       LOG("Input stream using undefined layout! Any mixing may be "
2239           "unpredictable!\n");
2240     }
2241     stm->input_mixer.reset(cubeb_mixer_create(stm->input_stream_params.format,
2242                                               stm->input_mix_params.channels,
2243                                               stm->input_mix_params.layout,
2244                                               stm->input_stream_params.channels,
2245                                               stm->input_stream_params.layout));
2246     assert(stm->input_mixer);
2247   }
2248 
2249   // Create output mixer.
2250   if (has_output(stm) && stm->output_mix_params.layout != stm->output_stream_params.layout) {
2251     if (stm->output_mix_params.layout == CUBEB_LAYOUT_UNDEFINED) {
2252       LOG("Output stream using undefined layout! Any mixing may be unpredictable!\n");
2253     }
2254     stm->output_mixer.reset(cubeb_mixer_create(stm->output_stream_params.format,
2255                                                stm->output_stream_params.channels,
2256                                                stm->output_stream_params.layout,
2257                                                stm->output_mix_params.channels,
2258                                                stm->output_mix_params.layout));
2259     assert(stm->output_mixer);
2260     // Input is up/down mixed when depacketized in get_input_buffer.
2261     stm->mix_buffer.resize(
2262       frames_to_bytes_before_mix(stm, stm->output_buffer_frame_count));
2263   }
2264 
2265   return CUBEB_OK;
2266 }
2267 
2268 ERole
2269 pref_to_role(cubeb_stream_prefs prefs)
2270 {
2271   if (prefs & CUBEB_STREAM_PREF_VOICE) {
2272     return eCommunications;
2273   }
2274 
2275   return eConsole;
2276 }
2277 
2278 int
2279 wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
2280                    char const * stream_name,
2281                    cubeb_devid input_device,
2282                    cubeb_stream_params * input_stream_params,
2283                    cubeb_devid output_device,
2284                    cubeb_stream_params * output_stream_params,
2285                    unsigned int latency_frames, cubeb_data_callback data_callback,
2286                    cubeb_state_callback state_callback, void * user_ptr)
2287 {
2288   int rv;
2289 
2290   XASSERT(context && stream && (input_stream_params || output_stream_params));
2291 
2292   if (output_stream_params && input_stream_params &&
2293       output_stream_params->format != input_stream_params->format) {
2294     return CUBEB_ERROR_INVALID_FORMAT;
2295   }
2296 
2297   std::unique_ptr<cubeb_stream, decltype(&wasapi_stream_destroy)> stm(new cubeb_stream(), wasapi_stream_destroy);
2298 
2299   stm->context = context;
2300   stm->data_callback = data_callback;
2301   stm->state_callback = state_callback;
2302   stm->user_ptr = user_ptr;
2303 
2304   if (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_VOICE ||
2305       stm->input_stream_params.prefs & CUBEB_STREAM_PREF_VOICE) {
2306     stm->role = eCommunications;
2307   } else {
2308     stm->role = eConsole;
2309   }
2310 
2311   if (input_stream_params) {
2312     stm->input_stream_params = *input_stream_params;
2313     stm->input_device = utf8_to_wstr(reinterpret_cast<char const *>(input_device));
2314   }
2315   if (output_stream_params) {
2316     stm->output_stream_params = *output_stream_params;
2317     stm->output_device = utf8_to_wstr(reinterpret_cast<char const *>(output_device));
2318   }
2319 
2320   switch (output_stream_params ? output_stream_params->format : input_stream_params->format) {
2321     case CUBEB_SAMPLE_S16NE:
2322       stm->bytes_per_sample = sizeof(short);
2323       stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_PCM;
2324       stm->linear_input_buffer.reset(new auto_array_wrapper_impl<short>);
2325       break;
2326     case CUBEB_SAMPLE_FLOAT32NE:
2327       stm->bytes_per_sample = sizeof(float);
2328       stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
2329       stm->linear_input_buffer.reset(new auto_array_wrapper_impl<float>);
2330       break;
2331     default:
2332       return CUBEB_ERROR_INVALID_FORMAT;
2333   }
2334 
2335   stm->latency = latency_frames;
2336 
2337   stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
2338   if (!stm->reconfigure_event) {
2339     LOG("Can't create the reconfigure event, error: %lx", GetLastError());
2340     return CUBEB_ERROR;
2341   }
2342 
2343   /* Unconditionally create the two events so that the wait logic is simpler. */
2344   stm->refill_event = CreateEvent(NULL, 0, 0, NULL);
2345   if (!stm->refill_event) {
2346     LOG("Can't create the refill event, error: %lx", GetLastError());
2347     return CUBEB_ERROR;
2348   }
2349 
2350   stm->input_available_event = CreateEvent(NULL, 0, 0, NULL);
2351   if (!stm->input_available_event) {
2352     LOG("Can't create the input available event , error: %lx", GetLastError());
2353     return CUBEB_ERROR;
2354   }
2355 
2356   {
2357     /* Locking here is not strictly necessary, because we don't have a
2358        notification client that can reset the stream yet, but it lets us
2359        assert that the lock is held in the function. */
2360     auto_lock lock(stm->stream_reset_lock);
2361     rv = setup_wasapi_stream(stm.get());
2362   }
2363   if (rv != CUBEB_OK) {
2364     return rv;
2365   }
2366 
2367   if (!((input_stream_params ?
2368          (input_stream_params->prefs & CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) : 0) ||
2369         (output_stream_params ?
2370          (output_stream_params->prefs & CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) : 0))) {
2371     HRESULT hr = register_notification_client(stm.get());
2372     if (FAILED(hr)) {
2373       /* this is not fatal, we can still play audio, but we won't be able
2374          to keep using the default audio endpoint if it changes. */
2375       LOG("failed to register notification client, %lx", hr);
2376     }
2377   }
2378 
2379   *stream = stm.release();
2380 
2381   LOG("Stream init succesfull (%p)", *stream);
2382   return CUBEB_OK;
2383 }
2384 
2385 void close_wasapi_stream(cubeb_stream * stm)
2386 {
2387   XASSERT(stm);
2388 
2389   stm->stream_reset_lock.assert_current_thread_owns();
2390 
2391   stm->output_client = nullptr;
2392   stm->render_client = nullptr;
2393 
2394   stm->input_client = nullptr;
2395   stm->capture_client = nullptr;
2396 
2397   stm->audio_stream_volume = nullptr;
2398 
2399   stm->audio_clock = nullptr;
2400   stm->total_frames_written += static_cast<UINT64>(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params)));
2401   stm->frames_written = 0;
2402 
2403   stm->resampler.reset();
2404   stm->output_mixer.reset();
2405   stm->input_mixer.reset();
2406   stm->mix_buffer.clear();
2407 }
2408 
2409 void wasapi_stream_destroy(cubeb_stream * stm)
2410 {
2411   XASSERT(stm);
2412   LOG("Stream destroy (%p)", stm);
2413 
2414   // Only free stm->emergency_bailout if we could join the thread.
2415   // If we could not join the thread, stm->emergency_bailout is true
2416   // and is still alive until the thread wakes up and exits cleanly.
2417   if (stop_and_join_render_thread(stm)) {
2418     delete stm->emergency_bailout.load();
2419     stm->emergency_bailout = nullptr;
2420   }
2421 
2422   if (stm->notification_client) {
2423     unregister_notification_client(stm);
2424   }
2425 
2426   CloseHandle(stm->reconfigure_event);
2427   CloseHandle(stm->refill_event);
2428   CloseHandle(stm->input_available_event);
2429 
2430   // The variables intialized in wasapi_stream_init,
2431   // must be destroyed in wasapi_stream_destroy.
2432   stm->linear_input_buffer.reset();
2433 
2434   {
2435     auto_lock lock(stm->stream_reset_lock);
2436     close_wasapi_stream(stm);
2437   }
2438 
2439   delete stm;
2440 }
2441 
2442 enum StreamDirection {
2443   OUTPUT,
2444   INPUT
2445 };
2446 
2447 int stream_start_one_side(cubeb_stream * stm, StreamDirection dir)
2448 {
2449   XASSERT((dir == OUTPUT && stm->output_client) ||
2450           (dir == INPUT && stm->input_client));
2451 
2452   HRESULT hr = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
2453   if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
2454     LOG("audioclient invalidated for %s device, reconfiguring",
2455         dir == OUTPUT ? "output" : "input");
2456 
2457     BOOL ok = ResetEvent(stm->reconfigure_event);
2458     if (!ok) {
2459       LOG("resetting reconfig event failed for %s stream: %lx",
2460           dir == OUTPUT ? "output" : "input", GetLastError());
2461     }
2462 
2463     close_wasapi_stream(stm);
2464     int r = setup_wasapi_stream(stm);
2465     if (r != CUBEB_OK) {
2466       LOG("reconfigure failed");
2467       return r;
2468     }
2469 
2470     HRESULT hr2 = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
2471     if (FAILED(hr2)) {
2472       LOG("could not start the %s stream after reconfig: %lx",
2473           dir == OUTPUT ? "output" : "input", hr);
2474       return CUBEB_ERROR;
2475     }
2476   } else if (FAILED(hr)) {
2477     LOG("could not start the %s stream: %lx.",
2478         dir == OUTPUT ? "output" : "input", hr);
2479     return CUBEB_ERROR;
2480   }
2481 
2482   return CUBEB_OK;
2483 }
2484 
2485 int wasapi_stream_start(cubeb_stream * stm)
2486 {
2487   auto_lock lock(stm->stream_reset_lock);
2488 
2489   XASSERT(stm && !stm->thread && !stm->shutdown_event);
2490   XASSERT(stm->output_client || stm->input_client);
2491 
2492   stm->emergency_bailout = new std::atomic<bool>(false);
2493 
2494   if (stm->output_client) {
2495     int rv = stream_start_one_side(stm, OUTPUT);
2496     if (rv != CUBEB_OK) {
2497       return rv;
2498     }
2499   }
2500 
2501   if (stm->input_client) {
2502     int rv = stream_start_one_side(stm, INPUT);
2503     if (rv != CUBEB_OK) {
2504       return rv;
2505     }
2506   }
2507 
2508   stm->shutdown_event = CreateEvent(NULL, 0, 0, NULL);
2509   if (!stm->shutdown_event) {
2510     LOG("Can't create the shutdown event, error: %lx", GetLastError());
2511     return CUBEB_ERROR;
2512   }
2513 
2514   stm->thread_ready_event = CreateEvent(NULL, 0, 0, NULL);
2515   if (!stm->thread_ready_event) {
2516     LOG("Can't create the thread_ready event, error: %lx", GetLastError());
2517     return CUBEB_ERROR;
2518   }
2519 
2520   cubeb_async_log_reset_threads();
2521   stm->thread = (HANDLE) _beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
2522   if (stm->thread == NULL) {
2523     LOG("could not create WASAPI render thread.");
2524     return CUBEB_ERROR;
2525   }
2526 
2527   // Wait for wasapi_stream_render_loop to signal that emergency_bailout has
2528   // been read, avoiding a bailout situation where we could free `stm`
2529   // before wasapi_stream_render_loop had a chance to run.
2530   HRESULT hr = WaitForSingleObject(stm->thread_ready_event, INFINITE);
2531   XASSERT(hr == WAIT_OBJECT_0);
2532   CloseHandle(stm->thread_ready_event);
2533   stm->thread_ready_event = 0;
2534 
2535   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
2536 
2537   return CUBEB_OK;
2538 }
2539 
2540 int wasapi_stream_stop(cubeb_stream * stm)
2541 {
2542   XASSERT(stm);
2543   HRESULT hr;
2544 
2545   {
2546     auto_lock lock(stm->stream_reset_lock);
2547 
2548     if (stm->output_client) {
2549       hr = stm->output_client->Stop();
2550       if (FAILED(hr)) {
2551         LOG("could not stop AudioClient (output)");
2552         return CUBEB_ERROR;
2553       }
2554     }
2555 
2556     if (stm->input_client) {
2557       hr = stm->input_client->Stop();
2558       if (FAILED(hr)) {
2559         LOG("could not stop AudioClient (input)");
2560         return CUBEB_ERROR;
2561       }
2562     }
2563 
2564     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
2565   }
2566 
2567   if (stop_and_join_render_thread(stm)) {
2568     delete stm->emergency_bailout.load();
2569     stm->emergency_bailout = nullptr;
2570   } else {
2571     // If we could not join the thread, put the stream in error.
2572     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
2573     return CUBEB_ERROR;
2574   }
2575 
2576   return CUBEB_OK;
2577 }
2578 
2579 int wasapi_stream_reset_default_device(cubeb_stream * stm)
2580 {
2581   XASSERT(stm && stm->reconfigure_event);
2582   BOOL ok = SetEvent(stm->reconfigure_event);
2583   if (!ok) {
2584     LOG("SetEvent on reconfigure_event failed: %lx", GetLastError());
2585     return CUBEB_ERROR;
2586   }
2587   return CUBEB_OK;
2588 }
2589 
2590 int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position)
2591 {
2592   XASSERT(stm && position);
2593   auto_lock lock(stm->stream_reset_lock);
2594 
2595   if (!has_output(stm)) {
2596     return CUBEB_ERROR;
2597   }
2598 
2599   /* Calculate how far behind the current stream head the playback cursor is. */
2600   uint64_t stream_delay = static_cast<uint64_t>(current_stream_delay(stm) * stm->output_stream_params.rate);
2601 
2602   /* Calculate the logical stream head in frames at the stream sample rate. */
2603   uint64_t max_pos = stm->total_frames_written +
2604                      static_cast<uint64_t>(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params)));
2605 
2606   *position = max_pos;
2607   if (stream_delay <= *position) {
2608     *position -= stream_delay;
2609   }
2610 
2611   if (*position < stm->prev_position) {
2612     *position = stm->prev_position;
2613   }
2614   stm->prev_position = *position;
2615 
2616   return CUBEB_OK;
2617 }
2618 
2619 int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
2620 {
2621   XASSERT(stm && latency);
2622 
2623   if (!has_output(stm)) {
2624     return CUBEB_ERROR;
2625   }
2626 
2627   auto_lock lock(stm->stream_reset_lock);
2628 
2629   /* The GetStreamLatency method only works if the
2630      AudioClient has been initialized. */
2631   if (!stm->output_client) {
2632     return CUBEB_ERROR;
2633   }
2634 
2635   REFERENCE_TIME latency_hns;
2636   HRESULT hr = stm->output_client->GetStreamLatency(&latency_hns);
2637   if (FAILED(hr)) {
2638     return CUBEB_ERROR;
2639   }
2640   *latency = hns_to_frames(stm, latency_hns);
2641 
2642   return CUBEB_OK;
2643 }
2644 
2645 
2646 int wasapi_stream_get_input_latency(cubeb_stream * stm, uint32_t * latency)
2647 {
2648   XASSERT(stm && latency);
2649 
2650   if (!has_input(stm)) {
2651     return CUBEB_ERROR;
2652   }
2653 
2654   auto_lock lock(stm->stream_reset_lock);
2655 
2656   if (stm->input_latency_hns == LATENCY_NOT_AVAILABLE_YET) {
2657     return CUBEB_ERROR;
2658   }
2659 
2660   *latency = hns_to_frames(stm, stm->input_latency_hns);
2661 
2662   return CUBEB_OK;
2663 }
2664 
2665 int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
2666 {
2667   auto_lock lock(stm->stream_reset_lock);
2668 
2669   if (!has_output(stm)) {
2670     return CUBEB_ERROR;
2671   }
2672 
2673   if (stream_set_volume(stm, volume) != CUBEB_OK) {
2674     return CUBEB_ERROR;
2675   }
2676 
2677   stm->volume = volume;
2678 
2679   return CUBEB_OK;
2680 }
2681 
2682 static char const *
2683 wstr_to_utf8(LPCWSTR str)
2684 {
2685   int size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, NULL, NULL);
2686   if (size <= 0) {
2687     return nullptr;
2688   }
2689 
2690   char * ret = static_cast<char *>(malloc(size));
2691   ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
2692   return ret;
2693 }
2694 
2695 static std::unique_ptr<wchar_t const []>
2696 utf8_to_wstr(char const * str)
2697 {
2698   int size = ::MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0);
2699   if (size <= 0) {
2700     return nullptr;
2701   }
2702 
2703   std::unique_ptr<wchar_t []> ret(new wchar_t[size]);
2704   ::MultiByteToWideChar(CP_UTF8, 0, str, -1, ret.get(), size);
2705   return ret;
2706 }
2707 
2708 static com_ptr<IMMDevice>
2709 wasapi_get_device_node(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
2710 {
2711   com_ptr<IMMDevice> ret;
2712   com_ptr<IDeviceTopology> devtopo;
2713   com_ptr<IConnector> connector;
2714 
2715   if (SUCCEEDED(dev->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, devtopo.receive_vpp())) &&
2716       SUCCEEDED(devtopo->GetConnector(0, connector.receive()))) {
2717     wchar_t * tmp = nullptr;
2718     if (SUCCEEDED(connector->GetDeviceIdConnectedTo(&tmp))) {
2719       com_heap_ptr<wchar_t> filterid(tmp);
2720       if (FAILED(enumerator->GetDevice(filterid.get(), ret.receive())))
2721         ret = NULL;
2722     }
2723   }
2724 
2725   return ret;
2726 }
2727 
2728 static BOOL
2729 wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id,
2730                          IMMDeviceEnumerator * enumerator)
2731 {
2732   BOOL ret = FALSE;
2733   com_ptr<IMMDevice> dev;
2734   HRESULT hr;
2735 
2736   hr = enumerator->GetDefaultAudioEndpoint(flow, role, dev.receive());
2737   if (SUCCEEDED(hr)) {
2738     wchar_t * tmp = nullptr;
2739     if (SUCCEEDED(dev->GetId(&tmp))) {
2740       com_heap_ptr<wchar_t> defdevid(tmp);
2741       ret = (wcscmp(defdevid.get(), device_id) == 0);
2742     }
2743   }
2744 
2745   return ret;
2746 }
2747 
2748 int
2749 wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev)
2750 {
2751   com_ptr<IMMEndpoint> endpoint;
2752   com_ptr<IMMDevice> devnode;
2753   com_ptr<IAudioClient> client;
2754   EDataFlow flow;
2755   DWORD state = DEVICE_STATE_NOTPRESENT;
2756   com_ptr<IPropertyStore> propstore;
2757   REFERENCE_TIME def_period, min_period;
2758   HRESULT hr;
2759 
2760   struct prop_variant : public PROPVARIANT {
2761     prop_variant() { PropVariantInit(this); }
2762     ~prop_variant() { PropVariantClear(this); }
2763     prop_variant(prop_variant const &) = delete;
2764     prop_variant & operator=(prop_variant const &) = delete;
2765   };
2766 
2767   hr = dev->QueryInterface(IID_PPV_ARGS(endpoint.receive()));
2768   if (FAILED(hr)) return CUBEB_ERROR;
2769 
2770   hr = endpoint->GetDataFlow(&flow);
2771   if (FAILED(hr)) return CUBEB_ERROR;
2772 
2773   wchar_t * tmp = nullptr;
2774   hr = dev->GetId(&tmp);
2775   if (FAILED(hr)) return CUBEB_ERROR;
2776   com_heap_ptr<wchar_t> device_id(tmp);
2777 
2778   char const * device_id_intern = intern_device_id(ctx, device_id.get());
2779   if (!device_id_intern) {
2780     return CUBEB_ERROR;
2781   }
2782 
2783   hr = dev->OpenPropertyStore(STGM_READ, propstore.receive());
2784   if (FAILED(hr)) return CUBEB_ERROR;
2785 
2786   hr = dev->GetState(&state);
2787   if (FAILED(hr)) return CUBEB_ERROR;
2788 
2789   ret.device_id = device_id_intern;
2790   ret.devid = reinterpret_cast<cubeb_devid>(ret.device_id);
2791   prop_variant namevar;
2792   hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar);
2793   if (SUCCEEDED(hr))
2794     ret.friendly_name = wstr_to_utf8(namevar.pwszVal);
2795 
2796   devnode = wasapi_get_device_node(enumerator, dev);
2797   if (devnode) {
2798     com_ptr<IPropertyStore> ps;
2799     hr = devnode->OpenPropertyStore(STGM_READ, ps.receive());
2800     if (FAILED(hr)) return CUBEB_ERROR;
2801 
2802     prop_variant instancevar;
2803     hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar);
2804     if (SUCCEEDED(hr)) {
2805       ret.group_id = wstr_to_utf8(instancevar.pwszVal);
2806     }
2807   }
2808 
2809   ret.preferred = CUBEB_DEVICE_PREF_NONE;
2810   if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator))
2811     ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA);
2812   if (wasapi_is_default_device(flow, eCommunications, device_id.get(), enumerator))
2813     ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE);
2814   if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator))
2815     ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_NOTIFICATION);
2816 
2817   if (flow == eRender) ret.type = CUBEB_DEVICE_TYPE_OUTPUT;
2818   else if (flow == eCapture) ret.type = CUBEB_DEVICE_TYPE_INPUT;
2819   switch (state) {
2820     case DEVICE_STATE_ACTIVE:
2821       ret.state = CUBEB_DEVICE_STATE_ENABLED;
2822       break;
2823     case DEVICE_STATE_UNPLUGGED:
2824       ret.state = CUBEB_DEVICE_STATE_UNPLUGGED;
2825       break;
2826     default:
2827       ret.state = CUBEB_DEVICE_STATE_DISABLED;
2828       break;
2829   };
2830 
2831   ret.format = static_cast<cubeb_device_fmt>(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE);
2832   ret.default_format = CUBEB_DEVICE_FMT_F32NE;
2833   prop_variant fmtvar;
2834   hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar);
2835   if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) {
2836     if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
2837       const PCMWAVEFORMAT * pcm = reinterpret_cast<const PCMWAVEFORMAT *>(fmtvar.blob.pBlobData);
2838 
2839       ret.max_rate = ret.min_rate = ret.default_rate = pcm->wf.nSamplesPerSec;
2840       ret.max_channels = pcm->wf.nChannels;
2841     } else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
2842       WAVEFORMATEX* wfx = reinterpret_cast<WAVEFORMATEX*>(fmtvar.blob.pBlobData);
2843 
2844       if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize ||
2845           wfx->wFormatTag == WAVE_FORMAT_PCM) {
2846         ret.max_rate = ret.min_rate = ret.default_rate = wfx->nSamplesPerSec;
2847         ret.max_channels = wfx->nChannels;
2848       }
2849     }
2850   }
2851 
2852   if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, client.receive_vpp())) &&
2853       SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
2854     ret.latency_lo = hns_to_frames(ret.default_rate, min_period);
2855     ret.latency_hi = hns_to_frames(ret.default_rate, def_period);
2856   } else {
2857     ret.latency_lo = 0;
2858     ret.latency_hi = 0;
2859   }
2860 
2861   return CUBEB_OK;
2862 }
2863 
2864 static int
2865 wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
2866                          cubeb_device_collection * out)
2867 {
2868   com_ptr<IMMDeviceEnumerator> enumerator;
2869   com_ptr<IMMDeviceCollection> collection;
2870   HRESULT hr;
2871   UINT cc, i;
2872   EDataFlow flow;
2873 
2874   hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
2875                         CLSCTX_INPROC_SERVER, IID_PPV_ARGS(enumerator.receive()));
2876   if (FAILED(hr)) {
2877     LOG("Could not get device enumerator: %lx", hr);
2878     return CUBEB_ERROR;
2879   }
2880 
2881   if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender;
2882   else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture;
2883   else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) flow = eAll;
2884   else return CUBEB_ERROR;
2885 
2886   hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, collection.receive());
2887   if (FAILED(hr)) {
2888     LOG("Could not enumerate audio endpoints: %lx", hr);
2889     return CUBEB_ERROR;
2890   }
2891 
2892   hr = collection->GetCount(&cc);
2893   if (FAILED(hr)) {
2894     LOG("IMMDeviceCollection::GetCount() failed: %lx", hr);
2895     return CUBEB_ERROR;
2896   }
2897   cubeb_device_info * devices = new cubeb_device_info[cc];
2898   if (!devices)
2899     return CUBEB_ERROR;
2900 
2901   PodZero(devices, cc);
2902   out->count = 0;
2903   for (i = 0; i < cc; i++) {
2904     com_ptr<IMMDevice> dev;
2905     hr = collection->Item(i, dev.receive());
2906     if (FAILED(hr)) {
2907       LOG("IMMDeviceCollection::Item(%u) failed: %lx", i-1, hr);
2908       continue;
2909     }
2910     if (wasapi_create_device(context, devices[out->count],
2911                              enumerator.get(), dev.get()) == CUBEB_OK) {
2912       out->count += 1;
2913     }
2914   }
2915 
2916   out->device = devices;
2917   return CUBEB_OK;
2918 }
2919 
2920 static int
2921 wasapi_device_collection_destroy(cubeb * /*ctx*/, cubeb_device_collection * collection)
2922 {
2923   XASSERT(collection);
2924 
2925   for (size_t n = 0; n < collection->count; n++) {
2926     cubeb_device_info& dev = collection->device[n];
2927     delete [] dev.friendly_name;
2928     delete [] dev.group_id;
2929   }
2930 
2931   delete [] collection->device;
2932   return CUBEB_OK;
2933 }
2934 
2935 static int
2936 wasapi_register_device_collection_changed(cubeb * context,
2937                                           cubeb_device_type devtype,
2938                                           cubeb_device_collection_changed_callback collection_changed_callback,
2939                                           void * user_ptr)
2940 {
2941   if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) {
2942     return CUBEB_ERROR_INVALID_PARAMETER;
2943   }
2944 
2945   if (collection_changed_callback) {
2946     // Make sure it has been unregistered first.
2947     XASSERT(((devtype & CUBEB_DEVICE_TYPE_INPUT) &&
2948              !context->input_collection_changed_callback) ||
2949             ((devtype & CUBEB_DEVICE_TYPE_OUTPUT) &&
2950              !context->output_collection_changed_callback));
2951 
2952     // Stop the notification client. Notifications arrive on
2953     // a separate thread. We stop them here to avoid
2954     // synchronization issues during the update.
2955     if (context->device_collection_enumerator.get()) {
2956       HRESULT hr = unregister_collection_notification_client(context);
2957       if (FAILED(hr)) {
2958         return CUBEB_ERROR;
2959       }
2960     }
2961 
2962     if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
2963       context->input_collection_changed_callback = collection_changed_callback;
2964       context->input_collection_changed_user_ptr = user_ptr;
2965     }
2966     if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
2967       context->output_collection_changed_callback = collection_changed_callback;
2968       context->output_collection_changed_user_ptr = user_ptr;
2969     }
2970 
2971     HRESULT hr = register_collection_notification_client(context);
2972     if (FAILED(hr)) {
2973       return CUBEB_ERROR;
2974     }
2975   } else {
2976     if (!context->device_collection_enumerator.get()) {
2977       // Already unregistered, ignore it.
2978       return CUBEB_OK;
2979     }
2980 
2981     HRESULT hr = unregister_collection_notification_client(context);
2982     if (FAILED(hr)) {
2983       return CUBEB_ERROR;
2984     }
2985     if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
2986       context->input_collection_changed_callback = nullptr;
2987       context->input_collection_changed_user_ptr = nullptr;
2988     }
2989     if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
2990       context->output_collection_changed_callback = nullptr;
2991       context->output_collection_changed_user_ptr = nullptr;
2992     }
2993 
2994     // If after the updates we still have registered
2995     // callbacks restart the notification client.
2996     if (context->input_collection_changed_callback ||
2997         context->output_collection_changed_callback) {
2998       hr = register_collection_notification_client(context);
2999       if (FAILED(hr)) {
3000         return CUBEB_ERROR;
3001       }
3002     }
3003   }
3004 
3005   return CUBEB_OK;
3006 }
3007 
3008 cubeb_ops const wasapi_ops = {
3009   /*.init =*/ wasapi_init,
3010   /*.get_backend_id =*/ wasapi_get_backend_id,
3011   /*.get_max_channel_count =*/ wasapi_get_max_channel_count,
3012   /*.get_min_latency =*/ wasapi_get_min_latency,
3013   /*.get_preferred_sample_rate =*/ wasapi_get_preferred_sample_rate,
3014   /*.enumerate_devices =*/ wasapi_enumerate_devices,
3015   /*.device_collection_destroy =*/ wasapi_device_collection_destroy,
3016   /*.destroy =*/ wasapi_destroy,
3017   /*.stream_init =*/ wasapi_stream_init,
3018   /*.stream_destroy =*/ wasapi_stream_destroy,
3019   /*.stream_start =*/ wasapi_stream_start,
3020   /*.stream_stop =*/ wasapi_stream_stop,
3021   /*.stream_reset_default_device =*/ wasapi_stream_reset_default_device,
3022   /*.stream_get_position =*/ wasapi_stream_get_position,
3023   /*.stream_get_latency =*/ wasapi_stream_get_latency,
3024   /*.stream_get_input_latency =*/ wasapi_stream_get_input_latency,
3025   /*.stream_set_volume =*/ wasapi_stream_set_volume,
3026   /*.stream_get_current_device =*/ NULL,
3027   /*.stream_device_destroy =*/ NULL,
3028   /*.stream_register_device_changed_callback =*/ NULL,
3029   /*.register_device_collection_changed =*/ wasapi_register_device_collection_changed,
3030 };
3031 } // namespace anonymous
3032