bb9d38c654c11f80ae7f89f640ccdf6ca1e17041
[platform/upstream/gst-plugins-good.git] / sys / directsound / gstdirectsoundsink.c
1 /* GStreamer
2 * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
3 * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
4 *
5 * gstdirectsoundsink.c:
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 *
23 * The development of this code was made possible due to the involvement
24 * of Pioneers of the Inevitable, the creators of the Songbird Music player
25 *
26 */
27
28 /**
29  * SECTION:element-directsoundsink
30  * @short_description: output sound using Directsound API
31  *
32  * <refsect2>
33  * <para>
34  * This element lets you output sound using the DirectSound API.
35  * </para>
36  * <para>
37  * Note that you should almost always use generic audio conversion elements
38  * like audioconvert and audioresample in front of an audiosink to make sure
39  * your pipeline works under all circumstances (those conversion elements will
40  * act in passthrough-mode if no conversion is necessary).
41  * </para>
42  * <title>Example pipelines</title>
43  * <para>
44  * <programlisting>
45  * gst-launch-0.10 -v audiotestsrc ! audioconvert ! volume volume=0.1 ! directsoundsink
46  * </programlisting>
47  * will output a sine wave (continuous beep sound) to your sound card (with
48  * a very low volume as precaution).
49  * </para>
50  * <para>
51  * <programlisting>
52  * gst-launch-0.10 -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! directsoundsink
53  * </programlisting>
54  * will play an Ogg/Vorbis audio file and output it.
55  * </para>
56  * </refsect2>
57  */
58
59 #ifdef HAVE_CONFIG_H
60 #include "config.h"
61 #endif
62
63 #include "gstdirectsoundsink.h"
64
65 #include <math.h>
66
67 GST_DEBUG_CATEGORY_STATIC (directsoundsink_debug);
68
69 /* elementfactory information */
70 static const GstElementDetails gst_directsound_sink_details =
71 GST_ELEMENT_DETAILS ("Direct Sound Audio Sink",
72     "Sink/Audio",
73     "Output to a sound card via Direct Sound",
74     "Sebastien Moutte <sebastien@moutte.net>");
75
76 static void gst_directsound_sink_base_init (gpointer g_class);
77 static void gst_directsound_sink_class_init (GstDirectSoundSinkClass * klass);
78 static void gst_directsound_sink_init (GstDirectSoundSink * dsoundsink,
79     GstDirectSoundSinkClass * g_class);
80 static void gst_directsound_sink_finalise (GObject * object);
81
82 static void gst_directsound_sink_set_property (GObject * object, guint prop_id,
83     const GValue * value, GParamSpec * pspec);
84 static void gst_directsound_sink_get_property (GObject * object, guint prop_id,
85     GValue * value, GParamSpec * pspec);
86
87 static GstCaps *gst_directsound_sink_getcaps (GstBaseSink * bsink);
88 static gboolean gst_directsound_sink_prepare (GstAudioSink * asink,
89     GstRingBufferSpec * spec);
90 static gboolean gst_directsound_sink_unprepare (GstAudioSink * asink);
91
92 static gboolean gst_directsound_sink_open (GstAudioSink * asink);
93 static gboolean gst_directsound_sink_close (GstAudioSink * asink);
94 static guint gst_directsound_sink_write (GstAudioSink * asink, gpointer data,
95     guint length);
96 static guint gst_directsound_sink_delay (GstAudioSink * asink);
97 static void gst_directsound_sink_reset (GstAudioSink * asink);
98
99 /* interfaces */
100 static void gst_directsound_sink_interfaces_init (GType type);
101 static void
102 gst_directsound_sink_implements_interface_init (GstImplementsInterfaceClass *
103     iface);
104 static void gst_directsound_sink_mixer_interface_init (GstMixerClass * iface);
105
106 static GstStaticPadTemplate directsoundsink_sink_factory =
107     GST_STATIC_PAD_TEMPLATE ("sink",
108     GST_PAD_SINK,
109     GST_PAD_ALWAYS,
110     GST_STATIC_CAPS ("audio/x-raw-int, "
111         "signed = (boolean) { TRUE, FALSE }, "
112         "width = (int) 16, "
113         "depth = (int) 16, "
114         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
115         "audio/x-raw-int, "
116         "signed = (boolean) { TRUE, FALSE }, "
117         "width = (int) 8, "
118         "depth = (int) 8, "
119         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]"));
120
121 enum
122 {
123   PROP_0,
124   PROP_VOLUME
125 };
126
127 GST_BOILERPLATE_FULL (GstDirectSoundSink, gst_directsound_sink, GstAudioSink,
128     GST_TYPE_AUDIO_SINK, gst_directsound_sink_interfaces_init);
129
130 /* interfaces stuff */
131 static void
132 gst_directsound_sink_interfaces_init (GType type)
133 {
134   static const GInterfaceInfo implements_interface_info = {
135     (GInterfaceInitFunc) gst_directsound_sink_implements_interface_init,
136     NULL,
137     NULL,
138   };
139
140   static const GInterfaceInfo mixer_interface_info = {
141     (GInterfaceInitFunc) gst_directsound_sink_mixer_interface_init,
142     NULL,
143     NULL,
144   };
145
146   g_type_add_interface_static (type,
147       GST_TYPE_IMPLEMENTS_INTERFACE, &implements_interface_info);
148   g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_interface_info);
149 }
150
151 static gboolean
152 gst_directsound_sink_interface_supported (GstImplementsInterface * iface,
153     GType iface_type)
154 {
155   g_return_val_if_fail (iface_type == GST_TYPE_MIXER, FALSE);
156
157   /* for the sake of this example, we'll always support it. However, normally,
158    * you would check whether the device you've opened supports mixers. */
159   return TRUE;
160 }
161
162 static void
163 gst_directsound_sink_implements_interface_init (GstImplementsInterfaceClass *
164     iface)
165 {
166   iface->supported = gst_directsound_sink_interface_supported;
167 }
168
169 /*
170  * This function returns the list of support tracks (inputs, outputs)
171  * on this element instance. Elements usually build this list during
172  * _init () or when going from NULL to READY.
173  */
174
175 static const GList *
176 gst_directsound_sink_mixer_list_tracks (GstMixer * mixer)
177 {
178   GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (mixer);
179
180   return dsoundsink->tracks;
181 }
182
183 static void
184 gst_directsound_sink_set_volume (GstDirectSoundSink * dsoundsink)
185 {
186   if (dsoundsink->pDSBSecondary) {
187     /* DirectSound controls volume using units of 100th of a decibel,
188      * ranging from -10000 to 0. We use a linear scale of 0 - 100
189      * here, so remap.
190      */
191     long dsVolume;
192     if (dsoundsink->volume == 0)
193       dsVolume = -10000;
194     else
195       dsVolume = 100 * (long) (20 * log10 ((double) dsoundsink->volume / 100.));
196     dsVolume = CLAMP (dsVolume, -10000, 0);
197
198     GST_DEBUG_OBJECT (dsoundsink,
199         "Setting volume on secondary buffer to %d from %d", (int) dsVolume,
200         (int) dsoundsink->volume);
201     IDirectSoundBuffer_SetVolume (dsoundsink->pDSBSecondary, dsVolume);
202   }
203 }
204
205 /*
206  * Set volume. volumes is an array of size track->num_channels, and
207  * each value in the array gives the wanted volume for one channel
208  * on the track.
209  */
210
211 static void
212 gst_directsound_sink_mixer_set_volume (GstMixer * mixer,
213     GstMixerTrack * track, gint * volumes)
214 {
215   GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (mixer);
216
217   if (volumes[0] != dsoundsink->volume) {
218     dsoundsink->volume = volumes[0];
219
220     gst_directsound_sink_set_volume (dsoundsink);
221   }
222 }
223
224 static void
225 gst_directsound_sink_mixer_get_volume (GstMixer * mixer,
226     GstMixerTrack * track, gint * volumes)
227 {
228   GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (mixer);
229
230   volumes[0] = dsoundsink->volume;
231 }
232
233 static void
234 gst_directsound_sink_mixer_interface_init (GstMixerClass * iface)
235 {
236   /* the mixer interface requires a definition of the mixer type:
237    * hardware or software? */
238   GST_MIXER_TYPE (iface) = GST_MIXER_SOFTWARE;
239
240   /* virtual function pointers */
241   iface->list_tracks = gst_directsound_sink_mixer_list_tracks;
242   iface->set_volume = gst_directsound_sink_mixer_set_volume;
243   iface->get_volume = gst_directsound_sink_mixer_get_volume;
244 }
245
246 static void
247 gst_directsound_sink_finalise (GObject * object)
248 {
249   GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (object);
250
251   g_mutex_free (dsoundsink->dsound_lock);
252
253   if (dsoundsink->tracks) {
254     g_list_foreach (dsoundsink->tracks, (GFunc) g_object_unref, NULL);
255     g_list_free (dsoundsink->tracks);
256     dsoundsink->tracks = NULL;
257   }
258
259   G_OBJECT_CLASS (parent_class)->finalize (object);
260 }
261
262 static void
263 gst_directsound_sink_base_init (gpointer g_class)
264 {
265   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
266
267   gst_element_class_set_details (element_class, &gst_directsound_sink_details);
268   gst_element_class_add_pad_template (element_class,
269       gst_static_pad_template_get (&directsoundsink_sink_factory));
270 }
271
272 static void
273 gst_directsound_sink_class_init (GstDirectSoundSinkClass * klass)
274 {
275   GObjectClass *gobject_class;
276   GstElementClass *gstelement_class;
277   GstBaseSinkClass *gstbasesink_class;
278   GstBaseAudioSinkClass *gstbaseaudiosink_class;
279   GstAudioSinkClass *gstaudiosink_class;
280
281   gobject_class = (GObjectClass *) klass;
282   gstelement_class = (GstElementClass *) klass;
283   gstbasesink_class = (GstBaseSinkClass *) klass;
284   gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
285   gstaudiosink_class = (GstAudioSinkClass *) klass;
286
287   GST_DEBUG_CATEGORY_INIT (directsoundsink_debug, "directsoundsink", 0,
288       "DirectSound sink");
289
290   parent_class = g_type_class_peek_parent (klass);
291
292   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_directsound_sink_finalise);
293   gobject_class->set_property =
294       GST_DEBUG_FUNCPTR (gst_directsound_sink_set_property);
295   gobject_class->get_property =
296       GST_DEBUG_FUNCPTR (gst_directsound_sink_get_property);
297
298   gstbasesink_class->get_caps =
299       GST_DEBUG_FUNCPTR (gst_directsound_sink_getcaps);
300
301   gstaudiosink_class->prepare =
302       GST_DEBUG_FUNCPTR (gst_directsound_sink_prepare);
303   gstaudiosink_class->unprepare =
304       GST_DEBUG_FUNCPTR (gst_directsound_sink_unprepare);
305   gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_directsound_sink_open);
306   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_directsound_sink_close);
307   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_directsound_sink_write);
308   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_directsound_sink_delay);
309   gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_directsound_sink_reset);
310
311   g_object_class_install_property (gobject_class,
312       PROP_VOLUME,
313       g_param_spec_double ("volume", "Volume",
314           "Volume of this stream", 0.0, 1.0, 1.0,
315           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
316 }
317
318 static void
319 gst_directsound_sink_init (GstDirectSoundSink * dsoundsink,
320     GstDirectSoundSinkClass * g_class)
321 {
322   GstMixerTrack *track = NULL;
323
324   dsoundsink->tracks = NULL;
325   track = g_object_new (GST_TYPE_MIXER_TRACK, NULL);
326   track->label = g_strdup ("DSoundTrack");
327   track->num_channels = 2;
328   track->min_volume = 0;
329   track->max_volume = 100;
330   track->flags = GST_MIXER_TRACK_OUTPUT;
331   dsoundsink->tracks = g_list_append (dsoundsink->tracks, track);
332
333   dsoundsink->pDS = NULL;
334   dsoundsink->pDSBSecondary = NULL;
335   dsoundsink->current_circular_offset = 0;
336   dsoundsink->buffer_size = DSBSIZE_MIN;
337   dsoundsink->volume = 100;
338   dsoundsink->dsound_lock = g_mutex_new ();
339   dsoundsink->first_buffer_after_reset = FALSE;
340 }
341
342 static void
343 gst_directsound_sink_set_property (GObject * object,
344     guint prop_id, const GValue * value, GParamSpec * pspec)
345 {
346   GstDirectSoundSink *sink = GST_DIRECTSOUND_SINK (object);
347
348   switch (prop_id) {
349     case PROP_VOLUME:
350       sink->volume = (int) (g_value_get_double (value) * 100);
351       gst_directsound_sink_set_volume (sink);
352       break;
353     default:
354       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
355       break;
356   }
357 }
358
359 static void
360 gst_directsound_sink_get_property (GObject * object,
361     guint prop_id, GValue * value, GParamSpec * pspec)
362 {
363   GstDirectSoundSink *sink = GST_DIRECTSOUND_SINK (object);
364
365   switch (prop_id) {
366     case PROP_VOLUME:
367       g_value_set_double (value, (double) sink->volume / 100.);
368       break;
369     default:
370       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371       break;
372   }
373 }
374
375 static GstCaps *
376 gst_directsound_sink_getcaps (GstBaseSink * bsink)
377 {
378   GstDirectSoundSink *dsoundsink;
379
380   dsoundsink = GST_DIRECTSOUND_SINK (bsink);
381
382   return
383       gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
384           (dsoundsink)));
385 }
386
387 static gboolean
388 gst_directsound_sink_open (GstAudioSink * asink)
389 {
390   GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (asink);
391   HRESULT hRes;
392
393   /* create and initialize a DirecSound object */
394   if (FAILED (hRes = DirectSoundCreate (NULL, &dsoundsink->pDS, NULL))) {
395     GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
396         ("gst_directsound_sink_open: DirectSoundCreate: %s",
397             DXGetErrorString9 (hRes)), (NULL));
398     return FALSE;
399   }
400
401   if (FAILED (hRes = IDirectSound_SetCooperativeLevel (dsoundsink->pDS,
402               GetDesktopWindow (), DSSCL_PRIORITY))) {
403     GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
404         ("gst_directsound_sink_open: IDirectSound_SetCooperativeLevel: %s",
405             DXGetErrorString9 (hRes)), (NULL));
406     return FALSE;
407   }
408
409   return TRUE;
410 }
411
412 static gboolean
413 gst_directsound_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
414 {
415   GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (asink);
416   HRESULT hRes;
417   DSBUFFERDESC descSecondary;
418   WAVEFORMATEX wfx;
419
420   /*save number of bytes per sample */
421   dsoundsink->bytes_per_sample = spec->bytes_per_sample;
422
423   /* fill the WAVEFORMATEX struture with spec params */
424   memset (&wfx, 0, sizeof (wfx));
425   wfx.cbSize = sizeof (wfx);
426   wfx.wFormatTag = WAVE_FORMAT_PCM;
427   wfx.nChannels = spec->channels;
428   wfx.nSamplesPerSec = spec->rate;
429   wfx.wBitsPerSample = (spec->bytes_per_sample * 8) / wfx.nChannels;
430   wfx.nBlockAlign = spec->bytes_per_sample;
431   wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
432
433   /* Create directsound buffer with size based on our configured 
434    * buffer_size (which is 200 ms by default) */
435   dsoundsink->buffer_size =
436       gst_util_uint64_scale_int (wfx.nAvgBytesPerSec, spec->buffer_time,
437       GST_MSECOND);
438
439   spec->segsize =
440       gst_util_uint64_scale_int (wfx.nAvgBytesPerSec, spec->latency_time,
441       GST_MSECOND);
442   spec->segtotal = dsoundsink->buffer_size / spec->segsize;
443
444   // Make the final buffer size be an integer number of segments
445   dsoundsink->buffer_size = spec->segsize * spec->segtotal;
446
447   GST_INFO_OBJECT (dsoundsink,
448       "GstRingBufferSpec->channels: %d, GstRingBufferSpec->rate: %d, GstRingBufferSpec->bytes_per_sample: %d\n"
449       "WAVEFORMATEX.nSamplesPerSec: %ld, WAVEFORMATEX.wBitsPerSample: %d, WAVEFORMATEX.nBlockAlign: %d, WAVEFORMATEX.nAvgBytesPerSec: %ld\n"
450       "Size of dsound cirucular buffe=>%d\n", spec->channels, spec->rate,
451       spec->bytes_per_sample, wfx.nSamplesPerSec, wfx.wBitsPerSample,
452       wfx.nBlockAlign, wfx.nAvgBytesPerSec, dsoundsink->buffer_size);
453
454   /* create a secondary directsound buffer */
455   memset (&descSecondary, 0, sizeof (DSBUFFERDESC));
456   descSecondary.dwSize = sizeof (DSBUFFERDESC);
457   descSecondary.dwFlags = DSBCAPS_GETCURRENTPOSITION2 |
458       DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME;
459
460   descSecondary.dwBufferBytes = dsoundsink->buffer_size;
461   descSecondary.lpwfxFormat = (WAVEFORMATEX *) & wfx;
462
463   hRes = IDirectSound_CreateSoundBuffer (dsoundsink->pDS, &descSecondary,
464       &dsoundsink->pDSBSecondary, NULL);
465   if (FAILED (hRes)) {
466     GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
467         ("gst_directsound_sink_prepare: IDirectSound_CreateSoundBuffer: %s",
468             DXGetErrorString9 (hRes)), (NULL));
469     return FALSE;
470   }
471
472   gst_directsound_sink_set_volume (dsoundsink);
473
474   return TRUE;
475 }
476
477 static gboolean
478 gst_directsound_sink_unprepare (GstAudioSink * asink)
479 {
480   GstDirectSoundSink *dsoundsink;
481
482   dsoundsink = GST_DIRECTSOUND_SINK (asink);
483
484   /* release secondary DirectSound buffer */
485   if (dsoundsink->pDSBSecondary)
486     IDirectSoundBuffer_Release (dsoundsink->pDSBSecondary);
487
488   return TRUE;
489 }
490
491 static gboolean
492 gst_directsound_sink_close (GstAudioSink * asink)
493 {
494   GstDirectSoundSink *dsoundsink = NULL;
495
496   dsoundsink = GST_DIRECTSOUND_SINK (asink);
497
498   /* release DirectSound object */
499   g_return_val_if_fail (dsoundsink->pDS != NULL, FALSE);
500   IDirectSound_Release (dsoundsink->pDS);
501
502   return TRUE;
503 }
504
505 static guint
506 gst_directsound_sink_write (GstAudioSink * asink, gpointer data, guint length)
507 {
508   GstDirectSoundSink *dsoundsink;
509   DWORD dwStatus;
510   HRESULT hRes;
511   LPVOID pLockedBuffer1 = NULL, pLockedBuffer2 = NULL;
512   DWORD dwSizeBuffer1, dwSizeBuffer2;
513   DWORD dwCurrentPlayCursor;
514
515   dsoundsink = GST_DIRECTSOUND_SINK (asink);
516
517   GST_DSOUND_LOCK (dsoundsink);
518
519   /* get current buffer status */
520   hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
521
522   /* get current play cursor position */
523   hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
524       &dwCurrentPlayCursor, NULL);
525
526   if (SUCCEEDED (hRes) && (dwStatus & DSBSTATUS_PLAYING)) {
527     DWORD dwFreeBufferSize;
528
529   calculate_freesize:
530     /* calculate the free size of the circular buffer */
531     if (dwCurrentPlayCursor < dsoundsink->current_circular_offset)
532       dwFreeBufferSize =
533           dsoundsink->buffer_size - (dsoundsink->current_circular_offset -
534           dwCurrentPlayCursor);
535     else
536       dwFreeBufferSize =
537           dwCurrentPlayCursor - dsoundsink->current_circular_offset;
538
539     if (length >= dwFreeBufferSize) {
540       Sleep (100);
541       hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
542           &dwCurrentPlayCursor, NULL);
543
544       hRes =
545           IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
546       if (SUCCEEDED (hRes) && (dwStatus & DSBSTATUS_PLAYING))
547         goto calculate_freesize;
548       else {
549         dsoundsink->first_buffer_after_reset = FALSE;
550         GST_DSOUND_UNLOCK (dsoundsink);
551         return 0;
552       }
553     }
554   }
555
556   if (dwStatus & DSBSTATUS_BUFFERLOST) {
557     hRes = IDirectSoundBuffer_Restore (dsoundsink->pDSBSecondary);      /*need a loop waiting the buffer is restored?? */
558
559     dsoundsink->current_circular_offset = 0;
560   }
561
562   hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary,
563       dsoundsink->current_circular_offset, length, &pLockedBuffer1,
564       &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L);
565
566   if (SUCCEEDED (hRes)) {
567     // Write to pointers without reordering.
568     memcpy (pLockedBuffer1, data, dwSizeBuffer1);
569     if (pLockedBuffer2 != NULL)
570       memcpy (pLockedBuffer2, (LPBYTE) data + dwSizeBuffer1, dwSizeBuffer2);
571
572     // Update where the buffer will lock (for next time)
573     dsoundsink->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2;
574     dsoundsink->current_circular_offset %= dsoundsink->buffer_size;     /* Circular buffer */
575
576     hRes = IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer1,
577         dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2);
578   }
579
580   /* if the buffer was not in playing state yet, call play on the buffer 
581      except if this buffer is the fist after a reset (base class call reset and write a buffer when setting the sink to pause) */
582   if (!(dwStatus & DSBSTATUS_PLAYING) &&
583       dsoundsink->first_buffer_after_reset == FALSE) {
584     hRes = IDirectSoundBuffer_Play (dsoundsink->pDSBSecondary, 0, 0,
585         DSBPLAY_LOOPING);
586   }
587
588   dsoundsink->first_buffer_after_reset = FALSE;
589
590   GST_DSOUND_UNLOCK (dsoundsink);
591
592   return length;
593 }
594
595 static guint
596 gst_directsound_sink_delay (GstAudioSink * asink)
597 {
598   GstDirectSoundSink *dsoundsink;
599   HRESULT hRes;
600   DWORD dwCurrentPlayCursor;
601   DWORD dwBytesInQueue = 0;
602   gint nNbSamplesInQueue = 0;
603   DWORD dwStatus;
604
605   dsoundsink = GST_DIRECTSOUND_SINK (asink);
606
607   /* get current buffer status */
608   hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
609
610   if (dwStatus & DSBSTATUS_PLAYING) {
611     /*evaluate the number of samples in queue in the circular buffer */
612     hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
613         &dwCurrentPlayCursor, NULL);
614
615     if (hRes == S_OK) {
616       if (dwCurrentPlayCursor < dsoundsink->current_circular_offset)
617         dwBytesInQueue =
618             dsoundsink->current_circular_offset - dwCurrentPlayCursor;
619       else
620         dwBytesInQueue =
621             dsoundsink->current_circular_offset + (dsoundsink->buffer_size -
622             dwCurrentPlayCursor);
623
624       nNbSamplesInQueue = dwBytesInQueue / dsoundsink->bytes_per_sample;
625     }
626   }
627
628   return nNbSamplesInQueue;
629 }
630
631 static void
632 gst_directsound_sink_reset (GstAudioSink * asink)
633 {
634   GstDirectSoundSink *dsoundsink;
635   LPVOID pLockedBuffer = NULL;
636   DWORD dwSizeBuffer = 0;
637
638   dsoundsink = GST_DIRECTSOUND_SINK (asink);
639
640   GST_DSOUND_LOCK (dsoundsink);
641
642   if (dsoundsink->pDSBSecondary) {
643     /*stop playing */
644     HRESULT hRes = IDirectSoundBuffer_Stop (dsoundsink->pDSBSecondary);
645
646     /*reset position */
647     hRes = IDirectSoundBuffer_SetCurrentPosition (dsoundsink->pDSBSecondary, 0);
648     dsoundsink->current_circular_offset = 0;
649
650     /*reset the buffer */
651     hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary,
652         dsoundsink->current_circular_offset, dsoundsink->buffer_size,
653         &pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L);
654
655     if (SUCCEEDED (hRes)) {
656       memset (pLockedBuffer, 0, dwSizeBuffer);
657
658       hRes =
659           IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer,
660           dwSizeBuffer, NULL, 0);
661     }
662   }
663
664   dsoundsink->first_buffer_after_reset = TRUE;
665
666   GST_DSOUND_UNLOCK (dsoundsink);
667 }