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