7ef0516f83b8a6998bcdd5a94c07929260cbf778
[platform/upstream/gstreamer.git] / sys / wasapi / gstwasapisink.c
1 /*
2  * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
3  * Copyright (C) 2013 Collabora Ltd.
4  *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  * Copyright (C) 2018 Centricular Ltd.
6  *   Author: Nirbheek Chauhan <nirbheek@centricular.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 /**
25  * SECTION:element-wasapisink
26  * @title: wasapisink
27  *
28  * Provides audio playback using the Windows Audio Session API available with
29  * Vista and newer.
30  *
31  * ## Example pipelines
32  * |[
33  * gst-launch-1.0 -v audiotestsrc samplesperbuffer=160 ! wasapisink
34  * ]| Generate 20 ms buffers and render to the default audio device.
35  *
36  */
37 #ifdef HAVE_CONFIG_H
38 #  include <config.h>
39 #endif
40
41 #include "gstwasapisink.h"
42
43 #include <avrt.h>
44
45 GST_DEBUG_CATEGORY_STATIC (gst_wasapi_sink_debug);
46 #define GST_CAT_DEFAULT gst_wasapi_sink_debug
47
48 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
49     GST_PAD_SINK,
50     GST_PAD_ALWAYS,
51     GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS));
52
53 #define DEFAULT_ROLE          GST_WASAPI_DEVICE_ROLE_CONSOLE
54 #define DEFAULT_MUTE          FALSE
55 #define DEFAULT_EXCLUSIVE     FALSE
56 #define DEFAULT_LOW_LATENCY   FALSE
57
58 enum
59 {
60   PROP_0,
61   PROP_ROLE,
62   PROP_MUTE,
63   PROP_DEVICE,
64   PROP_EXCLUSIVE,
65   PROP_LOW_LATENCY
66 };
67
68 static void gst_wasapi_sink_dispose (GObject * object);
69 static void gst_wasapi_sink_finalize (GObject * object);
70 static void gst_wasapi_sink_set_property (GObject * object, guint prop_id,
71     const GValue * value, GParamSpec * pspec);
72 static void gst_wasapi_sink_get_property (GObject * object, guint prop_id,
73     GValue * value, GParamSpec * pspec);
74
75 static GstCaps *gst_wasapi_sink_get_caps (GstBaseSink * bsink,
76     GstCaps * filter);
77
78 static gboolean gst_wasapi_sink_prepare (GstAudioSink * asink,
79     GstAudioRingBufferSpec * spec);
80 static gboolean gst_wasapi_sink_unprepare (GstAudioSink * asink);
81 static gboolean gst_wasapi_sink_open (GstAudioSink * asink);
82 static gboolean gst_wasapi_sink_close (GstAudioSink * asink);
83 static gint gst_wasapi_sink_write (GstAudioSink * asink,
84     gpointer data, guint length);
85 static guint gst_wasapi_sink_delay (GstAudioSink * asink);
86 static void gst_wasapi_sink_reset (GstAudioSink * asink);
87
88 #define gst_wasapi_sink_parent_class parent_class
89 G_DEFINE_TYPE (GstWasapiSink, gst_wasapi_sink, GST_TYPE_AUDIO_SINK);
90
91 static void
92 gst_wasapi_sink_class_init (GstWasapiSinkClass * klass)
93 {
94   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
95   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
96   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
97   GstAudioSinkClass *gstaudiosink_class = GST_AUDIO_SINK_CLASS (klass);
98
99   gobject_class->dispose = gst_wasapi_sink_dispose;
100   gobject_class->finalize = gst_wasapi_sink_finalize;
101   gobject_class->set_property = gst_wasapi_sink_set_property;
102   gobject_class->get_property = gst_wasapi_sink_get_property;
103
104   g_object_class_install_property (gobject_class,
105       PROP_ROLE,
106       g_param_spec_enum ("role", "Role",
107           "Role of the device: communications, multimedia, etc",
108           GST_WASAPI_DEVICE_TYPE_ROLE, DEFAULT_ROLE, G_PARAM_READWRITE |
109           G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY));
110
111   g_object_class_install_property (gobject_class,
112       PROP_MUTE,
113       g_param_spec_boolean ("mute", "Mute", "Mute state of this stream",
114           DEFAULT_MUTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
115           GST_PARAM_MUTABLE_PLAYING));
116
117   g_object_class_install_property (gobject_class,
118       PROP_DEVICE,
119       g_param_spec_string ("device", "Device",
120           "WASAPI playback device as a GUID string",
121           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
122
123   g_object_class_install_property (gobject_class,
124       PROP_EXCLUSIVE,
125       g_param_spec_boolean ("exclusive", "Exclusive mode",
126           "Open the device in exclusive mode",
127           DEFAULT_EXCLUSIVE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
128
129   g_object_class_install_property (gobject_class,
130       PROP_LOW_LATENCY,
131       g_param_spec_boolean ("low-latency", "Low latency",
132           "Optimize all settings for lowest latency",
133           DEFAULT_LOW_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
134
135   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
136   gst_element_class_set_static_metadata (gstelement_class, "WasapiSrc",
137       "Sink/Audio",
138       "Stream audio to an audio capture device through WASAPI",
139       "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>");
140
141   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wasapi_sink_get_caps);
142
143   gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_wasapi_sink_prepare);
144   gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_wasapi_sink_unprepare);
145   gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_wasapi_sink_open);
146   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_wasapi_sink_close);
147   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_wasapi_sink_write);
148   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_wasapi_sink_delay);
149   gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_wasapi_sink_reset);
150
151   GST_DEBUG_CATEGORY_INIT (gst_wasapi_sink_debug, "wasapisink",
152       0, "Windows audio session API sink");
153 }
154
155 static void
156 gst_wasapi_sink_init (GstWasapiSink * self)
157 {
158   self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
159
160   CoInitialize (NULL);
161 }
162
163 static void
164 gst_wasapi_sink_dispose (GObject * object)
165 {
166   GstWasapiSink *self = GST_WASAPI_SINK (object);
167
168   if (self->event_handle != NULL) {
169     CloseHandle (self->event_handle);
170     self->event_handle = NULL;
171   }
172
173   if (self->client != NULL) {
174     IUnknown_Release (self->client);
175     self->client = NULL;
176   }
177
178   if (self->render_client != NULL) {
179     IUnknown_Release (self->render_client);
180     self->render_client = NULL;
181   }
182
183   G_OBJECT_CLASS (gst_wasapi_sink_parent_class)->dispose (object);
184 }
185
186 static void
187 gst_wasapi_sink_finalize (GObject * object)
188 {
189   GstWasapiSink *self = GST_WASAPI_SINK (object);
190
191   g_clear_pointer (&self->mix_format, CoTaskMemFree);
192
193   CoUninitialize ();
194
195   if (self->cached_caps != NULL) {
196     gst_caps_unref (self->cached_caps);
197     self->cached_caps = NULL;
198   }
199
200   g_clear_pointer (&self->positions, g_free);
201   g_clear_pointer (&self->device_strid, g_free);
202   self->mute = FALSE;
203
204   G_OBJECT_CLASS (gst_wasapi_sink_parent_class)->finalize (object);
205 }
206
207 static void
208 gst_wasapi_sink_set_property (GObject * object, guint prop_id,
209     const GValue * value, GParamSpec * pspec)
210 {
211   GstWasapiSink *self = GST_WASAPI_SINK (object);
212
213   switch (prop_id) {
214     case PROP_ROLE:
215       self->role = gst_wasapi_device_role_to_erole (g_value_get_enum (value));
216       break;
217     case PROP_MUTE:
218       self->mute = g_value_get_boolean (value);
219       break;
220     case PROP_DEVICE:
221     {
222       const gchar *device = g_value_get_string (value);
223       g_free (self->device_strid);
224       self->device_strid =
225           device ? g_utf8_to_utf16 (device, -1, NULL, NULL, NULL) : NULL;
226       break;
227     }
228     case PROP_EXCLUSIVE:
229       self->sharemode = g_value_get_boolean (value)
230           ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED;
231       break;
232     case PROP_LOW_LATENCY:
233       self->low_latency = g_value_get_boolean (value);
234       break;
235     default:
236       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
237       break;
238   }
239 }
240
241 static void
242 gst_wasapi_sink_get_property (GObject * object, guint prop_id,
243     GValue * value, GParamSpec * pspec)
244 {
245   GstWasapiSink *self = GST_WASAPI_SINK (object);
246
247   switch (prop_id) {
248     case PROP_ROLE:
249       g_value_set_enum (value, gst_wasapi_erole_to_device_role (self->role));
250       break;
251     case PROP_MUTE:
252       g_value_set_boolean (value, self->mute);
253       break;
254     case PROP_DEVICE:
255       g_value_take_string (value, self->device_strid ?
256           g_utf16_to_utf8 (self->device_strid, -1, NULL, NULL, NULL) : NULL);
257       break;
258     case PROP_EXCLUSIVE:
259       g_value_set_boolean (value,
260           self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE);
261       break;
262     case PROP_LOW_LATENCY:
263       g_value_set_boolean (value, self->low_latency);
264       break;
265     default:
266       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267       break;
268   }
269 }
270
271 static GstCaps *
272 gst_wasapi_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
273 {
274   GstWasapiSink *self = GST_WASAPI_SINK (bsink);
275   WAVEFORMATEX *format = NULL;
276   GstCaps *caps = NULL;
277
278   GST_DEBUG_OBJECT (self, "entering get caps");
279
280   if (self->cached_caps) {
281     caps = gst_caps_ref (self->cached_caps);
282   } else {
283     GstCaps *template_caps;
284     gboolean ret;
285
286     template_caps = gst_pad_get_pad_template_caps (bsink->sinkpad);
287
288     if (!self->client)
289       gst_wasapi_sink_open (GST_AUDIO_SINK (bsink));
290
291     ret = gst_wasapi_util_get_device_format (GST_ELEMENT (self),
292         self->sharemode, self->device, self->client, &format);
293     if (!ret) {
294       GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL),
295           ("failed to detect format"));
296       goto out;
297     }
298
299     gst_wasapi_util_parse_waveformatex ((WAVEFORMATEXTENSIBLE *) format,
300         template_caps, &caps, &self->positions);
301     if (caps == NULL) {
302       GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("unknown format"));
303       goto out;
304     }
305
306     {
307       gchar *pos_str = gst_audio_channel_positions_to_string (self->positions,
308           format->nChannels);
309       GST_INFO_OBJECT (self, "positions are: %s", pos_str);
310       g_free (pos_str);
311     }
312
313     self->mix_format = format;
314     gst_caps_replace (&self->cached_caps, caps);
315     gst_caps_unref (template_caps);
316   }
317
318   if (filter) {
319     GstCaps *filtered =
320         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
321     gst_caps_unref (caps);
322     caps = filtered;
323   }
324
325   GST_DEBUG_OBJECT (self, "returning caps %" GST_PTR_FORMAT, caps);
326
327 out:
328   return caps;
329 }
330
331 static gboolean
332 gst_wasapi_sink_open (GstAudioSink * asink)
333 {
334   GstWasapiSink *self = GST_WASAPI_SINK (asink);
335   gboolean res = FALSE;
336   IMMDevice *device = NULL;
337   IAudioClient *client = NULL;
338
339   GST_DEBUG_OBJECT (self, "opening device");
340
341   if (self->client)
342     return TRUE;
343
344   /* FIXME: Switching the default device does not switch the stream to it,
345    * even if the old device was unplugged. We need to handle this somehow.
346    * For example, perhaps we should automatically switch to the new device if
347    * the default device is changed and a device isn't explicitly selected. */
348   if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self), FALSE,
349           self->role, self->device_strid, &device, &client)) {
350     if (!self->device_strid)
351       GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE, (NULL),
352           ("Failed to get default device"));
353     else
354       GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE, (NULL),
355           ("Failed to open device %S", self->device_strid));
356     goto beach;
357   }
358
359   self->client = client;
360   self->device = device;
361   res = TRUE;
362
363 beach:
364
365   return res;
366 }
367
368 static gboolean
369 gst_wasapi_sink_close (GstAudioSink * asink)
370 {
371   GstWasapiSink *self = GST_WASAPI_SINK (asink);
372
373   if (self->device != NULL) {
374     IUnknown_Release (self->device);
375     self->device = NULL;
376   }
377
378   if (self->client != NULL) {
379     IUnknown_Release (self->client);
380     self->client = NULL;
381   }
382
383   return TRUE;
384 }
385
386 /* Get the empty space in the buffer that we have to write to */
387 static gint
388 gst_wasapi_sink_get_can_frames (GstWasapiSink * self)
389 {
390   HRESULT hr;
391   guint n_frames_padding;
392
393   /* There is no padding in exclusive mode since there is no ringbuffer */
394   if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
395     GST_DEBUG_OBJECT (self, "exclusive mode, can write: %i",
396         self->buffer_frame_count);
397     return self->buffer_frame_count;
398   }
399
400   /* Frames the card hasn't rendered yet */
401   hr = IAudioClient_GetCurrentPadding (self->client, &n_frames_padding);
402   HR_FAILED_RET (hr, IAudioClient::GetCurrentPadding, -1);
403
404   GST_DEBUG_OBJECT (self, "%i unread frames (padding)", n_frames_padding);
405
406   /* We can write out these many frames */
407   return self->buffer_frame_count - n_frames_padding;
408 }
409
410 static gboolean
411 gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
412 {
413   GstWasapiSink *self = GST_WASAPI_SINK (asink);
414   gboolean res = FALSE;
415   REFERENCE_TIME latency_rt;
416   guint bpf, rate, devicep_frames;
417   HRESULT hr;
418
419   if (self->sharemode == AUDCLNT_SHAREMODE_SHARED &&
420       gst_wasapi_util_have_audioclient3 ()) {
421     if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
422             (IAudioClient3 *) self->client, self->mix_format, self->low_latency,
423             &devicep_frames))
424       goto beach;
425   } else {
426     if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
427             self->client, self->mix_format, self->sharemode, self->low_latency,
428             &devicep_frames))
429       goto beach;
430   }
431
432   bpf = GST_AUDIO_INFO_BPF (&spec->info);
433   rate = GST_AUDIO_INFO_RATE (&spec->info);
434
435   /* Total size of the allocated buffer that we will write to */
436   hr = IAudioClient_GetBufferSize (self->client, &self->buffer_frame_count);
437   HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach);
438
439   GST_INFO_OBJECT (self, "buffer size is %i frames, device period is %i "
440       "frames, bpf is %i bytes, rate is %i Hz", self->buffer_frame_count,
441       devicep_frames, bpf, rate);
442
443   /* Actual latency-time/buffer-time will be different now */
444   spec->segsize = devicep_frames * bpf;
445
446   /* We need a minimum of 2 segments to ensure glitch-free playback */
447   spec->segtotal = MAX (self->buffer_frame_count * bpf / spec->segsize, 2);
448
449   GST_INFO_OBJECT (self, "segsize is %i, segtotal is %i", spec->segsize,
450       spec->segtotal);
451
452   /* Get latency for logging */
453   hr = IAudioClient_GetStreamLatency (self->client, &latency_rt);
454   HR_FAILED_GOTO (hr, IAudioClient::GetStreamLatency, beach);
455
456   GST_INFO_OBJECT (self, "wasapi stream latency: %" G_GINT64_FORMAT " (%"
457       G_GINT64_FORMAT "ms)", latency_rt, latency_rt / 10000);
458
459   /* Set the event handler which will trigger writes */
460   hr = IAudioClient_SetEventHandle (self->client, self->event_handle);
461   HR_FAILED_GOTO (hr, IAudioClient::SetEventHandle, beach);
462
463   /* Get render sink client and start it up */
464   if (!gst_wasapi_util_get_render_client (GST_ELEMENT (self), self->client,
465           &self->render_client)) {
466     goto beach;
467   }
468
469   GST_INFO_OBJECT (self, "got render client");
470
471   /* To avoid start-up glitches, before starting the streaming, we fill the
472    * buffer with silence as recommended by the documentation:
473    * https://msdn.microsoft.com/en-us/library/windows/desktop/dd370879%28v=vs.85%29.aspx */
474   {
475     gint n_frames, len;
476     gint16 *dst = NULL;
477
478     n_frames = gst_wasapi_sink_get_can_frames (self);
479     if (n_frames < 1) {
480       GST_ELEMENT_ERROR (self, RESOURCE, WRITE, (NULL),
481           ("should have more than %i frames to write", n_frames));
482       goto beach;
483     }
484
485     len = n_frames * self->mix_format->nBlockAlign;
486
487     hr = IAudioRenderClient_GetBuffer (self->render_client, n_frames,
488         (BYTE **) & dst);
489     HR_FAILED_GOTO (hr, IAudioRenderClient::GetBuffer, beach);
490
491     GST_DEBUG_OBJECT (self, "pre-wrote %i bytes of silence", len);
492
493     hr = IAudioRenderClient_ReleaseBuffer (self->render_client, n_frames,
494         AUDCLNT_BUFFERFLAGS_SILENT);
495     HR_FAILED_GOTO (hr, IAudioRenderClient::ReleaseBuffer, beach);
496   }
497
498   hr = IAudioClient_Start (self->client);
499   HR_FAILED_GOTO (hr, IAudioClient::Start, beach);
500
501   gst_audio_ring_buffer_set_channel_positions (GST_AUDIO_BASE_SINK
502       (self)->ringbuffer, self->positions);
503
504   /* Increase the thread priority to reduce glitches */
505   self->thread_priority_handle = gst_wasapi_util_set_thread_characteristics ();
506
507   res = TRUE;
508
509 beach:
510   /* unprepare() is not called if prepare() fails, but we want it to be, so call
511    * it manually when needed */
512   if (!res)
513     gst_wasapi_sink_unprepare (asink);
514
515   return res;
516 }
517
518 static gboolean
519 gst_wasapi_sink_unprepare (GstAudioSink * asink)
520 {
521   GstWasapiSink *self = GST_WASAPI_SINK (asink);
522
523   if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
524       !gst_wasapi_util_have_audioclient3 ())
525     CoUninitialize ();
526
527   if (self->thread_priority_handle != NULL) {
528     gst_wasapi_util_revert_thread_characteristics
529         (self->thread_priority_handle);
530     self->thread_priority_handle = NULL;
531   }
532
533   if (self->client != NULL) {
534     IAudioClient_Stop (self->client);
535   }
536
537   if (self->render_client != NULL) {
538     IUnknown_Release (self->render_client);
539     self->render_client = NULL;
540   }
541
542   return TRUE;
543 }
544
545 static gint
546 gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
547 {
548   GstWasapiSink *self = GST_WASAPI_SINK (asink);
549   HRESULT hr;
550   gint16 *dst = NULL;
551   guint pending = length;
552
553   while (pending > 0) {
554     guint can_frames, have_frames, n_frames, write_len;
555
556     WaitForSingleObject (self->event_handle, INFINITE);
557
558     /* We have N frames to be written out */
559     have_frames = pending / (self->mix_format->nBlockAlign);
560     /* We have can_frames space in the output buffer */
561     can_frames = gst_wasapi_sink_get_can_frames (self);
562     /* We will write out these many frames, and this much length */
563     n_frames = MIN (can_frames, have_frames);
564     write_len = n_frames * self->mix_format->nBlockAlign;
565
566     GST_DEBUG_OBJECT (self, "total: %i, have_frames: %i (%i bytes), "
567         "can_frames: %i, will write: %i (%i bytes)", self->buffer_frame_count,
568         have_frames, pending, can_frames, n_frames, write_len);
569
570     hr = IAudioRenderClient_GetBuffer (self->render_client, n_frames,
571         (BYTE **) & dst);
572     HR_FAILED_AND (hr, IAudioRenderClient::GetBuffer, length = 0; goto beach);
573
574     memcpy (dst, data, write_len);
575
576     hr = IAudioRenderClient_ReleaseBuffer (self->render_client, n_frames,
577         self->mute ? AUDCLNT_BUFFERFLAGS_SILENT : 0);
578     HR_FAILED_AND (hr, IAudioRenderClient::ReleaseBuffer, length = 0;
579         goto beach);
580
581     pending -= write_len;
582   }
583
584 beach:
585
586   return length;
587 }
588
589 static guint
590 gst_wasapi_sink_delay (GstAudioSink * asink)
591 {
592   GstWasapiSink *self = GST_WASAPI_SINK (asink);
593   guint delay = 0;
594   HRESULT hr;
595
596   hr = IAudioClient_GetCurrentPadding (self->client, &delay);
597   HR_FAILED_RET (hr, IAudioClient::GetCurrentPadding, 0);
598
599   return delay;
600 }
601
602 static void
603 gst_wasapi_sink_reset (GstAudioSink * asink)
604 {
605   GstWasapiSink *self = GST_WASAPI_SINK (asink);
606   HRESULT hr;
607
608   if (!self->client)
609     return;
610
611   hr = IAudioClient_Stop (self->client);
612   HR_FAILED_RET (hr, IAudioClient::Stop,);
613
614   hr = IAudioClient_Reset (self->client);
615   HR_FAILED_RET (hr, IAudioClient::Reset,);
616 }