2 * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
3 * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
5 * gstdirectsoundsink.c:
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.
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.
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.
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
29 * SECTION:element-directsoundsink
30 * @short_description: output sound using Directsound API
34 * This element lets you output sound using the DirectSound API.
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).
42 * <title>Example pipelines</title>
45 * gst-launch-0.10 -v audiotestsrc ! audioconvert ! volume volume=0.1 ! directsoundsink
47 * will output a sine wave (continuous beep sound) to your sound card (with
48 * a very low volume as precaution).
52 * gst-launch-0.10 -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! directsoundsink
54 * will play an Ogg/Vorbis audio file and output it.
63 #include "gstdirectsoundsink.h"
67 GST_DEBUG_CATEGORY_STATIC (directsoundsink_debug);
69 /* elementfactory information */
70 static const GstElementDetails gst_directsound_sink_details =
71 GST_ELEMENT_DETAILS ("Direct Sound Audio Sink",
73 "Output to a sound card via Direct Sound",
74 "Sebastien Moutte <sebastien@moutte.net>");
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);
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);
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);
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,
96 static guint gst_directsound_sink_delay (GstAudioSink * asink);
97 static void gst_directsound_sink_reset (GstAudioSink * asink);
100 static void gst_directsound_sink_interfaces_init (GType type);
102 gst_directsound_sink_implements_interface_init (GstImplementsInterfaceClass *
104 static void gst_directsound_sink_mixer_interface_init (GstMixerClass * iface);
106 static GstStaticPadTemplate directsoundsink_sink_factory =
107 GST_STATIC_PAD_TEMPLATE ("sink",
110 GST_STATIC_CAPS ("audio/x-raw-int, "
111 "signed = (boolean) { TRUE, FALSE }, "
114 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
116 "signed = (boolean) { TRUE, FALSE }, "
119 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]"));
127 GST_BOILERPLATE_FULL (GstDirectSoundSink, gst_directsound_sink, GstAudioSink,
128 GST_TYPE_AUDIO_SINK, gst_directsound_sink_interfaces_init);
130 /* interfaces stuff */
132 gst_directsound_sink_interfaces_init (GType type)
134 static const GInterfaceInfo implements_interface_info = {
135 (GInterfaceInitFunc) gst_directsound_sink_implements_interface_init,
140 static const GInterfaceInfo mixer_interface_info = {
141 (GInterfaceInitFunc) gst_directsound_sink_mixer_interface_init,
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);
152 gst_directsound_sink_interface_supported (GstImplementsInterface * iface,
155 g_return_val_if_fail (iface_type == GST_TYPE_MIXER, FALSE);
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. */
163 gst_directsound_sink_implements_interface_init (GstImplementsInterfaceClass *
166 iface->supported = gst_directsound_sink_interface_supported;
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.
176 gst_directsound_sink_mixer_list_tracks (GstMixer * mixer)
178 GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (mixer);
180 return dsoundsink->tracks;
184 gst_directsound_sink_set_volume (GstDirectSoundSink * dsoundsink)
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
192 if (dsoundsink->volume == 0)
195 dsVolume = 100 * (long) (20 * log10 ((double) dsoundsink->volume / 100.));
196 dsVolume = CLAMP (dsVolume, -10000, 0);
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);
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
212 gst_directsound_sink_mixer_set_volume (GstMixer * mixer,
213 GstMixerTrack * track, gint * volumes)
215 GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (mixer);
217 if (volumes[0] != dsoundsink->volume) {
218 dsoundsink->volume = volumes[0];
220 gst_directsound_sink_set_volume (dsoundsink);
225 gst_directsound_sink_mixer_get_volume (GstMixer * mixer,
226 GstMixerTrack * track, gint * volumes)
228 GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (mixer);
230 volumes[0] = dsoundsink->volume;
234 gst_directsound_sink_mixer_interface_init (GstMixerClass * iface)
236 /* the mixer interface requires a definition of the mixer type:
237 * hardware or software? */
238 GST_MIXER_TYPE (iface) = GST_MIXER_SOFTWARE;
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;
247 gst_directsound_sink_finalise (GObject * object)
249 GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (object);
251 g_mutex_free (dsoundsink->dsound_lock);
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;
259 G_OBJECT_CLASS (parent_class)->finalize (object);
263 gst_directsound_sink_base_init (gpointer g_class)
265 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
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));
273 gst_directsound_sink_class_init (GstDirectSoundSinkClass * klass)
275 GObjectClass *gobject_class;
276 GstElementClass *gstelement_class;
277 GstBaseSinkClass *gstbasesink_class;
278 GstBaseAudioSinkClass *gstbaseaudiosink_class;
279 GstAudioSinkClass *gstaudiosink_class;
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;
287 GST_DEBUG_CATEGORY_INIT (directsoundsink_debug, "directsoundsink", 0,
290 parent_class = g_type_class_peek_parent (klass);
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);
298 gstbasesink_class->get_caps =
299 GST_DEBUG_FUNCPTR (gst_directsound_sink_getcaps);
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);
311 g_object_class_install_property (gobject_class,
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));
319 gst_directsound_sink_init (GstDirectSoundSink * dsoundsink,
320 GstDirectSoundSinkClass * g_class)
322 GstMixerTrack *track = NULL;
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);
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;
343 gst_directsound_sink_set_property (GObject * object,
344 guint prop_id, const GValue * value, GParamSpec * pspec)
346 GstDirectSoundSink *sink = GST_DIRECTSOUND_SINK (object);
350 sink->volume = (int) (g_value_get_double (value) * 100);
351 gst_directsound_sink_set_volume (sink);
354 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
360 gst_directsound_sink_get_property (GObject * object,
361 guint prop_id, GValue * value, GParamSpec * pspec)
363 GstDirectSoundSink *sink = GST_DIRECTSOUND_SINK (object);
367 g_value_set_double (value, (double) sink->volume / 100.);
370 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
376 gst_directsound_sink_getcaps (GstBaseSink * bsink)
378 GstDirectSoundSink *dsoundsink;
380 dsoundsink = GST_DIRECTSOUND_SINK (bsink);
383 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
388 gst_directsound_sink_open (GstAudioSink * asink)
390 GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (asink);
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));
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));
413 gst_directsound_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
415 GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (asink);
417 DSBUFFERDESC descSecondary;
420 /*save number of bytes per sample */
421 dsoundsink->bytes_per_sample = spec->bytes_per_sample;
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;
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,
440 gst_util_uint64_scale_int (wfx.nAvgBytesPerSec, spec->latency_time,
442 spec->segtotal = dsoundsink->buffer_size / spec->segsize;
444 // Make the final buffer size be an integer number of segments
445 dsoundsink->buffer_size = spec->segsize * spec->segtotal;
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);
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;
460 descSecondary.dwBufferBytes = dsoundsink->buffer_size;
461 descSecondary.lpwfxFormat = (WAVEFORMATEX *) & wfx;
463 hRes = IDirectSound_CreateSoundBuffer (dsoundsink->pDS, &descSecondary,
464 &dsoundsink->pDSBSecondary, NULL);
466 GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
467 ("gst_directsound_sink_prepare: IDirectSound_CreateSoundBuffer: %s",
468 DXGetErrorString9 (hRes)), (NULL));
472 gst_directsound_sink_set_volume (dsoundsink);
478 gst_directsound_sink_unprepare (GstAudioSink * asink)
480 GstDirectSoundSink *dsoundsink;
482 dsoundsink = GST_DIRECTSOUND_SINK (asink);
484 /* release secondary DirectSound buffer */
485 if (dsoundsink->pDSBSecondary)
486 IDirectSoundBuffer_Release (dsoundsink->pDSBSecondary);
492 gst_directsound_sink_close (GstAudioSink * asink)
494 GstDirectSoundSink *dsoundsink = NULL;
496 dsoundsink = GST_DIRECTSOUND_SINK (asink);
498 /* release DirectSound object */
499 g_return_val_if_fail (dsoundsink->pDS != NULL, FALSE);
500 IDirectSound_Release (dsoundsink->pDS);
506 gst_directsound_sink_write (GstAudioSink * asink, gpointer data, guint length)
508 GstDirectSoundSink *dsoundsink;
511 LPVOID pLockedBuffer1 = NULL, pLockedBuffer2 = NULL;
512 DWORD dwSizeBuffer1, dwSizeBuffer2;
513 DWORD dwCurrentPlayCursor;
515 dsoundsink = GST_DIRECTSOUND_SINK (asink);
517 GST_DSOUND_LOCK (dsoundsink);
519 /* get current buffer status */
520 hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
522 /* get current play cursor position */
523 hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
524 &dwCurrentPlayCursor, NULL);
526 if (SUCCEEDED (hRes) && (dwStatus & DSBSTATUS_PLAYING)) {
527 DWORD dwFreeBufferSize;
530 /* calculate the free size of the circular buffer */
531 if (dwCurrentPlayCursor < dsoundsink->current_circular_offset)
533 dsoundsink->buffer_size - (dsoundsink->current_circular_offset -
534 dwCurrentPlayCursor);
537 dwCurrentPlayCursor - dsoundsink->current_circular_offset;
539 if (length >= dwFreeBufferSize) {
541 hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
542 &dwCurrentPlayCursor, NULL);
545 IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
546 if (SUCCEEDED (hRes) && (dwStatus & DSBSTATUS_PLAYING))
547 goto calculate_freesize;
549 dsoundsink->first_buffer_after_reset = FALSE;
550 GST_DSOUND_UNLOCK (dsoundsink);
556 if (dwStatus & DSBSTATUS_BUFFERLOST) {
557 hRes = IDirectSoundBuffer_Restore (dsoundsink->pDSBSecondary); /*need a loop waiting the buffer is restored?? */
559 dsoundsink->current_circular_offset = 0;
562 hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary,
563 dsoundsink->current_circular_offset, length, &pLockedBuffer1,
564 &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L);
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);
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 */
576 hRes = IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer1,
577 dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2);
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,
588 dsoundsink->first_buffer_after_reset = FALSE;
590 GST_DSOUND_UNLOCK (dsoundsink);
596 gst_directsound_sink_delay (GstAudioSink * asink)
598 GstDirectSoundSink *dsoundsink;
600 DWORD dwCurrentPlayCursor;
601 DWORD dwBytesInQueue = 0;
602 gint nNbSamplesInQueue = 0;
605 dsoundsink = GST_DIRECTSOUND_SINK (asink);
607 /* get current buffer status */
608 hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
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);
616 if (dwCurrentPlayCursor < dsoundsink->current_circular_offset)
618 dsoundsink->current_circular_offset - dwCurrentPlayCursor;
621 dsoundsink->current_circular_offset + (dsoundsink->buffer_size -
622 dwCurrentPlayCursor);
624 nNbSamplesInQueue = dwBytesInQueue / dsoundsink->bytes_per_sample;
628 return nNbSamplesInQueue;
632 gst_directsound_sink_reset (GstAudioSink * asink)
634 GstDirectSoundSink *dsoundsink;
635 LPVOID pLockedBuffer = NULL;
636 DWORD dwSizeBuffer = 0;
638 dsoundsink = GST_DIRECTSOUND_SINK (asink);
640 GST_DSOUND_LOCK (dsoundsink);
642 if (dsoundsink->pDSBSecondary) {
644 HRESULT hRes = IDirectSoundBuffer_Stop (dsoundsink->pDSBSecondary);
647 hRes = IDirectSoundBuffer_SetCurrentPosition (dsoundsink->pDSBSecondary, 0);
648 dsoundsink->current_circular_offset = 0;
650 /*reset the buffer */
651 hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary,
652 dsoundsink->current_circular_offset, dsoundsink->buffer_size,
653 &pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L);
655 if (SUCCEEDED (hRes)) {
656 memset (pLockedBuffer, 0, dwSizeBuffer);
659 IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer,
660 dwSizeBuffer, NULL, 0);
664 dsoundsink->first_buffer_after_reset = TRUE;
666 GST_DSOUND_UNLOCK (dsoundsink);