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