duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cubeb_wasapi.cpp (120807B)


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