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 NOMINMAX
8 
9 #include <initguid.h>
10 #include <windows.h>
11 #include <mmdeviceapi.h>
12 #include <windef.h>
13 #include <audioclient.h>
14 #include <devicetopology.h>
15 #include <process.h>
16 #include <avrt.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdint.h>
21 #include <cmath>
22 #include <algorithm>
23 #include <memory>
24 #include <limits>
25 #include <atomic>
26 
27 #include "cubeb/cubeb.h"
28 #include "cubeb-internal.h"
29 #include "cubeb_resampler.h"
30 #include "cubeb_utils.h"
31 
32 /* devicetopology.h missing in MinGW. */
33 #ifndef __devicetopology_h__
34 #include "cubeb_devicetopology.h"
35 #endif
36 
37 /* Taken from winbase.h, Not in MinGW. */
38 #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
39 #define STACK_SIZE_PARAM_IS_A_RESERVATION   0x00010000    // Threads only
40 #endif
41 
42 #ifndef PKEY_Device_FriendlyName
43 DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,    0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);    // DEVPROP_TYPE_STRING
44 #endif
45 #ifndef PKEY_Device_InstanceId
46 DEFINE_PROPERTYKEY(PKEY_Device_InstanceId,      0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 0x00000100); //    VT_LPWSTR
47 #endif
48 
49 namespace {
50 template<typename T, size_t N>
51 constexpr size_t
ARRAY_LENGTH(T (&)[N])52 ARRAY_LENGTH(T(&)[N])
53 {
54   return N;
55 }
56 
57 void
SafeRelease(HANDLE handle)58 SafeRelease(HANDLE handle)
59 {
60   if (handle) {
61     CloseHandle(handle);
62   }
63 }
64 
65 template <typename T>
SafeRelease(T * ptr)66 void SafeRelease(T * ptr)
67 {
68   if (ptr) {
69     ptr->Release();
70   }
71 }
72 
73 struct auto_com {
auto_com__anon070327290111::auto_com74   auto_com() {
75     result = CoInitializeEx(NULL, COINIT_MULTITHREADED);
76   }
~auto_com__anon070327290111::auto_com77   ~auto_com() {
78     if (result == RPC_E_CHANGED_MODE) {
79       // This is not an error, COM was not initialized by this function, so it is
80       // not necessary to uninit it.
81       LOG("COM was already initialized in STA.");
82     } else if (result == S_FALSE) {
83       // This is not an error. We are allowed to call CoInitializeEx more than
84       // once, as long as it is matches by an CoUninitialize call.
85       // We do that in the dtor which is guaranteed to be called.
86       LOG("COM was already initialized in MTA");
87     }
88     if (SUCCEEDED(result)) {
89       CoUninitialize();
90     }
91   }
ok__anon070327290111::auto_com92   bool ok() {
93     return result == RPC_E_CHANGED_MODE || SUCCEEDED(result);
94   }
95 private:
96   HRESULT result;
97 };
98 
99 typedef HANDLE (WINAPI *set_mm_thread_characteristics_function)(
100                                       const char * TaskName, LPDWORD TaskIndex);
101 typedef BOOL (WINAPI *revert_mm_thread_characteristics_function)(HANDLE handle);
102 
103 extern cubeb_ops const wasapi_ops;
104 
105 int wasapi_stream_stop(cubeb_stream * stm);
106 int wasapi_stream_start(cubeb_stream * stm);
107 void close_wasapi_stream(cubeb_stream * stm);
108 int setup_wasapi_stream(cubeb_stream * stm);
109 static char * wstr_to_utf8(const wchar_t * str);
110 static std::unique_ptr<const wchar_t[]> utf8_to_wstr(char* str);
111 
112 }
113 
114 struct cubeb
115 {
116   cubeb_ops const * ops;
117   /* Library dynamically opened to increase the render thread priority, and
118      the two function pointers we need. */
119   HMODULE mmcss_module;
120   set_mm_thread_characteristics_function set_mm_thread_characteristics;
121   revert_mm_thread_characteristics_function revert_mm_thread_characteristics;
122 };
123 
124 class wasapi_endpoint_notification_client;
125 
126 /* We have three possible callbacks we can use with a stream:
127  * - input only
128  * - output only
129  * - synchronized input and output
130  *
131  * Returns true when we should continue to play, false otherwise.
132  */
133 typedef bool (*wasapi_refill_callback)(cubeb_stream * stm);
134 
135 struct cubeb_stream
136 {
137   cubeb * context;
138   /* Mixer pameters. We need to convert the input stream to this
139      samplerate/channel layout, as WASAPI does not resample nor upmix
140      itself. */
141   cubeb_stream_params input_mix_params;
142   cubeb_stream_params output_mix_params;
143   /* Stream parameters. This is what the client requested,
144    * and what will be presented in the callback. */
145   cubeb_stream_params input_stream_params;
146   cubeb_stream_params output_stream_params;
147   /* The input and output device, or NULL for default. */
148   cubeb_devid input_device;
149   cubeb_devid output_device;
150   /* The latency initially requested for this stream, in frames. */
151   unsigned latency;
152   cubeb_state_callback state_callback;
153   cubeb_data_callback data_callback;
154   wasapi_refill_callback refill_callback;
155   void * user_ptr;
156   /* Lifetime considerations:
157      - client, render_client, audio_clock and audio_stream_volume are interface
158        pointer to the IAudioClient.
159      - The lifetime for device_enumerator and notification_client, resampler,
160        mix_buffer are the same as the cubeb_stream instance. */
161 
162   /* Main handle on the WASAPI stream. */
163   IAudioClient * output_client;
164   /* Interface pointer to use the event-driven interface. */
165   IAudioRenderClient * render_client;
166   /* Interface pointer to use the volume facilities. */
167   IAudioStreamVolume * audio_stream_volume;
168   /* Interface pointer to use the stream audio clock. */
169   IAudioClock * audio_clock;
170   /* Frames written to the stream since it was opened. Reset on device
171      change. Uses mix_params.rate. */
172   UINT64 frames_written;
173   /* Frames written to the (logical) stream since it was first
174      created. Updated on device change. Uses stream_params.rate. */
175   UINT64 total_frames_written;
176   /* Last valid reported stream position.  Used to ensure the position
177      reported by stream_get_position increases monotonically. */
178   UINT64 prev_position;
179   /* Device enumerator to be able to be notified when the default
180      device change. */
181   IMMDeviceEnumerator * device_enumerator;
182   /* Device notification client, to be able to be notified when the default
183      audio device changes and route the audio to the new default audio output
184      device */
185   wasapi_endpoint_notification_client * notification_client;
186   /* Main andle to the WASAPI capture stream. */
187   IAudioClient * input_client;
188   /* Interface to use the event driven capture interface */
189   IAudioCaptureClient * capture_client;
190   /* This event is set by the stream_stop and stream_destroy
191      function, so the render loop can exit properly. */
192   HANDLE shutdown_event;
193   /* Set by OnDefaultDeviceChanged when a stream reconfiguration is required.
194      The reconfiguration is handled by the render loop thread. */
195   HANDLE reconfigure_event;
196   /* This is set by WASAPI when we should refill the stream. */
197   HANDLE refill_event;
198   /* This is set by WASAPI when we should read from the input stream. In
199    * practice, we read from the input stream in the output callback, so
200    * this is not used, but it is necessary to start getting input data. */
201   HANDLE input_available_event;
202   /* Each cubeb_stream has its own thread. */
203   HANDLE thread;
204   /* The lock protects all members that are touched by the render thread or
205      change during a device reset, including: audio_clock, audio_stream_volume,
206      client, frames_written, mix_params, total_frames_written, prev_position. */
207   owned_critical_section stream_reset_lock;
208   /* Maximum number of frames that can be passed down in a callback. */
209   uint32_t input_buffer_frame_count;
210   /* Maximum number of frames that can be requested in a callback. */
211   uint32_t output_buffer_frame_count;
212   /* Resampler instance. Resampling will only happen if necessary. */
213   cubeb_resampler * resampler;
214   /* A buffer for up/down mixing multi-channel audio. */
215   float * mix_buffer;
216   /* WASAPI input works in "packets". We re-linearize the audio packets
217    * into this buffer before handing it to the resampler. */
218   auto_array<float> linear_input_buffer;
219   /* Stream volume.  Set via stream_set_volume and used to reset volume on
220      device changes. */
221   float volume;
222   /* True if the stream is draining. */
223   bool draining;
224   /* True when we've destroyed the stream. This pointer is leaked on stream
225    * destruction if we could not join the thread. */
226   std::atomic<std::atomic<bool>*> emergency_bailout;
227 };
228 
229 class wasapi_endpoint_notification_client : public IMMNotificationClient
230 {
231 public:
232   /* The implementation of MSCOM was copied from MSDN. */
233   ULONG STDMETHODCALLTYPE
AddRef()234   AddRef()
235   {
236     return InterlockedIncrement(&ref_count);
237   }
238 
239   ULONG STDMETHODCALLTYPE
Release()240   Release()
241   {
242     ULONG ulRef = InterlockedDecrement(&ref_count);
243     if (0 == ulRef) {
244       delete this;
245     }
246     return ulRef;
247   }
248 
249   HRESULT STDMETHODCALLTYPE
QueryInterface(REFIID riid,VOID ** ppvInterface)250   QueryInterface(REFIID riid, VOID **ppvInterface)
251   {
252     if (__uuidof(IUnknown) == riid) {
253       AddRef();
254       *ppvInterface = (IUnknown*)this;
255     } else if (__uuidof(IMMNotificationClient) == riid) {
256       AddRef();
257       *ppvInterface = (IMMNotificationClient*)this;
258     } else {
259       *ppvInterface = NULL;
260       return E_NOINTERFACE;
261     }
262     return S_OK;
263   }
264 
wasapi_endpoint_notification_client(HANDLE event)265   wasapi_endpoint_notification_client(HANDLE event)
266     : ref_count(1)
267     , reconfigure_event(event)
268   { }
269 
~wasapi_endpoint_notification_client()270   virtual ~wasapi_endpoint_notification_client()
271   { }
272 
273   HRESULT STDMETHODCALLTYPE
OnDefaultDeviceChanged(EDataFlow flow,ERole role,LPCWSTR device_id)274   OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
275   {
276     LOG("Audio device default changed.");
277 
278     /* we only support a single stream type for now. */
279     if (flow != eRender && role != eConsole) {
280       return S_OK;
281     }
282 
283     BOOL ok = SetEvent(reconfigure_event);
284     if (!ok) {
285       LOG("SetEvent on reconfigure_event failed: %x", GetLastError());
286     }
287 
288     return S_OK;
289   }
290 
291   /* The remaining methods are not implemented, they simply log when called (if
292      log is enabled), for debugging. */
OnDeviceAdded(LPCWSTR device_id)293   HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
294   {
295     LOG("Audio device added.");
296     return S_OK;
297   };
298 
OnDeviceRemoved(LPCWSTR device_id)299   HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
300   {
301     LOG("Audio device removed.");
302     return S_OK;
303   }
304 
305   HRESULT STDMETHODCALLTYPE
OnDeviceStateChanged(LPCWSTR device_id,DWORD new_state)306   OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
307   {
308     LOG("Audio device state changed.");
309     return S_OK;
310   }
311 
312   HRESULT STDMETHODCALLTYPE
OnPropertyValueChanged(LPCWSTR device_id,const PROPERTYKEY key)313   OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
314   {
315     LOG("Audio device property value changed.");
316     return S_OK;
317   }
318 private:
319   /* refcount for this instance, necessary to implement MSCOM semantics. */
320   LONG ref_count;
321   HANDLE reconfigure_event;
322 };
323 
324 namespace {
has_input(cubeb_stream * stm)325 bool has_input(cubeb_stream * stm)
326 {
327   return stm->input_stream_params.rate != 0;
328 }
329 
has_output(cubeb_stream * stm)330 bool has_output(cubeb_stream * stm)
331 {
332   return stm->output_stream_params.rate != 0;
333 }
334 
should_upmix(cubeb_stream_params & stream,cubeb_stream_params & mixer)335 bool should_upmix(cubeb_stream_params & stream, cubeb_stream_params & mixer)
336 {
337   return mixer.channels > stream.channels;
338 }
339 
should_downmix(cubeb_stream_params & stream,cubeb_stream_params & mixer)340 bool should_downmix(cubeb_stream_params & stream, cubeb_stream_params & mixer)
341 {
342   return mixer.channels < stream.channels;
343 }
344 
stream_to_mix_samplerate_ratio(cubeb_stream_params & stream,cubeb_stream_params & mixer)345 double stream_to_mix_samplerate_ratio(cubeb_stream_params & stream, cubeb_stream_params & mixer)
346 {
347   return double(stream.rate) / mixer.rate;
348 }
349 
350 
351 uint32_t
get_rate(cubeb_stream * stm)352 get_rate(cubeb_stream * stm)
353 {
354   return has_input(stm) ? stm->input_stream_params.rate
355                         : stm->output_stream_params.rate;
356 }
357 
358 uint32_t
ms_to_hns(uint32_t ms)359 ms_to_hns(uint32_t ms)
360 {
361   return ms * 10000;
362 }
363 
364 uint32_t
hns_to_ms(REFERENCE_TIME hns)365 hns_to_ms(REFERENCE_TIME hns)
366 {
367   return static_cast<uint32_t>(hns / 10000);
368 }
369 
370 double
hns_to_s(REFERENCE_TIME hns)371 hns_to_s(REFERENCE_TIME hns)
372 {
373   return static_cast<double>(hns) / 10000000;
374 }
375 
376 uint32_t
hns_to_frames(cubeb_stream * stm,REFERENCE_TIME hns)377 hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns)
378 {
379   return hns_to_ms(hns * get_rate(stm)) / 1000;
380 }
381 
382 uint32_t
hns_to_frames(uint32_t rate,REFERENCE_TIME hns)383 hns_to_frames(uint32_t rate, REFERENCE_TIME hns)
384 {
385   return hns_to_ms(hns * rate) / 1000;
386 }
387 
388 REFERENCE_TIME
frames_to_hns(cubeb_stream * stm,uint32_t frames)389 frames_to_hns(cubeb_stream * stm, uint32_t frames)
390 {
391    return frames * 1000 / get_rate(stm);
392 }
393 
394 /* Upmix function, copies a mono channel into L and R */
395 template<typename T>
396 void
mono_to_stereo(T * in,long insamples,T * out,int32_t out_channels)397 mono_to_stereo(T * in, long insamples, T * out, int32_t out_channels)
398 {
399   for (int i = 0, j = 0; i < insamples; ++i, j += out_channels) {
400     out[j] = out[j + 1] = in[i];
401   }
402 }
403 
404 template<typename T>
405 void
upmix(T * in,long inframes,T * out,int32_t in_channels,int32_t out_channels)406 upmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channels)
407 {
408   XASSERT(out_channels >= in_channels && in_channels > 0);
409 
410   /* Either way, if we have 2 or more channels, the first two are L and R. */
411   /* If we are playing a mono stream over stereo speakers, copy the data over. */
412   if (in_channels == 1 && out_channels >= 2) {
413     mono_to_stereo(in, inframes, out, out_channels);
414   } else {
415     /* Copy through. */
416     for (int i = 0, o = 0; i < inframes * in_channels;
417         i += in_channels, o += out_channels) {
418       for (int j = 0; j < in_channels; ++j) {
419         out[o + j] = in[i + j];
420       }
421     }
422   }
423 
424   /* Check if more channels. */
425   if (out_channels <= 2) {
426     return;
427   }
428 
429   /* Put silence in remaining channels. */
430   for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) {
431     for (int j = 2; j < out_channels; ++j) {
432       out[o + j] = 0.0;
433     }
434   }
435 }
436 
437 template<typename T>
438 void
downmix(T * in,long inframes,T * out,int32_t in_channels,int32_t out_channels)439 downmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channels)
440 {
441   XASSERT(in_channels >= out_channels);
442   /* We could use a downmix matrix here, applying mixing weight based on the
443      channel, but directsound and winmm simply drop the channels that cannot be
444      rendered by the hardware, so we do the same for consistency. */
445   long out_index = 0;
446   for (long i = 0; i < inframes * in_channels; i += in_channels) {
447     for (int j = 0; j < out_channels; ++j) {
448       out[out_index + j] = in[i + j];
449     }
450     out_index += out_channels;
451   }
452 }
453 
454 /* This returns the size of a frame in the stream, before the eventual upmix
455    occurs. */
456 static size_t
frames_to_bytes_before_mix(cubeb_stream * stm,size_t frames)457 frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames)
458 {
459   size_t stream_frame_size = stm->output_stream_params.channels * sizeof(float);
460   return stream_frame_size * frames;
461 }
462 
463 /* This function handles the processing of the input and output audio,
464  * converting it to rate and channel layout specified at initialization.
465  * It then calls the data callback, via the resampler. */
466 long
refill(cubeb_stream * stm,float * input_buffer,long input_frames_count,float * output_buffer,long output_frames_needed)467 refill(cubeb_stream * stm, float * input_buffer, long input_frames_count,
468        float * output_buffer, long output_frames_needed)
469 {
470   /* If we need to upmix after resampling, resample into the mix buffer to
471      avoid a copy. */
472   float * dest = nullptr;
473   if (has_output(stm)) {
474     if (should_upmix(stm->output_stream_params, stm->output_mix_params) ||
475         should_downmix(stm->output_stream_params, stm->output_mix_params)) {
476       dest = stm->mix_buffer;
477     } else {
478       dest = output_buffer;
479     }
480   }
481 
482   long out_frames = cubeb_resampler_fill(stm->resampler,
483                                          input_buffer,
484                                          &input_frames_count,
485                                          dest,
486                                          output_frames_needed);
487   /* TODO: Report out_frames < 0 as an error via the API. */
488   XASSERT(out_frames >= 0);
489 
490   {
491     auto_lock lock(stm->stream_reset_lock);
492     stm->frames_written += out_frames;
493   }
494 
495   /* Go in draining mode if we got fewer frames than requested. */
496   if (out_frames < output_frames_needed) {
497     LOG("start draining.");
498     stm->draining = true;
499   }
500 
501   /* If this is not true, there will be glitches.
502      It is alright to have produced less frames if we are draining, though. */
503   XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm));
504 
505   if (has_output(stm)) {
506     if (should_upmix(stm->output_stream_params, stm->output_mix_params)) {
507       upmix(dest, out_frames, output_buffer,
508             stm->output_stream_params.channels, stm->output_mix_params.channels);
509     } else if (should_downmix(stm->output_stream_params, stm->output_mix_params)) {
510       downmix(dest, out_frames, output_buffer,
511               stm->output_stream_params.channels, stm->output_mix_params.channels);
512     }
513   }
514 
515   return out_frames;
516 }
517 
518 /* This helper grabs all the frames available from a capture client, put them in
519  * linear_input_buffer. linear_input_buffer should be cleared before the
520  * callback exits. */
get_input_buffer(cubeb_stream * stm)521 bool get_input_buffer(cubeb_stream * stm)
522 {
523   HRESULT hr;
524   UINT32 padding_in;
525 
526   XASSERT(has_input(stm));
527 
528   hr = stm->input_client->GetCurrentPadding(&padding_in);
529   if (FAILED(hr)) {
530     LOG("Failed to get padding");
531     return false;
532   }
533   XASSERT(padding_in <= stm->input_buffer_frame_count);
534   UINT32 total_available_input = padding_in;
535 
536   BYTE * input_packet = NULL;
537   DWORD flags;
538   UINT64 dev_pos;
539   UINT32 next;
540   /* Get input packets until we have captured enough frames, and put them in a
541    * contiguous buffer. */
542   uint32_t offset = 0;
543   while (offset != total_available_input) {
544     hr = stm->capture_client->GetNextPacketSize(&next);
545     if (FAILED(hr)) {
546       LOG("cannot get next packet size: %x", hr);
547       return false;
548     }
549     /* This can happen if the capture stream has stopped. Just return in this
550      * case. */
551     if (!next) {
552       break;
553     }
554 
555     UINT32 packet_size;
556     hr = stm->capture_client->GetBuffer(&input_packet,
557                                         &packet_size,
558                                         &flags,
559                                         &dev_pos,
560                                         NULL);
561     if (FAILED(hr)) {
562       LOG("GetBuffer failed for capture: %x", hr);
563       return false;
564     }
565     XASSERT(packet_size == next);
566     if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
567       LOG("insert silence: ps=%u", packet_size);
568       stm->linear_input_buffer.push_silence(packet_size * stm->input_stream_params.channels);
569     } else {
570       if (should_upmix(stm->input_mix_params, stm->input_stream_params)) {
571         bool ok = stm->linear_input_buffer.reserve(stm->linear_input_buffer.length() +
572                                                    packet_size * stm->input_stream_params.channels);
573         assert(ok);
574         upmix(reinterpret_cast<float*>(input_packet), packet_size,
575               stm->linear_input_buffer.data() + stm->linear_input_buffer.length(),
576               stm->input_mix_params.channels,
577               stm->input_stream_params.channels);
578         stm->linear_input_buffer.set_length(stm->linear_input_buffer.length() + packet_size * stm->input_stream_params.channels);
579       } else if (should_downmix(stm->input_mix_params, stm->input_stream_params)) {
580         bool ok = stm->linear_input_buffer.reserve(stm->linear_input_buffer.length() +
581                                                    packet_size * stm->input_stream_params.channels);
582         assert(ok);
583         downmix(reinterpret_cast<float*>(input_packet), packet_size,
584                 stm->linear_input_buffer.data() + stm->linear_input_buffer.length(),
585                 stm->input_mix_params.channels,
586                 stm->input_stream_params.channels);
587         stm->linear_input_buffer.set_length(stm->linear_input_buffer.length() + packet_size * stm->input_stream_params.channels);
588       } else {
589         stm->linear_input_buffer.push(reinterpret_cast<float*>(input_packet),
590                                       packet_size * stm->input_stream_params.channels);
591       }
592     }
593     hr = stm->capture_client->ReleaseBuffer(packet_size);
594     if (FAILED(hr)) {
595       LOG("FAILED to release intput buffer");
596       return false;
597     }
598     offset += packet_size;
599   }
600 
601   assert(stm->linear_input_buffer.length() >= total_available_input &&
602          offset == total_available_input);
603 
604   return true;
605 }
606 
607 /* Get an output buffer from the render_client. It has to be released before
608  * exiting the callback. */
get_output_buffer(cubeb_stream * stm,float * & buffer,size_t & frame_count)609 bool get_output_buffer(cubeb_stream * stm, float *& buffer, size_t & frame_count)
610 {
611   UINT32 padding_out;
612   HRESULT hr;
613 
614   XASSERT(has_output(stm));
615 
616   hr = stm->output_client->GetCurrentPadding(&padding_out);
617   if (FAILED(hr)) {
618     LOG("Failed to get padding: %x", hr);
619     return false;
620   }
621   XASSERT(padding_out <= stm->output_buffer_frame_count);
622 
623   if (stm->draining) {
624     if (padding_out == 0) {
625       LOG("Draining finished.");
626       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
627       return false;
628     }
629     LOG("Draining.");
630     return true;
631   }
632 
633   frame_count = stm->output_buffer_frame_count - padding_out;
634   BYTE * output_buffer;
635 
636   hr = stm->render_client->GetBuffer(frame_count, &output_buffer);
637   if (FAILED(hr)) {
638     LOG("cannot get render buffer");
639     return false;
640   }
641 
642   buffer = reinterpret_cast<float*>(output_buffer);
643 
644   return true;
645 }
646 
647 /**
648  * This function gets input data from a input device, and pass it along with an
649  * output buffer to the resamplers.  */
650 bool
refill_callback_duplex(cubeb_stream * stm)651 refill_callback_duplex(cubeb_stream * stm)
652 {
653   HRESULT hr;
654   float * output_buffer = nullptr;
655   size_t output_frames = 0;
656   size_t input_frames;
657   bool rv;
658 
659   XASSERT(has_input(stm) && has_output(stm));
660 
661   rv = get_input_buffer(stm);
662   if (!rv) {
663     return rv;
664   }
665 
666   input_frames = stm->linear_input_buffer.length() / stm->input_stream_params.channels;
667   if (!input_frames) {
668     return true;
669   }
670 
671   rv = get_output_buffer(stm, output_buffer, output_frames);
672   if (!rv) {
673     hr = stm->render_client->ReleaseBuffer(output_frames, 0);
674     return rv;
675   }
676 
677   /* This can only happen when debugging, and having breakpoints set in the
678    * callback in a way that it makes the stream underrun. */
679   if (output_frames == 0) {
680     return true;
681   }
682 
683   // When WASAPI has not filled the input buffer yet, send silence.
684   double output_duration = double(output_frames) / stm->output_mix_params.rate;
685   double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate;
686   if (input_duration < output_duration) {
687     size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate));
688     LOG("padding silence: out=%f in=%f pad=%u", output_duration, input_duration, padding);
689     stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels);
690   }
691 
692   LOGV("Duplex callback: input frames: %zu, output frames: %zu",
693        stm->linear_input_buffer.length(), output_frames);
694 
695   refill(stm,
696          stm->linear_input_buffer.data(),
697          stm->linear_input_buffer.length(),
698          output_buffer,
699          output_frames);
700 
701   stm->linear_input_buffer.clear();
702 
703   hr = stm->render_client->ReleaseBuffer(output_frames, 0);
704   if (FAILED(hr)) {
705     LOG("failed to release buffer: %x", hr);
706     return false;
707   }
708   return true;
709 }
710 
711 bool
refill_callback_input(cubeb_stream * stm)712 refill_callback_input(cubeb_stream * stm)
713 {
714   bool rv, consumed_all_buffer;
715 
716   XASSERT(has_input(stm) && !has_output(stm));
717 
718   rv = get_input_buffer(stm);
719   if (!rv) {
720     return rv;
721   }
722 
723   // This can happen at the very beginning of the stream.
724   if (!stm->linear_input_buffer.length()) {
725     return true;
726   }
727 
728   LOGV("Input callback: input frames: %zu", stm->linear_input_buffer.length());
729 
730   long read = refill(stm,
731                      stm->linear_input_buffer.data(),
732                      stm->linear_input_buffer.length(),
733                      nullptr,
734                      0);
735 
736   consumed_all_buffer = read == stm->linear_input_buffer.length();
737 
738   stm->linear_input_buffer.clear();
739 
740   return consumed_all_buffer;
741 }
742 
743 bool
refill_callback_output(cubeb_stream * stm)744 refill_callback_output(cubeb_stream * stm)
745 {
746   bool rv;
747   HRESULT hr;
748   float * output_buffer = nullptr;
749   size_t output_frames = 0;
750 
751   XASSERT(!has_input(stm) && has_output(stm));
752 
753   rv = get_output_buffer(stm, output_buffer, output_frames);
754   if (!rv) {
755     return rv;
756   }
757 
758   if (stm->draining || output_frames == 0) {
759     return true;
760   }
761 
762   long got = refill(stm,
763                     nullptr,
764                     0,
765                     output_buffer,
766                     output_frames);
767 
768   LOGV("Output callback: output frames requested: %zu, got %ld",
769        output_frames, got);
770 
771   XASSERT(got >= 0);
772   XASSERT(got == output_frames || stm->draining);
773 
774   hr = stm->render_client->ReleaseBuffer(got, 0);
775   if (FAILED(hr)) {
776     LOG("failed to release buffer: %x", hr);
777     return false;
778   }
779 
780   return got == output_frames || stm->draining;
781 }
782 
783 static unsigned int __stdcall
wasapi_stream_render_loop(LPVOID stream)784 wasapi_stream_render_loop(LPVOID stream)
785 {
786   cubeb_stream * stm = static_cast<cubeb_stream *>(stream);
787   std::atomic<bool> * emergency_bailout = stm->emergency_bailout;
788 
789   bool is_playing = true;
790   HANDLE wait_array[4] = {
791     stm->shutdown_event,
792     stm->reconfigure_event,
793     stm->refill_event,
794     stm->input_available_event
795   };
796   HANDLE mmcss_handle = NULL;
797   HRESULT hr = 0;
798   DWORD mmcss_task_index = 0;
799   auto_com com;
800   if (!com.ok()) {
801     LOG("COM initialization failed on render_loop thread.");
802     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
803     return 0;
804   }
805 
806   /* We could consider using "Pro Audio" here for WebAudio and
807      maybe WebRTC. */
808   mmcss_handle =
809     stm->context->set_mm_thread_characteristics("Audio", &mmcss_task_index);
810   if (!mmcss_handle) {
811     /* This is not fatal, but we might glitch under heavy load. */
812     LOG("Unable to use mmcss to bump the render thread priority: %x", GetLastError());
813   }
814 
815   // This has already been nulled out, simply exit.
816   if (!emergency_bailout) {
817     is_playing = false;
818   }
819 
820   /* WaitForMultipleObjects timeout can trigger in cases where we don't want to
821      treat it as a timeout, such as across a system sleep/wake cycle.  Trigger
822      the timeout error handling only when the timeout_limit is reached, which is
823      reset on each successful loop. */
824   unsigned timeout_count = 0;
825   const unsigned timeout_limit = 5;
826   while (is_playing) {
827     // We want to check the emergency bailout variable before a
828     // and after the WaitForMultipleObject, because the handles WaitForMultipleObjects
829     // is going to wait on might have been closed already.
830     if (*emergency_bailout) {
831       delete emergency_bailout;
832       return 0;
833     }
834     DWORD waitResult = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
835                                               wait_array,
836                                               FALSE,
837                                               1000);
838     if (*emergency_bailout) {
839       delete emergency_bailout;
840       return 0;
841     }
842     if (waitResult != WAIT_TIMEOUT) {
843       timeout_count = 0;
844     }
845     switch (waitResult) {
846     case WAIT_OBJECT_0: { /* shutdown */
847       is_playing = false;
848       /* We don't check if the drain is actually finished here, we just want to
849          shutdown. */
850       if (stm->draining) {
851         stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
852       }
853       continue;
854     }
855     case WAIT_OBJECT_0 + 1: { /* reconfigure */
856       XASSERT(stm->output_client || stm->input_client);
857       LOG("Reconfiguring the stream");
858       /* Close the stream */
859       if (stm->output_client) {
860         stm->output_client->Stop();
861         LOG("Output stopped.");
862       }
863       if (stm->input_client) {
864         stm->input_client->Stop();
865         LOG("Input stopped.");
866       }
867       {
868         auto_lock lock(stm->stream_reset_lock);
869         close_wasapi_stream(stm);
870         LOG("Stream closed.");
871         /* Reopen a stream and start it immediately. This will automatically pick the
872            new default device for this role. */
873         int r = setup_wasapi_stream(stm);
874         if (r != CUBEB_OK) {
875           LOG("Error setting up the stream during reconfigure.");
876           /* Don't destroy the stream here, since we expect the caller to do
877              so after the error has propagated via the state callback. */
878           is_playing = false;
879           hr = E_FAIL;
880           continue;
881         }
882         LOG("Stream setup successfuly.");
883       }
884       XASSERT(stm->output_client || stm->input_client);
885       if (stm->output_client) {
886         stm->output_client->Start();
887         LOG("Output started after reconfigure.");
888       }
889       if (stm->input_client) {
890         stm->input_client->Start();
891         LOG("Input started after reconfigure.");
892       }
893       break;
894     }
895     case WAIT_OBJECT_0 + 2:  /* refill */
896       XASSERT(has_input(stm) && has_output(stm) ||
897               !has_input(stm) && has_output(stm));
898       is_playing = stm->refill_callback(stm);
899       break;
900     case WAIT_OBJECT_0 + 3: /* input available */
901       if (has_input(stm) && has_output(stm)) { continue; }
902       is_playing = stm->refill_callback(stm);
903       break;
904     case WAIT_TIMEOUT:
905       XASSERT(stm->shutdown_event == wait_array[0]);
906       if (++timeout_count >= timeout_limit) {
907         LOG("Render loop reached the timeout limit.");
908         is_playing = false;
909         hr = E_FAIL;
910       }
911       break;
912     default:
913       LOG("case %d not handled in render loop.", waitResult);
914       abort();
915     }
916   }
917 
918   if (FAILED(hr)) {
919     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
920   }
921 
922   stm->context->revert_mm_thread_characteristics(mmcss_handle);
923 
924   return 0;
925 }
926 
927 void wasapi_destroy(cubeb * context);
928 
set_mm_thread_characteristics_noop(const char *,LPDWORD mmcss_task_index)929 HANDLE WINAPI set_mm_thread_characteristics_noop(const char *, LPDWORD mmcss_task_index)
930 {
931   return (HANDLE)1;
932 }
933 
revert_mm_thread_characteristics_noop(HANDLE mmcss_handle)934 BOOL WINAPI revert_mm_thread_characteristics_noop(HANDLE mmcss_handle)
935 {
936   return true;
937 }
938 
register_notification_client(cubeb_stream * stm)939 HRESULT register_notification_client(cubeb_stream * stm)
940 {
941   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
942                                 NULL, CLSCTX_INPROC_SERVER,
943                                 IID_PPV_ARGS(&stm->device_enumerator));
944   if (FAILED(hr)) {
945     LOG("Could not get device enumerator: %x", hr);
946     return hr;
947   }
948 
949   stm->notification_client = new wasapi_endpoint_notification_client(stm->reconfigure_event);
950 
951   hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client);
952   if (FAILED(hr)) {
953     LOG("Could not register endpoint notification callback: %x", hr);
954     SafeRelease(stm->notification_client);
955     stm->notification_client = nullptr;
956     SafeRelease(stm->device_enumerator);
957     stm->device_enumerator = nullptr;
958   }
959 
960   return hr;
961 }
962 
unregister_notification_client(cubeb_stream * stm)963 HRESULT unregister_notification_client(cubeb_stream * stm)
964 {
965   XASSERT(stm);
966   HRESULT hr;
967 
968   if (!stm->device_enumerator) {
969     return S_OK;
970   }
971 
972   hr = stm->device_enumerator->UnregisterEndpointNotificationCallback(stm->notification_client);
973   if (FAILED(hr)) {
974     // We can't really do anything here, we'll probably leak the
975     // notification client, but we can at least release the enumerator.
976     SafeRelease(stm->device_enumerator);
977     return S_OK;
978   }
979 
980   SafeRelease(stm->notification_client);
981   SafeRelease(stm->device_enumerator);
982 
983   return S_OK;
984 }
985 
get_endpoint(IMMDevice ** device,LPCWSTR devid)986 HRESULT get_endpoint(IMMDevice ** device, LPCWSTR devid)
987 {
988   IMMDeviceEnumerator * enumerator;
989   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
990                                 NULL, CLSCTX_INPROC_SERVER,
991                                 IID_PPV_ARGS(&enumerator));
992   if (FAILED(hr)) {
993     LOG("Could not get device enumerator: %x", hr);
994     return hr;
995   }
996 
997   hr = enumerator->GetDevice(devid, device);
998   if (FAILED(hr)) {
999     LOG("Could not get device: %x", hr);
1000     SafeRelease(enumerator);
1001     return hr;
1002   }
1003 
1004   SafeRelease(enumerator);
1005 
1006   return S_OK;
1007 }
1008 
get_default_endpoint(IMMDevice ** device,EDataFlow direction)1009 HRESULT get_default_endpoint(IMMDevice ** device, EDataFlow direction)
1010 {
1011   IMMDeviceEnumerator * enumerator;
1012   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
1013                                 NULL, CLSCTX_INPROC_SERVER,
1014                                 IID_PPV_ARGS(&enumerator));
1015   if (FAILED(hr)) {
1016     LOG("Could not get device enumerator: %x", hr);
1017     return hr;
1018   }
1019   hr = enumerator->GetDefaultAudioEndpoint(direction, eConsole, device);
1020   if (FAILED(hr)) {
1021     LOG("Could not get default audio endpoint: %x", hr);
1022     SafeRelease(enumerator);
1023     return hr;
1024   }
1025 
1026   SafeRelease(enumerator);
1027 
1028   return ERROR_SUCCESS;
1029 }
1030 
1031 double
current_stream_delay(cubeb_stream * stm)1032 current_stream_delay(cubeb_stream * stm)
1033 {
1034   stm->stream_reset_lock.assert_current_thread_owns();
1035 
1036   /* If the default audio endpoint went away during playback and we weren't
1037      able to configure a new one, it's possible the caller may call this
1038      before the error callback has propogated back. */
1039   if (!stm->audio_clock) {
1040     return 0;
1041   }
1042 
1043   UINT64 freq;
1044   HRESULT hr = stm->audio_clock->GetFrequency(&freq);
1045   if (FAILED(hr)) {
1046     LOG("GetFrequency failed: %x", hr);
1047     return 0;
1048   }
1049 
1050   UINT64 pos;
1051   hr = stm->audio_clock->GetPosition(&pos, NULL);
1052   if (FAILED(hr)) {
1053     LOG("GetPosition failed: %x", hr);
1054     return 0;
1055   }
1056 
1057   double cur_pos = static_cast<double>(pos) / freq;
1058   double max_pos = static_cast<double>(stm->frames_written)  / stm->output_mix_params.rate;
1059   double delay = max_pos - cur_pos;
1060   XASSERT(delay >= 0);
1061 
1062   return delay;
1063 }
1064 
1065 int
stream_set_volume(cubeb_stream * stm,float volume)1066 stream_set_volume(cubeb_stream * stm, float volume)
1067 {
1068   stm->stream_reset_lock.assert_current_thread_owns();
1069 
1070   if (!stm->audio_stream_volume) {
1071     return CUBEB_ERROR;
1072   }
1073 
1074   uint32_t channels;
1075   HRESULT hr = stm->audio_stream_volume->GetChannelCount(&channels);
1076   if (hr != S_OK) {
1077     LOG("could not get the channel count: %x", hr);
1078     return CUBEB_ERROR;
1079   }
1080 
1081   /* up to 9.1 for now */
1082   if (channels > 10) {
1083     return CUBEB_ERROR_NOT_SUPPORTED;
1084   }
1085 
1086   float volumes[10];
1087   for (uint32_t i = 0; i < channels; i++) {
1088     volumes[i] = volume;
1089   }
1090 
1091   hr = stm->audio_stream_volume->SetAllVolumes(channels,  volumes);
1092   if (hr != S_OK) {
1093     LOG("could not set the channels volume: %x", hr);
1094     return CUBEB_ERROR;
1095   }
1096 
1097   return CUBEB_OK;
1098 }
1099 } // namespace anonymous
1100 
1101 extern "C" {
wasapi_init(cubeb ** context,char const * context_name)1102 int wasapi_init(cubeb ** context, char const * context_name)
1103 {
1104   HRESULT hr;
1105   auto_com com;
1106   if (!com.ok()) {
1107     return CUBEB_ERROR;
1108   }
1109 
1110   /* We don't use the device yet, but need to make sure we can initialize one
1111      so that this backend is not incorrectly enabled on platforms that don't
1112      support WASAPI. */
1113   IMMDevice * device;
1114   hr = get_default_endpoint(&device, eRender);
1115   if (FAILED(hr)) {
1116     LOG("Could not get device: %x", hr);
1117     return CUBEB_ERROR;
1118   }
1119   SafeRelease(device);
1120 
1121   cubeb * ctx = (cubeb *)calloc(1, sizeof(cubeb));
1122   if (!ctx) {
1123     return CUBEB_ERROR;
1124   }
1125 
1126   ctx->ops = &wasapi_ops;
1127 
1128   ctx->mmcss_module = LoadLibraryA("Avrt.dll");
1129 
1130   if (ctx->mmcss_module) {
1131     ctx->set_mm_thread_characteristics =
1132       (set_mm_thread_characteristics_function) GetProcAddress(
1133           ctx->mmcss_module, "AvSetMmThreadCharacteristicsA");
1134     ctx->revert_mm_thread_characteristics =
1135       (revert_mm_thread_characteristics_function) GetProcAddress(
1136           ctx->mmcss_module, "AvRevertMmThreadCharacteristics");
1137     if (!(ctx->set_mm_thread_characteristics && ctx->revert_mm_thread_characteristics)) {
1138       LOG("Could not load AvSetMmThreadCharacteristics or AvRevertMmThreadCharacteristics: %x", GetLastError());
1139       FreeLibrary(ctx->mmcss_module);
1140     }
1141   } else {
1142     // This is not a fatal error, but we might end up glitching when
1143     // the system is under high load.
1144     LOG("Could not load Avrt.dll");
1145     ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
1146     ctx->revert_mm_thread_characteristics = &revert_mm_thread_characteristics_noop;
1147   }
1148 
1149   *context = ctx;
1150 
1151   return CUBEB_OK;
1152 }
1153 }
1154 
1155 namespace {
stop_and_join_render_thread(cubeb_stream * stm)1156 bool stop_and_join_render_thread(cubeb_stream * stm)
1157 {
1158   bool rv = true;
1159   LOG("Stop and join render thread.");
1160   if (!stm->thread) {
1161     LOG("No thread present.");
1162     return true;
1163   }
1164 
1165   // If we've already leaked the thread, just return,
1166   // there is not much we can do.
1167   if (!stm->emergency_bailout.load()) {
1168     return false;
1169   }
1170 
1171   BOOL ok = SetEvent(stm->shutdown_event);
1172   if (!ok) {
1173     LOG("Destroy SetEvent failed: %d", GetLastError());
1174   }
1175 
1176   /* Wait five seconds for the rendering thread to return. It's supposed to
1177    * check its event loop very often, five seconds is rather conservative. */
1178   DWORD r = WaitForSingleObject(stm->thread, 5000);
1179   if (r == WAIT_TIMEOUT) {
1180     /* Something weird happened, leak the thread and continue the shutdown
1181      * process. */
1182     *(stm->emergency_bailout) = true;
1183     // We give the ownership to the rendering thread.
1184     stm->emergency_bailout = nullptr;
1185     LOG("Destroy WaitForSingleObject on thread timed out,"
1186         " leaking the thread: %d", GetLastError());
1187     rv = false;
1188   }
1189   if (r == WAIT_FAILED) {
1190     *(stm->emergency_bailout) = true;
1191     // We give the ownership to the rendering thread.
1192     stm->emergency_bailout = nullptr;
1193     LOG("Destroy WaitForSingleObject on thread failed: %d", GetLastError());
1194     rv = false;
1195   }
1196 
1197 
1198   // Only attempts to close and null out the thread and event if the
1199   // WaitForSingleObject above succeeded, so that calling this function again
1200   // attemps to clean up the thread and event each time.
1201   if (rv) {
1202     LOG("Closing thread.");
1203     CloseHandle(stm->thread);
1204     stm->thread = NULL;
1205 
1206     CloseHandle(stm->shutdown_event);
1207     stm->shutdown_event = 0;
1208   }
1209 
1210   return rv;
1211 }
1212 
wasapi_destroy(cubeb * context)1213 void wasapi_destroy(cubeb * context)
1214 {
1215   if (context->mmcss_module) {
1216     FreeLibrary(context->mmcss_module);
1217   }
1218   free(context);
1219 }
1220 
wasapi_get_backend_id(cubeb * context)1221 char const * wasapi_get_backend_id(cubeb * context)
1222 {
1223   return "wasapi";
1224 }
1225 
1226 int
wasapi_get_max_channel_count(cubeb * ctx,uint32_t * max_channels)1227 wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
1228 {
1229   HRESULT hr;
1230   IAudioClient * client;
1231   WAVEFORMATEX * mix_format;
1232   auto_com com;
1233   if (!com.ok()) {
1234     return CUBEB_ERROR;
1235   }
1236 
1237   XASSERT(ctx && max_channels);
1238 
1239   IMMDevice * device;
1240   hr = get_default_endpoint(&device, eRender);
1241   if (FAILED(hr)) {
1242     return CUBEB_ERROR;
1243   }
1244 
1245   hr = device->Activate(__uuidof(IAudioClient),
1246                         CLSCTX_INPROC_SERVER,
1247                         NULL, (void **)&client);
1248   SafeRelease(device);
1249   if (FAILED(hr)) {
1250     return CUBEB_ERROR;
1251   }
1252 
1253   hr = client->GetMixFormat(&mix_format);
1254   if (FAILED(hr)) {
1255     SafeRelease(client);
1256     return CUBEB_ERROR;
1257   }
1258 
1259   *max_channels = mix_format->nChannels;
1260 
1261   CoTaskMemFree(mix_format);
1262   SafeRelease(client);
1263 
1264   return CUBEB_OK;
1265 }
1266 
1267 int
wasapi_get_min_latency(cubeb * ctx,cubeb_stream_params params,uint32_t * latency_frames)1268 wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
1269 {
1270   HRESULT hr;
1271   IAudioClient * client;
1272   REFERENCE_TIME default_period;
1273   auto_com com;
1274   if (!com.ok()) {
1275     return CUBEB_ERROR;
1276   }
1277 
1278   if (params.format != CUBEB_SAMPLE_FLOAT32NE) {
1279     return CUBEB_ERROR_INVALID_FORMAT;
1280   }
1281 
1282   IMMDevice * device;
1283   hr = get_default_endpoint(&device, eRender);
1284   if (FAILED(hr)) {
1285     LOG("Could not get default endpoint: %x", hr);
1286     return CUBEB_ERROR;
1287   }
1288 
1289   hr = device->Activate(__uuidof(IAudioClient),
1290                         CLSCTX_INPROC_SERVER,
1291                         NULL, (void **)&client);
1292   SafeRelease(device);
1293   if (FAILED(hr)) {
1294     LOG("Could not activate device for latency: %x", hr);
1295     return CUBEB_ERROR;
1296   }
1297 
1298   /* The second parameter is for exclusive mode, that we don't use. */
1299   hr = client->GetDevicePeriod(&default_period, NULL);
1300   if (FAILED(hr)) {
1301     SafeRelease(client);
1302     LOG("Could not get device period: %x", hr);
1303     return CUBEB_ERROR;
1304   }
1305 
1306   LOG("default device period: %lld", default_period);
1307 
1308   /* According to the docs, the best latency we can achieve is by synchronizing
1309      the stream and the engine.
1310      http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
1311 
1312   *latency_frames = hns_to_frames(params.rate, default_period);
1313 
1314   LOG("Minimum latency in frames: %u", *latency_frames);
1315 
1316   SafeRelease(client);
1317 
1318   return CUBEB_OK;
1319 }
1320 
1321 int
wasapi_get_preferred_sample_rate(cubeb * ctx,uint32_t * rate)1322 wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
1323 {
1324   HRESULT hr;
1325   IAudioClient * client;
1326   WAVEFORMATEX * mix_format;
1327   auto_com com;
1328   if (!com.ok()) {
1329     return CUBEB_ERROR;
1330   }
1331 
1332   IMMDevice * device;
1333   hr = get_default_endpoint(&device, eRender);
1334   if (FAILED(hr)) {
1335     return CUBEB_ERROR;
1336   }
1337 
1338   hr = device->Activate(__uuidof(IAudioClient),
1339                         CLSCTX_INPROC_SERVER,
1340                         NULL, (void **)&client);
1341   SafeRelease(device);
1342   if (FAILED(hr)) {
1343     return CUBEB_ERROR;
1344   }
1345 
1346   hr = client->GetMixFormat(&mix_format);
1347   if (FAILED(hr)) {
1348     SafeRelease(client);
1349     return CUBEB_ERROR;
1350   }
1351 
1352   *rate = mix_format->nSamplesPerSec;
1353 
1354   LOG("Preferred sample rate for output: %u", *rate);
1355 
1356   CoTaskMemFree(mix_format);
1357   SafeRelease(client);
1358 
1359   return CUBEB_OK;
1360 }
1361 
1362 void wasapi_stream_destroy(cubeb_stream * stm);
1363 
1364 /* Based on the mix format and the stream format, try to find a way to play
1365    what the user requested. */
1366 static void
handle_channel_layout(cubeb_stream * stm,WAVEFORMATEX ** mix_format,const cubeb_stream_params * stream_params)1367 handle_channel_layout(cubeb_stream * stm,  WAVEFORMATEX ** mix_format, const cubeb_stream_params * stream_params)
1368 {
1369   /* Common case: the hardware is stereo. Up-mixing and down-mixing will be
1370      handled in the callback. */
1371   if ((*mix_format)->nChannels <= 2) {
1372     return;
1373   }
1374 
1375   /* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1],
1376      so the reinterpret_cast below should be safe. In practice, this is not
1377      true, and we just want to bail out and let the rest of the code find a good
1378      conversion path instead of trying to make WASAPI do it by itself.
1379      [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/
1380   if ((*mix_format)->wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
1381     return;
1382   }
1383 
1384   WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(*mix_format);
1385 
1386   /* Stash a copy of the original mix format in case we need to restore it later. */
1387   WAVEFORMATEXTENSIBLE hw_mix_format = *format_pcm;
1388 
1389   /* The hardware is in surround mode, we want to only use front left and front
1390      right. Try that, and check if it works. */
1391   switch (stream_params->channels) {
1392     case 1: /* Mono */
1393       format_pcm->dwChannelMask = KSAUDIO_SPEAKER_MONO;
1394       break;
1395     case 2: /* Stereo */
1396       format_pcm->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
1397       break;
1398     default:
1399       XASSERT(false && "Channel layout not supported.");
1400       break;
1401   }
1402   (*mix_format)->nChannels = stream_params->channels;
1403   (*mix_format)->nBlockAlign = ((*mix_format)->wBitsPerSample * (*mix_format)->nChannels) / 8;
1404   (*mix_format)->nAvgBytesPerSec = (*mix_format)->nSamplesPerSec * (*mix_format)->nBlockAlign;
1405   format_pcm->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1406   (*mix_format)->wBitsPerSample = 32;
1407   format_pcm->Samples.wValidBitsPerSample = (*mix_format)->wBitsPerSample;
1408 
1409   /* Check if wasapi will accept our channel layout request. */
1410   WAVEFORMATEX * closest;
1411   HRESULT hr = stm->output_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
1412                                                      *mix_format,
1413                                                      &closest);
1414   if (hr == S_FALSE) {
1415     /* Not supported, but WASAPI gives us a suggestion. Use it, and handle the
1416        eventual upmix/downmix ourselves */
1417     LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
1418     WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
1419     XASSERT(closest_pcm->SubFormat == format_pcm->SubFormat);
1420     CoTaskMemFree(*mix_format);
1421     *mix_format = closest;
1422   } else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
1423     /* Not supported, no suggestion. This should not happen, but it does in the
1424        field with some sound cards. We restore the mix format, and let the rest
1425        of the code figure out the right conversion path. */
1426     *reinterpret_cast<WAVEFORMATEXTENSIBLE *>(*mix_format) = hw_mix_format;
1427   } else if (hr == S_OK) {
1428     LOG("Requested format accepted by WASAPI.");
1429   } else {
1430     LOG("IsFormatSupported unhandled error: %x", hr);
1431   }
1432 }
1433 
1434 #define DIRECTION_NAME (direction == eCapture ? "capture" : "render")
1435 
1436 template<typename T>
setup_wasapi_stream_one_side(cubeb_stream * stm,cubeb_stream_params * stream_params,cubeb_devid devid,EDataFlow direction,REFIID riid,IAudioClient ** audio_client,uint32_t * buffer_frame_count,HANDLE & event,T ** render_or_capture_client,cubeb_stream_params * mix_params)1437 int setup_wasapi_stream_one_side(cubeb_stream * stm,
1438                                  cubeb_stream_params * stream_params,
1439                                  cubeb_devid devid,
1440                                  EDataFlow direction,
1441                                  REFIID riid,
1442                                  IAudioClient ** audio_client,
1443                                  uint32_t * buffer_frame_count,
1444                                  HANDLE & event,
1445                                  T ** render_or_capture_client,
1446                                  cubeb_stream_params * mix_params)
1447 {
1448   IMMDevice * device;
1449   WAVEFORMATEX * mix_format;
1450   HRESULT hr;
1451 
1452   stm->stream_reset_lock.assert_current_thread_owns();
1453   bool try_again = false;
1454   // This loops until we find a device that works, or we've exhausted all
1455   // possibilities.
1456   do {
1457     if (devid) {
1458       std::unique_ptr<const wchar_t[]> id(utf8_to_wstr(reinterpret_cast<char*>(devid)));
1459       hr = get_endpoint(&device, id.get());
1460       if (FAILED(hr)) {
1461         LOG("Could not get %s endpoint, error: %x\n", DIRECTION_NAME, hr);
1462         return CUBEB_ERROR;
1463       }
1464     }
1465     else {
1466       hr = get_default_endpoint(&device, direction);
1467       if (FAILED(hr)) {
1468         LOG("Could not get default %s endpoint, error: %x\n", DIRECTION_NAME, hr);
1469         return CUBEB_ERROR;
1470       }
1471     }
1472 
1473     /* Get a client. We will get all other interfaces we need from
1474      * this pointer. */
1475     hr = device->Activate(__uuidof(IAudioClient),
1476                           CLSCTX_INPROC_SERVER,
1477                           NULL, (void **)audio_client);
1478     SafeRelease(device);
1479     if (FAILED(hr)) {
1480       LOG("Could not activate the device to get an audio"
1481           " client for %s: error: %x\n", DIRECTION_NAME, hr);
1482       // A particular device can't be activated because it has been
1483       // unplugged, try fall back to the default audio device.
1484       if (devid && hr == AUDCLNT_E_DEVICE_INVALIDATED) {
1485         LOG("Trying again with the default %s audio device.", DIRECTION_NAME);
1486         devid = nullptr;
1487         try_again = true;
1488       } else {
1489         return CUBEB_ERROR;
1490       }
1491     } else {
1492       try_again = false;
1493     }
1494   } while (try_again);
1495 
1496   /* We have to distinguish between the format the mixer uses,
1497    * and the format the stream we want to play uses. */
1498   hr = (*audio_client)->GetMixFormat(&mix_format);
1499   if (FAILED(hr)) {
1500     LOG("Could not fetch current mix format from the audio"
1501         " client for %s: error: %x", DIRECTION_NAME, hr);
1502     return CUBEB_ERROR;
1503   }
1504 
1505   handle_channel_layout(stm, &mix_format, stream_params);
1506 
1507   /* Shared mode WASAPI always supports float32 sample format, so this
1508    * is safe. */
1509   mix_params->format = CUBEB_SAMPLE_FLOAT32NE;
1510   mix_params->rate = mix_format->nSamplesPerSec;
1511   mix_params->channels = mix_format->nChannels;
1512   LOG("Setup requested=[f=%d r=%u c=%u] mix=[f=%d r=%u c=%u]",
1513       stream_params->format, stream_params->rate, stream_params->channels,
1514       mix_params->format, mix_params->rate, mix_params->channels);
1515 
1516   hr = (*audio_client)->Initialize(AUDCLNT_SHAREMODE_SHARED,
1517                                    AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1518                                    AUDCLNT_STREAMFLAGS_NOPERSIST,
1519                                    frames_to_hns(stm, stm->latency),
1520                                    0,
1521                                    mix_format,
1522                                    NULL);
1523   if (FAILED(hr)) {
1524     LOG("Unable to initialize audio client for %s: %x.", DIRECTION_NAME, hr);
1525     return CUBEB_ERROR;
1526   }
1527 
1528   CoTaskMemFree(mix_format);
1529 
1530   hr = (*audio_client)->GetBufferSize(buffer_frame_count);
1531   if (FAILED(hr)) {
1532     LOG("Could not get the buffer size from the client"
1533         " for %s %x.", DIRECTION_NAME, hr);
1534     return CUBEB_ERROR;
1535   }
1536 
1537   // Input is up/down mixed when depacketized in get_input_buffer.
1538   if (has_output(stm) &&
1539       (should_upmix(*stream_params, *mix_params) ||
1540        should_downmix(*stream_params, *mix_params))) {
1541     stm->mix_buffer = (float *)malloc(frames_to_bytes_before_mix(stm, *buffer_frame_count));
1542   }
1543 
1544   hr = (*audio_client)->SetEventHandle(event);
1545   if (FAILED(hr)) {
1546     LOG("Could set the event handle for the %s client %x.",
1547         DIRECTION_NAME, hr);
1548     return CUBEB_ERROR;
1549   }
1550 
1551   hr = (*audio_client)->GetService(riid, (void **)render_or_capture_client);
1552   if (FAILED(hr)) {
1553     LOG("Could not get the %s client %x.", DIRECTION_NAME, hr);
1554     return CUBEB_ERROR;
1555   }
1556 
1557   return CUBEB_OK;
1558 }
1559 
1560 #undef DIRECTION_NAME
1561 
setup_wasapi_stream(cubeb_stream * stm)1562 int setup_wasapi_stream(cubeb_stream * stm)
1563 {
1564   HRESULT hr;
1565   int rv;
1566 
1567   stm->stream_reset_lock.assert_current_thread_owns();
1568 
1569   auto_com com;
1570   if (!com.ok()) {
1571     LOG("Failure to initialize COM.");
1572     return CUBEB_ERROR;
1573   }
1574 
1575   XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first.");
1576 
1577   if (has_input(stm)) {
1578     LOG("Setup capture: device=%x", (int)stm->input_device);
1579     rv = setup_wasapi_stream_one_side(stm,
1580                                       &stm->input_stream_params,
1581                                       stm->input_device,
1582                                       eCapture,
1583                                       __uuidof(IAudioCaptureClient),
1584                                       &stm->input_client,
1585                                       &stm->input_buffer_frame_count,
1586                                       stm->input_available_event,
1587                                       &stm->capture_client,
1588                                       &stm->input_mix_params);
1589     if (rv != CUBEB_OK) {
1590       LOG("Failure to open the input side.");
1591       return rv;
1592     }
1593   }
1594 
1595   if (has_output(stm)) {
1596     LOG("Setup render: device=%x", (int)stm->output_device);
1597     rv = setup_wasapi_stream_one_side(stm,
1598                                       &stm->output_stream_params,
1599                                       stm->output_device,
1600                                       eRender,
1601                                       __uuidof(IAudioRenderClient),
1602                                       &stm->output_client,
1603                                       &stm->output_buffer_frame_count,
1604                                       stm->refill_event,
1605                                       &stm->render_client,
1606                                       &stm->output_mix_params);
1607     if (rv != CUBEB_OK) {
1608       LOG("Failure to open the output side.");
1609       return rv;
1610     }
1611 
1612     hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume),
1613                                         (void **)&stm->audio_stream_volume);
1614     if (FAILED(hr)) {
1615       LOG("Could not get the IAudioStreamVolume: %x", hr);
1616       return CUBEB_ERROR;
1617     }
1618 
1619     XASSERT(stm->frames_written == 0);
1620     hr = stm->output_client->GetService(__uuidof(IAudioClock),
1621                                         (void **)&stm->audio_clock);
1622     if (FAILED(hr)) {
1623       LOG("Could not get the IAudioClock: %x", hr);
1624       return CUBEB_ERROR;
1625     }
1626 
1627     /* Restore the stream volume over a device change. */
1628     if (stream_set_volume(stm, stm->volume) != CUBEB_OK) {
1629       LOG("Could not set the volume.");
1630       return CUBEB_ERROR;
1631     }
1632   }
1633 
1634   /* If we have both input and output, we resample to
1635    * the highest sample rate available. */
1636   int32_t target_sample_rate;
1637   if (has_input(stm) && has_output(stm)) {
1638     assert(stm->input_stream_params.rate == stm->output_stream_params.rate);
1639     target_sample_rate = stm->input_stream_params.rate;
1640   } else if (has_input(stm)) {
1641     target_sample_rate = stm->input_stream_params.rate;
1642   } else {
1643     XASSERT(has_output(stm));
1644     target_sample_rate = stm->output_stream_params.rate;
1645   }
1646 
1647   LOG("Target sample rate: %d", target_sample_rate);
1648 
1649   /* If we are playing/capturing a mono stream, we only resample one channel,
1650    and copy it over, so we are always resampling the number
1651    of channels of the stream, not the number of channels
1652    that WASAPI wants. */
1653   cubeb_stream_params input_params = stm->input_mix_params;
1654   input_params.channels = stm->input_stream_params.channels;
1655   cubeb_stream_params output_params = stm->output_mix_params;
1656   output_params.channels = stm->output_stream_params.channels;
1657 
1658   stm->resampler =
1659     cubeb_resampler_create(stm,
1660                            has_input(stm) ? &input_params : nullptr,
1661                            has_output(stm) ? &output_params : nullptr,
1662                            target_sample_rate,
1663                            stm->data_callback,
1664                            stm->user_ptr,
1665                            CUBEB_RESAMPLER_QUALITY_DESKTOP);
1666   if (!stm->resampler) {
1667     LOG("Could not get a resampler");
1668     return CUBEB_ERROR;
1669   }
1670 
1671   XASSERT(has_input(stm) || has_output(stm));
1672 
1673   if (has_input(stm) && has_output(stm)) {
1674     stm->refill_callback = refill_callback_duplex;
1675   } else if (has_input(stm)) {
1676     stm->refill_callback = refill_callback_input;
1677   } else if (has_output(stm)) {
1678     stm->refill_callback = refill_callback_output;
1679   }
1680 
1681   return CUBEB_OK;
1682 }
1683 
1684 int
wasapi_stream_init(cubeb * context,cubeb_stream ** stream,char const * stream_name,cubeb_devid input_device,cubeb_stream_params * input_stream_params,cubeb_devid output_device,cubeb_stream_params * output_stream_params,unsigned int latency_frames,cubeb_data_callback data_callback,cubeb_state_callback state_callback,void * user_ptr)1685 wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
1686                    char const * stream_name,
1687                    cubeb_devid input_device,
1688                    cubeb_stream_params * input_stream_params,
1689                    cubeb_devid output_device,
1690                    cubeb_stream_params * output_stream_params,
1691                    unsigned int latency_frames, cubeb_data_callback data_callback,
1692                    cubeb_state_callback state_callback, void * user_ptr)
1693 {
1694   HRESULT hr;
1695   int rv;
1696   auto_com com;
1697   if (!com.ok()) {
1698     return CUBEB_ERROR;
1699   }
1700 
1701   XASSERT(context && stream && (input_stream_params || output_stream_params));
1702 
1703   if (output_stream_params && output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE ||
1704       input_stream_params && input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE) {
1705     LOG("Invalid format, %p %p %d %d",
1706         output_stream_params, input_stream_params,
1707         output_stream_params && output_stream_params->format,
1708         input_stream_params && input_stream_params->format);
1709     return CUBEB_ERROR_INVALID_FORMAT;
1710   }
1711 
1712   cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream));
1713 
1714   XASSERT(stm);
1715 
1716   stm->context = context;
1717   stm->data_callback = data_callback;
1718   stm->state_callback = state_callback;
1719   stm->user_ptr = user_ptr;
1720   stm->draining = false;
1721   if (input_stream_params) {
1722     stm->input_stream_params = *input_stream_params;
1723     stm->input_device = input_device;
1724   }
1725   if (output_stream_params) {
1726     stm->output_stream_params = *output_stream_params;
1727     stm->output_device = output_device;
1728   }
1729 
1730   stm->latency = latency_frames;
1731   stm->volume = 1.0;
1732 
1733   // Placement new to call ctor.
1734   new (&stm->stream_reset_lock) owned_critical_section();
1735 
1736   stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
1737   if (!stm->reconfigure_event) {
1738     LOG("Can't create the reconfigure event, error: %x", GetLastError());
1739     wasapi_stream_destroy(stm);
1740     return CUBEB_ERROR;
1741   }
1742 
1743   /* Unconditionally create the two events so that the wait logic is simpler. */
1744   stm->refill_event = CreateEvent(NULL, 0, 0, NULL);
1745   if (!stm->refill_event) {
1746     LOG("Can't create the refill event, error: %x", GetLastError());
1747     wasapi_stream_destroy(stm);
1748     return CUBEB_ERROR;
1749   }
1750 
1751   stm->input_available_event = CreateEvent(NULL, 0, 0, NULL);
1752   if (!stm->input_available_event) {
1753     LOG("Can't create the input available event , error: %x", GetLastError());
1754     wasapi_stream_destroy(stm);
1755     return CUBEB_ERROR;
1756   }
1757 
1758 
1759   {
1760     /* Locking here is not strictly necessary, because we don't have a
1761        notification client that can reset the stream yet, but it lets us
1762        assert that the lock is held in the function. */
1763     auto_lock lock(stm->stream_reset_lock);
1764     rv = setup_wasapi_stream(stm);
1765   }
1766   if (rv != CUBEB_OK) {
1767     wasapi_stream_destroy(stm);
1768     return rv;
1769   }
1770 
1771   hr = register_notification_client(stm);
1772   if (FAILED(hr)) {
1773     /* this is not fatal, we can still play audio, but we won't be able
1774        to keep using the default audio endpoint if it changes. */
1775     LOG("failed to register notification client, %x", hr);
1776   }
1777 
1778   *stream = stm;
1779 
1780   return CUBEB_OK;
1781 }
1782 
close_wasapi_stream(cubeb_stream * stm)1783 void close_wasapi_stream(cubeb_stream * stm)
1784 {
1785   XASSERT(stm);
1786 
1787   stm->stream_reset_lock.assert_current_thread_owns();
1788 
1789   SafeRelease(stm->output_client);
1790   stm->output_client = NULL;
1791   SafeRelease(stm->input_client);
1792   stm->input_client = NULL;
1793 
1794   SafeRelease(stm->render_client);
1795   stm->render_client = NULL;
1796 
1797   SafeRelease(stm->capture_client);
1798   stm->capture_client = NULL;
1799 
1800   SafeRelease(stm->audio_stream_volume);
1801   stm->audio_stream_volume = NULL;
1802 
1803   SafeRelease(stm->audio_clock);
1804   stm->audio_clock = NULL;
1805   stm->total_frames_written += static_cast<UINT64>(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params)));
1806   stm->frames_written = 0;
1807 
1808   if (stm->resampler) {
1809     cubeb_resampler_destroy(stm->resampler);
1810     stm->resampler = NULL;
1811   }
1812 
1813   free(stm->mix_buffer);
1814   stm->mix_buffer = NULL;
1815 }
1816 
wasapi_stream_destroy(cubeb_stream * stm)1817 void wasapi_stream_destroy(cubeb_stream * stm)
1818 {
1819   XASSERT(stm);
1820 
1821   // Only free stm->emergency_bailout if we could not join the thread.
1822   // If we could not join the thread, stm->emergency_bailout is true
1823   // and is still alive until the thread wakes up and exits cleanly.
1824   if (stop_and_join_render_thread(stm)) {
1825     delete stm->emergency_bailout.load();
1826     stm->emergency_bailout = nullptr;
1827   }
1828 
1829   unregister_notification_client(stm);
1830 
1831   SafeRelease(stm->reconfigure_event);
1832   SafeRelease(stm->refill_event);
1833   SafeRelease(stm->input_available_event);
1834 
1835   {
1836     auto_lock lock(stm->stream_reset_lock);
1837     close_wasapi_stream(stm);
1838   }
1839 
1840   // Need to call dtor to free the resource in owned_critical_section.
1841   stm->stream_reset_lock.~owned_critical_section();
1842 
1843   free(stm);
1844 }
1845 
1846 enum StreamDirection {
1847   OUTPUT,
1848   INPUT
1849 };
1850 
stream_start_one_side(cubeb_stream * stm,StreamDirection dir)1851 int stream_start_one_side(cubeb_stream * stm, StreamDirection dir)
1852 {
1853   XASSERT((dir == OUTPUT && stm->output_client) ||
1854           (dir == INPUT && stm->input_client));
1855 
1856   HRESULT hr = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
1857   if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
1858     LOG("audioclient invalidated for %s device, reconfiguring",
1859         dir == OUTPUT ? "output" : "input");
1860 
1861     BOOL ok = ResetEvent(stm->reconfigure_event);
1862     if (!ok) {
1863       LOG("resetting reconfig event failed for %s stream: %x",
1864           dir == OUTPUT ? "output" : "input", GetLastError());
1865     }
1866 
1867     close_wasapi_stream(stm);
1868     int r = setup_wasapi_stream(stm);
1869     if (r != CUBEB_OK) {
1870       LOG("reconfigure failed");
1871       return r;
1872     }
1873 
1874     HRESULT hr2 = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
1875     if (FAILED(hr2)) {
1876       LOG("could not start the %s stream after reconfig: %x",
1877           dir == OUTPUT ? "output" : "input", hr);
1878       return CUBEB_ERROR;
1879     }
1880   } else if (FAILED(hr)) {
1881     LOG("could not start the %s stream: %x.",
1882         dir == OUTPUT ? "output" : "input", hr);
1883     return CUBEB_ERROR;
1884   }
1885 
1886   return CUBEB_OK;
1887 }
1888 
wasapi_stream_start(cubeb_stream * stm)1889 int wasapi_stream_start(cubeb_stream * stm)
1890 {
1891   auto_lock lock(stm->stream_reset_lock);
1892 
1893   XASSERT(stm && !stm->thread && !stm->shutdown_event);
1894   XASSERT(stm->output_client || stm->input_client);
1895 
1896   stm->emergency_bailout = new std::atomic<bool>(false);
1897 
1898   if (stm->output_client) {
1899     int rv = stream_start_one_side(stm, OUTPUT);
1900     if (rv != CUBEB_OK) {
1901       return rv;
1902     }
1903   }
1904 
1905   if (stm->input_client) {
1906     int rv = stream_start_one_side(stm, INPUT);
1907     if (rv != CUBEB_OK) {
1908       return rv;
1909     }
1910   }
1911 
1912   stm->shutdown_event = CreateEvent(NULL, 0, 0, NULL);
1913   if (!stm->shutdown_event) {
1914     LOG("Can't create the shutdown event, error: %x", GetLastError());
1915     return CUBEB_ERROR;
1916   }
1917 
1918   stm->thread = (HANDLE) _beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
1919   if (stm->thread == NULL) {
1920     LOG("could not create WASAPI render thread.");
1921     return CUBEB_ERROR;
1922   }
1923 
1924   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
1925 
1926   return CUBEB_OK;
1927 }
1928 
wasapi_stream_stop(cubeb_stream * stm)1929 int wasapi_stream_stop(cubeb_stream * stm)
1930 {
1931   XASSERT(stm);
1932   HRESULT hr;
1933 
1934   {
1935     auto_lock lock(stm->stream_reset_lock);
1936 
1937     if (stm->output_client) {
1938       hr = stm->output_client->Stop();
1939       if (FAILED(hr)) {
1940         LOG("could not stop AudioClient (output)");
1941         return CUBEB_ERROR;
1942       }
1943     }
1944 
1945     if (stm->input_client) {
1946       hr = stm->input_client->Stop();
1947       if (FAILED(hr)) {
1948         LOG("could not stop AudioClient (input)");
1949         return CUBEB_ERROR;
1950       }
1951     }
1952 
1953 
1954     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
1955   }
1956 
1957   if (stop_and_join_render_thread(stm)) {
1958     // This is null if we've given the pointer to the other thread
1959     if (stm->emergency_bailout.load()) {
1960       delete stm->emergency_bailout.load();
1961       stm->emergency_bailout = nullptr;
1962     }
1963   }
1964 
1965   return CUBEB_OK;
1966 }
1967 
wasapi_stream_get_position(cubeb_stream * stm,uint64_t * position)1968 int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position)
1969 {
1970   XASSERT(stm && position);
1971   auto_lock lock(stm->stream_reset_lock);
1972 
1973   if (!has_output(stm)) {
1974     return CUBEB_ERROR;
1975   }
1976 
1977   /* Calculate how far behind the current stream head the playback cursor is. */
1978   uint64_t stream_delay = static_cast<uint64_t>(current_stream_delay(stm) * stm->output_stream_params.rate);
1979 
1980   /* Calculate the logical stream head in frames at the stream sample rate. */
1981   uint64_t max_pos = stm->total_frames_written +
1982                      static_cast<uint64_t>(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params)));
1983 
1984   *position = max_pos;
1985   if (stream_delay <= *position) {
1986     *position -= stream_delay;
1987   }
1988 
1989   if (*position < stm->prev_position) {
1990     *position = stm->prev_position;
1991   }
1992   stm->prev_position = *position;
1993 
1994   return CUBEB_OK;
1995 }
1996 
wasapi_stream_get_latency(cubeb_stream * stm,uint32_t * latency)1997 int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
1998 {
1999   XASSERT(stm && latency);
2000 
2001   if (!has_output(stm)) {
2002     return CUBEB_ERROR;
2003   }
2004 
2005   auto_lock lock(stm->stream_reset_lock);
2006 
2007   /* The GetStreamLatency method only works if the
2008      AudioClient has been initialized. */
2009   if (!stm->output_client) {
2010     return CUBEB_ERROR;
2011   }
2012 
2013   REFERENCE_TIME latency_hns;
2014   HRESULT hr = stm->output_client->GetStreamLatency(&latency_hns);
2015   if (FAILED(hr)) {
2016     return CUBEB_ERROR;
2017   }
2018   *latency = hns_to_frames(stm, latency_hns);
2019 
2020   return CUBEB_OK;
2021 }
2022 
wasapi_stream_set_volume(cubeb_stream * stm,float volume)2023 int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
2024 {
2025   auto_lock lock(stm->stream_reset_lock);
2026 
2027   if (!has_output(stm)) {
2028     return CUBEB_ERROR;
2029   }
2030 
2031   if (stream_set_volume(stm, volume) != CUBEB_OK) {
2032     return CUBEB_ERROR;
2033   }
2034 
2035   stm->volume = volume;
2036 
2037   return CUBEB_OK;
2038 }
2039 
2040 static char *
wstr_to_utf8(LPCWSTR str)2041 wstr_to_utf8(LPCWSTR str)
2042 {
2043   char * ret = NULL;
2044   int size;
2045 
2046   size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, 0, NULL, NULL);
2047   if (size > 0) {
2048     ret = static_cast<char *>(malloc(size));
2049     ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
2050   }
2051 
2052   return ret;
2053 }
2054 
2055 static std::unique_ptr<const wchar_t[]>
utf8_to_wstr(char * str)2056 utf8_to_wstr(char* str)
2057 {
2058   std::unique_ptr<wchar_t[]> ret;
2059   int size;
2060 
2061   size = ::MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0);
2062   if (size > 0) {
2063     ret.reset(new wchar_t[size]);
2064     ::MultiByteToWideChar(CP_UTF8, 0, str, -1, ret.get(), size);
2065   }
2066 
2067   return std::move(ret);
2068 }
2069 
2070 static IMMDevice *
wasapi_get_device_node(IMMDeviceEnumerator * enumerator,IMMDevice * dev)2071 wasapi_get_device_node(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
2072 {
2073   IMMDevice * ret = NULL;
2074   IDeviceTopology * devtopo = NULL;
2075   IConnector * connector = NULL;
2076 
2077   if (SUCCEEDED(dev->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&devtopo)) &&
2078       SUCCEEDED(devtopo->GetConnector(0, &connector))) {
2079     LPWSTR filterid;
2080     if (SUCCEEDED(connector->GetDeviceIdConnectedTo(&filterid))) {
2081       if (FAILED(enumerator->GetDevice(filterid, &ret)))
2082         ret = NULL;
2083       CoTaskMemFree(filterid);
2084     }
2085   }
2086 
2087   SafeRelease(connector);
2088   SafeRelease(devtopo);
2089   return ret;
2090 }
2091 
2092 static BOOL
wasapi_is_default_device(EDataFlow flow,ERole role,LPCWSTR device_id,IMMDeviceEnumerator * enumerator)2093 wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id,
2094     IMMDeviceEnumerator * enumerator)
2095 {
2096   BOOL ret = FALSE;
2097   IMMDevice * dev;
2098   HRESULT hr;
2099 
2100   hr = enumerator->GetDefaultAudioEndpoint(flow, role, &dev);
2101   if (SUCCEEDED(hr)) {
2102     LPWSTR defdevid = NULL;
2103     if (SUCCEEDED(dev->GetId(&defdevid)))
2104       ret = (wcscmp(defdevid, device_id) == 0);
2105     if (defdevid != NULL)
2106       CoTaskMemFree(defdevid);
2107     SafeRelease(dev);
2108   }
2109 
2110   return ret;
2111 }
2112 
2113 static cubeb_device_info *
wasapi_create_device(IMMDeviceEnumerator * enumerator,IMMDevice * dev)2114 wasapi_create_device(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
2115 {
2116   IMMEndpoint * endpoint = NULL;
2117   IMMDevice * devnode = NULL;
2118   IAudioClient * client = NULL;
2119   cubeb_device_info * ret = NULL;
2120   EDataFlow flow;
2121   LPWSTR device_id = NULL;
2122   DWORD state = DEVICE_STATE_NOTPRESENT;
2123   IPropertyStore * propstore = NULL;
2124   PROPVARIANT propvar;
2125   REFERENCE_TIME def_period, min_period;
2126   HRESULT hr;
2127 
2128   PropVariantInit(&propvar);
2129 
2130   hr = dev->QueryInterface(IID_PPV_ARGS(&endpoint));
2131   if (FAILED(hr)) goto done;
2132 
2133   hr = endpoint->GetDataFlow(&flow);
2134   if (FAILED(hr)) goto done;
2135 
2136   hr = dev->GetId(&device_id);
2137   if (FAILED(hr)) goto done;
2138 
2139   hr = dev->OpenPropertyStore(STGM_READ, &propstore);
2140   if (FAILED(hr)) goto done;
2141 
2142   hr = dev->GetState(&state);
2143   if (FAILED(hr)) goto done;
2144 
2145   ret = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
2146 
2147   ret->devid = ret->device_id = wstr_to_utf8(device_id);
2148   hr = propstore->GetValue(PKEY_Device_FriendlyName, &propvar);
2149   if (SUCCEEDED(hr))
2150     ret->friendly_name = wstr_to_utf8(propvar.pwszVal);
2151 
2152   devnode = wasapi_get_device_node(enumerator, dev);
2153   if (devnode != NULL) {
2154     IPropertyStore * ps = NULL;
2155     hr = devnode->OpenPropertyStore(STGM_READ, &ps);
2156     if (FAILED(hr)) goto done;
2157 
2158     PropVariantClear(&propvar);
2159     hr = ps->GetValue(PKEY_Device_InstanceId, &propvar);
2160     if (SUCCEEDED(hr)) {
2161       ret->group_id = wstr_to_utf8(propvar.pwszVal);
2162     }
2163     SafeRelease(ps);
2164   }
2165 
2166   ret->preferred = CUBEB_DEVICE_PREF_NONE;
2167   if (wasapi_is_default_device(flow, eConsole, device_id, enumerator))
2168     ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_MULTIMEDIA);
2169   if (wasapi_is_default_device(flow, eCommunications, device_id, enumerator))
2170     ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_VOICE);
2171   if (wasapi_is_default_device(flow, eConsole, device_id, enumerator))
2172     ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_NOTIFICATION);
2173 
2174   if (flow == eRender) ret->type = CUBEB_DEVICE_TYPE_OUTPUT;
2175   else if (flow == eCapture) ret->type = CUBEB_DEVICE_TYPE_INPUT;
2176   switch (state) {
2177     case DEVICE_STATE_ACTIVE:
2178       ret->state = CUBEB_DEVICE_STATE_ENABLED;
2179       break;
2180     case DEVICE_STATE_UNPLUGGED:
2181       ret->state = CUBEB_DEVICE_STATE_UNPLUGGED;
2182       break;
2183     default:
2184       ret->state = CUBEB_DEVICE_STATE_DISABLED;
2185       break;
2186   };
2187 
2188   ret->format = CUBEB_DEVICE_FMT_F32NE; /* cubeb only supports 32bit float at the moment */
2189   ret->default_format = CUBEB_DEVICE_FMT_F32NE;
2190   PropVariantClear(&propvar);
2191   hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &propvar);
2192   if (SUCCEEDED(hr) && propvar.vt == VT_BLOB) {
2193     if (propvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
2194       const PCMWAVEFORMAT * pcm = reinterpret_cast<const PCMWAVEFORMAT *>(propvar.blob.pBlobData);
2195 
2196       ret->max_rate = ret->min_rate = ret->default_rate = pcm->wf.nSamplesPerSec;
2197       ret->max_channels = pcm->wf.nChannels;
2198     } else if (propvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
2199       WAVEFORMATEX* wfx = reinterpret_cast<WAVEFORMATEX*>(propvar.blob.pBlobData);
2200 
2201       if (propvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize ||
2202           wfx->wFormatTag == WAVE_FORMAT_PCM) {
2203         ret->max_rate = ret->min_rate = ret->default_rate = wfx->nSamplesPerSec;
2204         ret->max_channels = wfx->nChannels;
2205       }
2206     }
2207   }
2208 
2209   if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&client)) &&
2210       SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
2211     ret->latency_lo = hns_to_frames(ret->default_rate, min_period);
2212     ret->latency_hi = hns_to_frames(ret->default_rate, def_period);
2213   } else {
2214     ret->latency_lo = 0;
2215     ret->latency_hi = 0;
2216   }
2217   SafeRelease(client);
2218 
2219 done:
2220   SafeRelease(devnode);
2221   SafeRelease(endpoint);
2222   SafeRelease(propstore);
2223   if (device_id != NULL)
2224     CoTaskMemFree(device_id);
2225   PropVariantClear(&propvar);
2226   return ret;
2227 }
2228 
2229 static int
wasapi_enumerate_devices(cubeb * context,cubeb_device_type type,cubeb_device_collection ** out)2230 wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
2231                          cubeb_device_collection ** out)
2232 {
2233   auto_com com;
2234   IMMDeviceEnumerator * enumerator;
2235   IMMDeviceCollection * collection;
2236   IMMDevice * dev;
2237   cubeb_device_info * cur;
2238   HRESULT hr;
2239   UINT cc, i;
2240   EDataFlow flow;
2241 
2242   *out = NULL;
2243 
2244   if (!com.ok())
2245     return CUBEB_ERROR;
2246 
2247   hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
2248       CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator));
2249   if (FAILED(hr)) {
2250     LOG("Could not get device enumerator: %x", hr);
2251     return CUBEB_ERROR;
2252   }
2253 
2254   if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender;
2255   else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture;
2256   else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_INPUT)) flow = eAll;
2257   else return CUBEB_ERROR;
2258 
2259   hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, &collection);
2260   if (FAILED(hr)) {
2261     LOG("Could not enumerate audio endpoints: %x", hr);
2262     return CUBEB_ERROR;
2263   }
2264 
2265   hr = collection->GetCount(&cc);
2266   if (FAILED(hr)) {
2267     LOG("IMMDeviceCollection::GetCount() failed: %x", hr);
2268     return CUBEB_ERROR;
2269   }
2270   *out = (cubeb_device_collection *) malloc(sizeof(cubeb_device_collection) +
2271       sizeof(cubeb_device_info*) * (cc > 0 ? cc - 1 : 0));
2272   if (!*out) {
2273     return CUBEB_ERROR;
2274   }
2275   (*out)->count = 0;
2276   for (i = 0; i < cc; i++) {
2277     hr = collection->Item(i, &dev);
2278     if (FAILED(hr)) {
2279       LOG("IMMDeviceCollection::Item(%u) failed: %x", i-1, hr);
2280     } else if ((cur = wasapi_create_device(enumerator, dev)) != NULL) {
2281       (*out)->device[(*out)->count++] = cur;
2282     }
2283   }
2284 
2285   SafeRelease(collection);
2286   SafeRelease(enumerator);
2287   return CUBEB_OK;
2288 }
2289 
2290 cubeb_ops const wasapi_ops = {
2291   /*.init =*/ wasapi_init,
2292   /*.get_backend_id =*/ wasapi_get_backend_id,
2293   /*.get_max_channel_count =*/ wasapi_get_max_channel_count,
2294   /*.get_min_latency =*/ wasapi_get_min_latency,
2295   /*.get_preferred_sample_rate =*/ wasapi_get_preferred_sample_rate,
2296   /*.enumerate_devices =*/ wasapi_enumerate_devices,
2297   /*.destroy =*/ wasapi_destroy,
2298   /*.stream_init =*/ wasapi_stream_init,
2299   /*.stream_destroy =*/ wasapi_stream_destroy,
2300   /*.stream_start =*/ wasapi_stream_start,
2301   /*.stream_stop =*/ wasapi_stream_stop,
2302   /*.stream_get_position =*/ wasapi_stream_get_position,
2303   /*.stream_get_latency =*/ wasapi_stream_get_latency,
2304   /*.stream_set_volume =*/ wasapi_stream_set_volume,
2305   /*.stream_set_panning =*/ NULL,
2306   /*.stream_get_current_device =*/ NULL,
2307   /*.stream_device_destroy =*/ NULL,
2308   /*.stream_register_device_changed_callback =*/ NULL,
2309   /*.register_device_collection_changed =*/ NULL
2310 };
2311 } // namespace anonymous
2312