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_base_init (gpointer g_class);
53 static void gst_waveform_sink_class_init (GstWaveFormSinkClass * klass);
54 static void gst_waveform_sink_init (GstWaveFormSink * wfsink,
55 GstWaveFormSinkClass * g_class);
56 static void gst_waveform_sink_finalise (GObject * object);
57 static void gst_waveform_sink_set_property (GObject * object,
58 guint prop_id, const GValue * value, GParamSpec * pspec);
59 static void gst_waveform_sink_get_property (GObject * object,
60 guint prop_id, GValue * value, GParamSpec * pspec);
61 static GstCaps *gst_waveform_sink_getcaps (GstBaseSink * bsink);
63 /************************************************************************/
64 /* GstAudioSink functions */
65 /************************************************************************/
66 static gboolean gst_waveform_sink_prepare (GstAudioSink * asink,
67 GstRingBufferSpec * spec);
68 static gboolean gst_waveform_sink_unprepare (GstAudioSink * asink);
69 static gboolean gst_waveform_sink_open (GstAudioSink * asink);
70 static gboolean gst_waveform_sink_close (GstAudioSink * asink);
71 static guint gst_waveform_sink_write (GstAudioSink * asink, gpointer data,
73 static guint gst_waveform_sink_delay (GstAudioSink * asink);
74 static void gst_waveform_sink_reset (GstAudioSink * asink);
76 /************************************************************************/
78 /************************************************************************/
79 GstCaps *gst_waveform_sink_create_caps (gint rate, gint channels,
80 gint bits_per_sample);
81 WAVEHDR *bufferpool_get_buffer (GstWaveFormSink * wfsink, gpointer data,
83 void CALLBACK waveOutProc (HWAVEOUT hwo, UINT uMsg, unsigned long dwInstance,
84 DWORD dwParam1, DWORD dwParam2);
86 static GstStaticPadTemplate waveformsink_sink_factory =
87 GST_STATIC_PAD_TEMPLATE ("sink",
90 GST_STATIC_CAPS ("audio/x-raw-int, "
91 "signed = (boolean) { TRUE, FALSE }, "
94 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
96 "signed = (boolean) { TRUE, FALSE }, "
99 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]"));
101 GST_BOILERPLATE (GstWaveFormSink, gst_waveform_sink, GstAudioSink,
102 GST_TYPE_AUDIO_SINK);
105 gst_waveform_sink_base_init (gpointer g_class)
107 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
109 gst_element_class_set_details_simple (element_class, "WaveForm Audio Sink",
111 "Output to a sound card via WaveForm API",
112 "Sebastien Moutte <sebastien@moutte.net>");
113 gst_element_class_add_static_pad_template (element_class,
114 &waveformsink_sink_factory);
118 gst_waveform_sink_class_init (GstWaveFormSinkClass * klass)
120 GObjectClass *gobject_class;
121 GstElementClass *gstelement_class;
122 GstBaseSinkClass *gstbasesink_class;
123 GstBaseAudioSinkClass *gstbaseaudiosink_class;
124 GstAudioSinkClass *gstaudiosink_class;
126 gobject_class = (GObjectClass *) klass;
127 gstelement_class = (GstElementClass *) klass;
128 gstbasesink_class = (GstBaseSinkClass *) klass;
129 gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
130 gstaudiosink_class = (GstAudioSinkClass *) klass;
132 parent_class = g_type_class_peek_parent (klass);
134 gobject_class->finalize = gst_waveform_sink_finalise;
135 gobject_class->get_property = gst_waveform_sink_get_property;
136 gobject_class->set_property = gst_waveform_sink_set_property;
138 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_waveform_sink_getcaps);
140 gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_waveform_sink_prepare);
141 gstaudiosink_class->unprepare =
142 GST_DEBUG_FUNCPTR (gst_waveform_sink_unprepare);
143 gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_waveform_sink_open);
144 gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_waveform_sink_close);
145 gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_waveform_sink_write);
146 gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_waveform_sink_delay);
147 gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_waveform_sink_reset);
149 GST_DEBUG_CATEGORY_INIT (waveformsink_debug, "waveformsink", 0,
154 gst_waveform_sink_set_property (GObject * object, guint prop_id,
155 const GValue * value, GParamSpec * pspec)
157 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
161 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
167 gst_waveform_sink_get_property (GObject * object, guint prop_id,
168 GValue * value, GParamSpec * pspec)
170 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
174 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
180 gst_waveform_sink_init (GstWaveFormSink * wfsink,
181 GstWaveFormSinkClass * g_class)
183 /* initialize members */
184 wfsink->hwaveout = NULL;
185 wfsink->cached_caps = NULL;
186 wfsink->wave_buffers = NULL;
187 wfsink->write_buffer = 0;
188 wfsink->buffer_count = BUFFER_COUNT;
189 wfsink->buffer_size = BUFFER_SIZE;
190 wfsink->free_buffers_count = wfsink->buffer_count;
191 wfsink->bytes_in_queue = 0;
193 InitializeCriticalSection (&wfsink->critic_wave);
197 gst_waveform_sink_finalise (GObject * object)
199 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
201 if (wfsink->cached_caps) {
202 gst_caps_unref (wfsink->cached_caps);
203 wfsink->cached_caps = NULL;
206 DeleteCriticalSection (&wfsink->critic_wave);
208 G_OBJECT_CLASS (parent_class)->finalize (object);
212 gst_waveform_sink_getcaps (GstBaseSink * bsink)
214 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (bsink);
217 GstCaps *caps, *caps_temp;
219 /* return the cached caps if already defined */
220 if (wfsink->cached_caps) {
221 return gst_caps_ref (wfsink->cached_caps);
224 /* get the default device caps */
225 mmresult = waveOutGetDevCaps (WAVE_MAPPER, &wocaps, sizeof (wocaps));
226 if (mmresult != MMSYSERR_NOERROR) {
227 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
228 GST_ELEMENT_ERROR (wfsink, RESOURCE, SETTINGS,
229 ("gst_waveform_sink_getcaps: waveOutGetDevCaps failed error=>%s",
230 wfsink->error_string), (NULL));
234 caps = gst_caps_new_empty ();
236 /* create a caps for all wave formats supported by the device
237 starting by the best quality format */
238 if (wocaps.dwFormats & WAVE_FORMAT_96S16) {
239 caps_temp = gst_waveform_sink_create_caps (96000, 2, 16);
241 gst_caps_append (caps, caps_temp);
244 if (wocaps.dwFormats & WAVE_FORMAT_96S08) {
245 caps_temp = gst_waveform_sink_create_caps (96000, 2, 8);
247 gst_caps_append (caps, caps_temp);
250 if (wocaps.dwFormats & WAVE_FORMAT_96M16) {
251 caps_temp = gst_waveform_sink_create_caps (96000, 1, 16);
253 gst_caps_append (caps, caps_temp);
256 if (wocaps.dwFormats & WAVE_FORMAT_96M08) {
257 caps_temp = gst_waveform_sink_create_caps (96000, 1, 8);
259 gst_caps_append (caps, caps_temp);
262 if (wocaps.dwFormats & WAVE_FORMAT_4S16) {
263 caps_temp = gst_waveform_sink_create_caps (44100, 2, 16);
265 gst_caps_append (caps, caps_temp);
268 if (wocaps.dwFormats & WAVE_FORMAT_4S08) {
269 caps_temp = gst_waveform_sink_create_caps (44100, 2, 8);
271 gst_caps_append (caps, caps_temp);
274 if (wocaps.dwFormats & WAVE_FORMAT_4M16) {
275 caps_temp = gst_waveform_sink_create_caps (44100, 1, 16);
277 gst_caps_append (caps, caps_temp);
280 if (wocaps.dwFormats & WAVE_FORMAT_4M08) {
281 caps_temp = gst_waveform_sink_create_caps (44100, 1, 8);
283 gst_caps_append (caps, caps_temp);
286 if (wocaps.dwFormats & WAVE_FORMAT_2S16) {
287 caps_temp = gst_waveform_sink_create_caps (22050, 2, 16);
289 gst_caps_append (caps, caps_temp);
292 if (wocaps.dwFormats & WAVE_FORMAT_2S08) {
293 caps_temp = gst_waveform_sink_create_caps (22050, 2, 8);
295 gst_caps_append (caps, caps_temp);
298 if (wocaps.dwFormats & WAVE_FORMAT_2M16) {
299 caps_temp = gst_waveform_sink_create_caps (22050, 1, 16);
301 gst_caps_append (caps, caps_temp);
304 if (wocaps.dwFormats & WAVE_FORMAT_2M08) {
305 caps_temp = gst_waveform_sink_create_caps (22050, 1, 8);
307 gst_caps_append (caps, caps_temp);
310 if (wocaps.dwFormats & WAVE_FORMAT_1S16) {
311 caps_temp = gst_waveform_sink_create_caps (11025, 2, 16);
313 gst_caps_append (caps, caps_temp);
316 if (wocaps.dwFormats & WAVE_FORMAT_1S08) {
317 caps_temp = gst_waveform_sink_create_caps (11025, 2, 8);
319 gst_caps_append (caps, caps_temp);
322 if (wocaps.dwFormats & WAVE_FORMAT_1M16) {
323 caps_temp = gst_waveform_sink_create_caps (11025, 1, 16);
325 gst_caps_append (caps, caps_temp);
328 if (wocaps.dwFormats & WAVE_FORMAT_1M08) {
329 caps_temp = gst_waveform_sink_create_caps (11025, 1, 8);
331 gst_caps_append (caps, caps_temp);
335 if (gst_caps_is_empty (caps)) {
336 gst_caps_unref (caps);
339 wfsink->cached_caps = gst_caps_ref (caps);
342 GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink, "Returning caps %s",
343 gst_caps_to_string (caps));
349 gst_waveform_sink_open (GstAudioSink * asink)
351 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
353 /* nothing to do here as the device needs to be opened with the format we will use */
359 gst_waveform_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
361 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
366 /* setup waveformex struture with the input ringbuffer specs */
367 memset (&wfx, 0, sizeof (wfx));
369 wfx.wFormatTag = WAVE_FORMAT_PCM;
370 wfx.nChannels = spec->channels;
371 wfx.nSamplesPerSec = spec->rate;
372 wfx.wBitsPerSample = (spec->bytes_per_sample * 8) / wfx.nChannels;
373 wfx.nBlockAlign = spec->bytes_per_sample;
374 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
376 /* save bytes per sample to use it in delay */
377 wfsink->bytes_per_sample = spec->bytes_per_sample;
379 /* open the default audio device with the given caps */
380 mmresult = waveOutOpen (&wfsink->hwaveout, WAVE_MAPPER,
381 &wfx, (DWORD) waveOutProc, (DWORD) wfsink, CALLBACK_FUNCTION);
382 if (mmresult != MMSYSERR_NOERROR) {
383 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
384 GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
385 ("gst_waveform_sink_prepare: waveOutOpen failed error=>%s",
386 wfsink->error_string), (NULL));
390 /* evaluate the buffer size and the number of buffers needed */
391 wfsink->free_buffers_count = wfsink->buffer_count;
393 /* allocate wave buffers */
394 wfsink->wave_buffers = (WAVEHDR *) g_new0 (WAVEHDR, wfsink->buffer_count);
395 if (!wfsink->wave_buffers) {
396 GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
397 ("gst_waveform_sink_prepare: Failed to allocate wave buffer headers (buffer count=%d)",
398 wfsink->buffer_count), (NULL));
401 memset (wfsink->wave_buffers, 0, sizeof (WAVEHDR) * wfsink->buffer_count);
404 for (index = 0; index < wfsink->buffer_count; index++) {
405 wfsink->wave_buffers[index].dwBufferLength = wfsink->buffer_size;
406 wfsink->wave_buffers[index].lpData = g_new0 (gchar, wfsink->buffer_size);
413 gst_waveform_sink_unprepare (GstAudioSink * asink)
415 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
417 /* free wave buffers */
418 if (wfsink->wave_buffers) {
421 for (index = 0; index < wfsink->buffer_count; index++) {
422 if (wfsink->wave_buffers[index].dwFlags & WHDR_PREPARED) {
423 MMRESULT mmresult = waveOutUnprepareHeader (wfsink->hwaveout,
424 &wfsink->wave_buffers[index], sizeof (WAVEHDR));
425 if (mmresult != MMSYSERR_NOERROR) {
426 waveOutGetErrorText (mmresult, wfsink->error_string,
428 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
429 "gst_waveform_sink_unprepare: Error unpreparing buffer => %s",
430 wfsink->error_string);
433 g_free (wfsink->wave_buffers[index].lpData);
435 g_free (wfsink->wave_buffers);
436 wfsink->wave_buffers = NULL;
439 /* close waveform-audio output device */
440 if (wfsink->hwaveout) {
441 waveOutClose (wfsink->hwaveout);
442 wfsink->hwaveout = NULL;
449 gst_waveform_sink_close (GstAudioSink * asink)
451 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
457 gst_waveform_sink_write (GstAudioSink * asink, gpointer data, guint length)
459 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
462 guint bytes_to_write = length;
463 guint remaining_length = length;
465 wfsink->bytes_in_queue += length;
467 while (remaining_length > 0) {
468 if (wfsink->free_buffers_count == 0) {
469 /* no free buffer available, wait for one */
474 /* get the current write buffer header */
475 waveheader = &wfsink->wave_buffers[wfsink->write_buffer];
477 /* unprepare the header if needed */
478 if (waveheader->dwFlags & WHDR_PREPARED) {
480 waveOutUnprepareHeader (wfsink->hwaveout, waveheader,
482 if (mmresult != MMSYSERR_NOERROR) {
483 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
484 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
485 "Error unpreparing buffer => %s", wfsink->error_string);
489 if (wfsink->buffer_size - waveheader->dwUser >= remaining_length)
490 bytes_to_write = remaining_length;
492 bytes_to_write = wfsink->buffer_size - waveheader->dwUser;
494 memcpy (waveheader->lpData + waveheader->dwUser, data, bytes_to_write);
495 waveheader->dwUser += bytes_to_write;
496 remaining_length -= bytes_to_write;
497 data = (guint8 *) data + bytes_to_write;
499 if (waveheader->dwUser == wfsink->buffer_size) {
500 /* we have filled a buffer, let's prepare it and next write it to the device */
502 waveOutPrepareHeader (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
503 if (mmresult != MMSYSERR_NOERROR) {
504 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
505 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
506 "gst_waveform_sink_write: Error preparing header => %s",
507 wfsink->error_string);
509 mmresult = waveOutWrite (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
510 if (mmresult != MMSYSERR_NOERROR) {
511 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
512 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
513 "gst_waveform_sink_write: Error writting buffer to the device => %s",
514 wfsink->error_string);
517 EnterCriticalSection (&wfsink->critic_wave);
518 wfsink->free_buffers_count--;
519 LeaveCriticalSection (&wfsink->critic_wave);
521 wfsink->write_buffer++;
522 wfsink->write_buffer %= wfsink->buffer_count;
523 waveheader->dwUser = 0;
524 wfsink->bytes_in_queue = 0;
525 GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink,
526 "gst_waveform_sink_write: Writting a buffer to the device (free buffers remaining=%d, write buffer=%d)",
527 wfsink->free_buffers_count, wfsink->write_buffer);
535 gst_waveform_sink_delay (GstAudioSink * asink)
537 /* return the number of samples in queue (device+internal queue) */
538 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
539 guint bytes_in_device =
540 (wfsink->buffer_count - wfsink->free_buffers_count) * wfsink->buffer_size;
542 (bytes_in_device + wfsink->bytes_in_queue) / wfsink->bytes_per_sample;
547 gst_waveform_sink_reset (GstAudioSink * asink)
549 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
550 MMRESULT mmresult = waveOutReset (wfsink->hwaveout);
552 if (mmresult != MMSYSERR_NOERROR) {
553 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
554 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
555 "gst_waveform_sink_reset: Error reseting waveform-audio device => %s",
556 wfsink->error_string);
561 gst_waveform_sink_create_caps (gint rate, gint channels, gint bits_per_sample)
563 GstCaps *caps = NULL;
565 caps = gst_caps_new_simple ("audio/x-raw-int",
566 "width", G_TYPE_INT, bits_per_sample,
567 "depth", G_TYPE_INT, bits_per_sample,
568 "endianness", G_TYPE_INT, G_BYTE_ORDER,
569 "signed", G_TYPE_BOOLEAN, TRUE,
570 "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL);
575 waveOutProc (HWAVEOUT hwo,
576 UINT uMsg, unsigned long dwInstance, DWORD dwParam1, DWORD dwParam2)
578 GstWaveFormSink *wfsink = (GstWaveFormSink *) dwInstance;
580 if (uMsg == WOM_DONE) {
581 EnterCriticalSection (&wfsink->critic_wave);
582 wfsink->free_buffers_count++;
583 LeaveCriticalSection (&wfsink->critic_wave);