2 * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 * SECTION:element-waveformsink
25 * This element lets you output sound using the Windows WaveForm API.
27 * Note that you should almost always use generic audio conversion elements
28 * like audioconvert and audioresample in front of an audiosink to make sure
29 * your pipeline works under all circumstances (those conversion elements will
30 * act in passthrough-mode if no conversion is necessary).
33 * <title>Example pipelines</title>
35 * gst-launch -v audiotestsrc ! audioconvert ! volume volume=0.1 ! waveformsink
36 * ]| will output a sine wave (continuous beep sound) to your sound card (with
37 * a very low volume as precaution).
39 * gst-launch -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! waveformsink
40 * ]| will play an Ogg/Vorbis audio file and output it.
48 #include "gstwaveformsink.h"
50 GST_DEBUG_CATEGORY_STATIC (waveformsink_debug);
52 static void gst_waveform_sink_finalise (GObject * object);
53 static void gst_waveform_sink_set_property (GObject * object,
54 guint prop_id, const GValue * value, GParamSpec * pspec);
55 static void gst_waveform_sink_get_property (GObject * object,
56 guint prop_id, GValue * value, GParamSpec * pspec);
57 static GstCaps *gst_waveform_sink_getcaps (GstBaseSink * bsink);
59 /************************************************************************/
60 /* GstAudioSink functions */
61 /************************************************************************/
62 static gboolean gst_waveform_sink_prepare (GstAudioSink * asink,
63 GstRingBufferSpec * spec);
64 static gboolean gst_waveform_sink_unprepare (GstAudioSink * asink);
65 static gboolean gst_waveform_sink_open (GstAudioSink * asink);
66 static gboolean gst_waveform_sink_close (GstAudioSink * asink);
67 static guint gst_waveform_sink_write (GstAudioSink * asink, gpointer data,
69 static guint gst_waveform_sink_delay (GstAudioSink * asink);
70 static void gst_waveform_sink_reset (GstAudioSink * asink);
72 /************************************************************************/
74 /************************************************************************/
75 GstCaps *gst_waveform_sink_create_caps (gint rate, gint channels,
76 gint bits_per_sample);
77 WAVEHDR *bufferpool_get_buffer (GstWaveFormSink * wfsink, gpointer data,
79 void CALLBACK waveOutProc (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
80 DWORD_PTR dwParam1, DWORD_PTR dwParam2);
82 static GstStaticPadTemplate waveformsink_sink_factory =
83 GST_STATIC_PAD_TEMPLATE ("sink",
86 GST_STATIC_CAPS ("audio/x-raw-int, "
87 "signed = (boolean) { TRUE, FALSE }, "
90 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
92 "signed = (boolean) { TRUE, FALSE }, "
95 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]"));
97 GST_BOILERPLATE (GstWaveFormSink, gst_waveform_sink, GstAudioSink,
101 gst_waveform_sink_base_init (gpointer g_class)
103 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
105 gst_element_class_set_details_simple (element_class, "WaveForm Audio Sink",
107 "Output to a sound card via WaveForm API",
108 "Sebastien Moutte <sebastien@moutte.net>");
109 gst_element_class_add_static_pad_template (element_class,
110 &waveformsink_sink_factory);
114 gst_waveform_sink_class_init (GstWaveFormSinkClass * klass)
116 GObjectClass *gobject_class;
117 GstElementClass *gstelement_class;
118 GstBaseSinkClass *gstbasesink_class;
119 GstBaseAudioSinkClass *gstbaseaudiosink_class;
120 GstAudioSinkClass *gstaudiosink_class;
122 gobject_class = (GObjectClass *) klass;
123 gstelement_class = (GstElementClass *) klass;
124 gstbasesink_class = (GstBaseSinkClass *) klass;
125 gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
126 gstaudiosink_class = (GstAudioSinkClass *) klass;
128 parent_class = g_type_class_peek_parent (klass);
130 gobject_class->finalize = gst_waveform_sink_finalise;
131 gobject_class->get_property = gst_waveform_sink_get_property;
132 gobject_class->set_property = gst_waveform_sink_set_property;
134 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_waveform_sink_getcaps);
136 gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_waveform_sink_prepare);
137 gstaudiosink_class->unprepare =
138 GST_DEBUG_FUNCPTR (gst_waveform_sink_unprepare);
139 gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_waveform_sink_open);
140 gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_waveform_sink_close);
141 gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_waveform_sink_write);
142 gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_waveform_sink_delay);
143 gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_waveform_sink_reset);
145 GST_DEBUG_CATEGORY_INIT (waveformsink_debug, "waveformsink", 0,
150 gst_waveform_sink_set_property (GObject * object, guint prop_id,
151 const GValue * value, GParamSpec * pspec)
153 /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); */
157 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
163 gst_waveform_sink_get_property (GObject * object, guint prop_id,
164 GValue * value, GParamSpec * pspec)
166 /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); */
170 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
176 gst_waveform_sink_init (GstWaveFormSink * wfsink,
177 GstWaveFormSinkClass * g_class)
179 /* initialize members */
180 wfsink->hwaveout = NULL;
181 wfsink->cached_caps = NULL;
182 wfsink->wave_buffers = NULL;
183 wfsink->write_buffer = 0;
184 wfsink->buffer_count = BUFFER_COUNT;
185 wfsink->buffer_size = BUFFER_SIZE;
186 wfsink->free_buffers_count = wfsink->buffer_count;
187 wfsink->bytes_in_queue = 0;
189 InitializeCriticalSection (&wfsink->critic_wave);
193 gst_waveform_sink_finalise (GObject * object)
195 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
197 if (wfsink->cached_caps) {
198 gst_caps_unref (wfsink->cached_caps);
199 wfsink->cached_caps = NULL;
202 DeleteCriticalSection (&wfsink->critic_wave);
204 G_OBJECT_CLASS (parent_class)->finalize (object);
208 gst_waveform_sink_getcaps (GstBaseSink * bsink)
210 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (bsink);
213 GstCaps *caps, *caps_temp;
215 /* return the cached caps if already defined */
216 if (wfsink->cached_caps) {
217 return gst_caps_ref (wfsink->cached_caps);
220 /* get the default device caps */
221 mmresult = waveOutGetDevCaps (WAVE_MAPPER, &wocaps, sizeof (wocaps));
222 if (mmresult != MMSYSERR_NOERROR) {
223 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
224 GST_ELEMENT_ERROR (wfsink, RESOURCE, SETTINGS,
225 ("gst_waveform_sink_getcaps: waveOutGetDevCaps failed error=>%s",
226 wfsink->error_string), (NULL));
230 caps = gst_caps_new_empty ();
232 /* create a caps for all wave formats supported by the device
233 starting by the best quality format */
234 if (wocaps.dwFormats & WAVE_FORMAT_96S16) {
235 caps_temp = gst_waveform_sink_create_caps (96000, 2, 16);
237 gst_caps_append (caps, caps_temp);
240 if (wocaps.dwFormats & WAVE_FORMAT_96S08) {
241 caps_temp = gst_waveform_sink_create_caps (96000, 2, 8);
243 gst_caps_append (caps, caps_temp);
246 if (wocaps.dwFormats & WAVE_FORMAT_96M16) {
247 caps_temp = gst_waveform_sink_create_caps (96000, 1, 16);
249 gst_caps_append (caps, caps_temp);
252 if (wocaps.dwFormats & WAVE_FORMAT_96M08) {
253 caps_temp = gst_waveform_sink_create_caps (96000, 1, 8);
255 gst_caps_append (caps, caps_temp);
258 if (wocaps.dwFormats & WAVE_FORMAT_4S16) {
259 caps_temp = gst_waveform_sink_create_caps (44100, 2, 16);
261 gst_caps_append (caps, caps_temp);
264 if (wocaps.dwFormats & WAVE_FORMAT_4S08) {
265 caps_temp = gst_waveform_sink_create_caps (44100, 2, 8);
267 gst_caps_append (caps, caps_temp);
270 if (wocaps.dwFormats & WAVE_FORMAT_4M16) {
271 caps_temp = gst_waveform_sink_create_caps (44100, 1, 16);
273 gst_caps_append (caps, caps_temp);
276 if (wocaps.dwFormats & WAVE_FORMAT_4M08) {
277 caps_temp = gst_waveform_sink_create_caps (44100, 1, 8);
279 gst_caps_append (caps, caps_temp);
282 if (wocaps.dwFormats & WAVE_FORMAT_2S16) {
283 caps_temp = gst_waveform_sink_create_caps (22050, 2, 16);
285 gst_caps_append (caps, caps_temp);
288 if (wocaps.dwFormats & WAVE_FORMAT_2S08) {
289 caps_temp = gst_waveform_sink_create_caps (22050, 2, 8);
291 gst_caps_append (caps, caps_temp);
294 if (wocaps.dwFormats & WAVE_FORMAT_2M16) {
295 caps_temp = gst_waveform_sink_create_caps (22050, 1, 16);
297 gst_caps_append (caps, caps_temp);
300 if (wocaps.dwFormats & WAVE_FORMAT_2M08) {
301 caps_temp = gst_waveform_sink_create_caps (22050, 1, 8);
303 gst_caps_append (caps, caps_temp);
306 if (wocaps.dwFormats & WAVE_FORMAT_1S16) {
307 caps_temp = gst_waveform_sink_create_caps (11025, 2, 16);
309 gst_caps_append (caps, caps_temp);
312 if (wocaps.dwFormats & WAVE_FORMAT_1S08) {
313 caps_temp = gst_waveform_sink_create_caps (11025, 2, 8);
315 gst_caps_append (caps, caps_temp);
318 if (wocaps.dwFormats & WAVE_FORMAT_1M16) {
319 caps_temp = gst_waveform_sink_create_caps (11025, 1, 16);
321 gst_caps_append (caps, caps_temp);
324 if (wocaps.dwFormats & WAVE_FORMAT_1M08) {
325 caps_temp = gst_waveform_sink_create_caps (11025, 1, 8);
327 gst_caps_append (caps, caps_temp);
331 if (gst_caps_is_empty (caps)) {
332 gst_caps_unref (caps);
335 wfsink->cached_caps = gst_caps_ref (caps);
338 GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink, "Returning caps %s",
339 gst_caps_to_string (caps));
345 gst_waveform_sink_open (GstAudioSink * asink)
347 /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); */
349 /* nothing to do here as the device needs to be opened with the format we will use */
355 gst_waveform_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
357 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
362 /* setup waveformex struture with the input ringbuffer specs */
363 memset (&wfx, 0, sizeof (wfx));
365 wfx.wFormatTag = WAVE_FORMAT_PCM;
366 wfx.nChannels = spec->channels;
367 wfx.nSamplesPerSec = spec->rate;
368 wfx.wBitsPerSample = (spec->bytes_per_sample * 8) / wfx.nChannels;
369 wfx.nBlockAlign = spec->bytes_per_sample;
370 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
372 /* save bytes per sample to use it in delay */
373 wfsink->bytes_per_sample = spec->bytes_per_sample;
375 /* open the default audio device with the given caps */
376 mmresult = waveOutOpen (&wfsink->hwaveout, WAVE_MAPPER,
377 &wfx, (DWORD_PTR) waveOutProc, (DWORD_PTR) wfsink, CALLBACK_FUNCTION);
378 if (mmresult != MMSYSERR_NOERROR) {
379 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
380 GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
381 ("gst_waveform_sink_prepare: waveOutOpen failed error=>%s",
382 wfsink->error_string), (NULL));
386 /* evaluate the buffer size and the number of buffers needed */
387 wfsink->free_buffers_count = wfsink->buffer_count;
389 /* allocate wave buffers */
390 wfsink->wave_buffers = (WAVEHDR *) g_new0 (WAVEHDR, wfsink->buffer_count);
391 if (!wfsink->wave_buffers) {
392 GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
393 ("gst_waveform_sink_prepare: Failed to allocate wave buffer headers (buffer count=%d)",
394 wfsink->buffer_count), (NULL));
397 memset (wfsink->wave_buffers, 0, sizeof (WAVEHDR) * wfsink->buffer_count);
400 for (index = 0; index < wfsink->buffer_count; index++) {
401 wfsink->wave_buffers[index].dwBufferLength = wfsink->buffer_size;
402 wfsink->wave_buffers[index].lpData = g_new0 (gchar, wfsink->buffer_size);
409 gst_waveform_sink_unprepare (GstAudioSink * asink)
411 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
413 /* free wave buffers */
414 if (wfsink->wave_buffers) {
417 for (index = 0; index < wfsink->buffer_count; index++) {
418 if (wfsink->wave_buffers[index].dwFlags & WHDR_PREPARED) {
419 MMRESULT mmresult = waveOutUnprepareHeader (wfsink->hwaveout,
420 &wfsink->wave_buffers[index], sizeof (WAVEHDR));
421 if (mmresult != MMSYSERR_NOERROR) {
422 waveOutGetErrorText (mmresult, wfsink->error_string,
424 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
425 "gst_waveform_sink_unprepare: Error unpreparing buffer => %s",
426 wfsink->error_string);
429 g_free (wfsink->wave_buffers[index].lpData);
431 g_free (wfsink->wave_buffers);
432 wfsink->wave_buffers = NULL;
435 /* close waveform-audio output device */
436 if (wfsink->hwaveout) {
437 waveOutClose (wfsink->hwaveout);
438 wfsink->hwaveout = NULL;
445 gst_waveform_sink_close (GstAudioSink * asink)
447 /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); */
453 gst_waveform_sink_write (GstAudioSink * asink, gpointer data, guint length)
455 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
458 guint bytes_to_write = length;
459 guint remaining_length = length;
461 wfsink->bytes_in_queue += length;
463 while (remaining_length > 0) {
464 if (wfsink->free_buffers_count == 0) {
465 /* no free buffer available, wait for one */
470 /* get the current write buffer header */
471 waveheader = &wfsink->wave_buffers[wfsink->write_buffer];
473 /* unprepare the header if needed */
474 if (waveheader->dwFlags & WHDR_PREPARED) {
476 waveOutUnprepareHeader (wfsink->hwaveout, waveheader,
478 if (mmresult != MMSYSERR_NOERROR) {
479 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
480 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
481 "Error unpreparing buffer => %s", wfsink->error_string);
485 if (wfsink->buffer_size - waveheader->dwUser >= remaining_length)
486 bytes_to_write = remaining_length;
488 bytes_to_write = wfsink->buffer_size - waveheader->dwUser;
490 memcpy (waveheader->lpData + waveheader->dwUser, data, bytes_to_write);
491 waveheader->dwUser += bytes_to_write;
492 remaining_length -= bytes_to_write;
493 data = (guint8 *) data + bytes_to_write;
495 if (waveheader->dwUser == wfsink->buffer_size) {
496 /* we have filled a buffer, let's prepare it and next write it to the device */
498 waveOutPrepareHeader (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
499 if (mmresult != MMSYSERR_NOERROR) {
500 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
501 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
502 "gst_waveform_sink_write: Error preparing header => %s",
503 wfsink->error_string);
505 mmresult = waveOutWrite (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
506 if (mmresult != MMSYSERR_NOERROR) {
507 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
508 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
509 "gst_waveform_sink_write: Error writting buffer to the device => %s",
510 wfsink->error_string);
513 EnterCriticalSection (&wfsink->critic_wave);
514 wfsink->free_buffers_count--;
515 LeaveCriticalSection (&wfsink->critic_wave);
517 wfsink->write_buffer++;
518 wfsink->write_buffer %= wfsink->buffer_count;
519 waveheader->dwUser = 0;
520 wfsink->bytes_in_queue = 0;
521 GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink,
522 "gst_waveform_sink_write: Writting a buffer to the device (free buffers remaining=%d, write buffer=%d)",
523 wfsink->free_buffers_count, wfsink->write_buffer);
531 gst_waveform_sink_delay (GstAudioSink * asink)
533 /* return the number of samples in queue (device+internal queue) */
534 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
535 guint bytes_in_device =
536 (wfsink->buffer_count - wfsink->free_buffers_count) * wfsink->buffer_size;
538 (bytes_in_device + wfsink->bytes_in_queue) / wfsink->bytes_per_sample;
543 gst_waveform_sink_reset (GstAudioSink * asink)
545 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
546 MMRESULT mmresult = waveOutReset (wfsink->hwaveout);
548 if (mmresult != MMSYSERR_NOERROR) {
549 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
550 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
551 "gst_waveform_sink_reset: Error reseting waveform-audio device => %s",
552 wfsink->error_string);
557 gst_waveform_sink_create_caps (gint rate, gint channels, gint bits_per_sample)
559 GstCaps *caps = NULL;
561 caps = gst_caps_new_simple ("audio/x-raw-int",
562 "width", G_TYPE_INT, bits_per_sample,
563 "depth", G_TYPE_INT, bits_per_sample,
564 "endianness", G_TYPE_INT, G_BYTE_ORDER,
565 "signed", G_TYPE_BOOLEAN, TRUE,
566 "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL);
571 waveOutProc (HWAVEOUT hwo,
572 UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
574 GstWaveFormSink *wfsink = (GstWaveFormSink *) dwInstance;
576 if (uMsg == WOM_DONE) {
577 EnterCriticalSection (&wfsink->critic_wave);
578 wfsink->free_buffers_count++;
579 LeaveCriticalSection (&wfsink->critic_wave);