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., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, 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-1.0 -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-1.0 -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,
60 /************************************************************************/
61 /* GstAudioSink functions */
62 /************************************************************************/
63 static gboolean gst_waveform_sink_prepare (GstAudioSink * asink,
64 GstAudioRingBufferSpec * spec);
65 static gboolean gst_waveform_sink_unprepare (GstAudioSink * asink);
66 static gboolean gst_waveform_sink_open (GstAudioSink * asink);
67 static gboolean gst_waveform_sink_close (GstAudioSink * asink);
68 static gint gst_waveform_sink_write (GstAudioSink * asink, gpointer data,
70 static guint gst_waveform_sink_delay (GstAudioSink * asink);
71 static void gst_waveform_sink_reset (GstAudioSink * asink);
73 /************************************************************************/
75 /************************************************************************/
76 GstCaps *gst_waveform_sink_create_caps (gint rate, gint channels,
77 const gchar * format);
78 WAVEHDR *bufferpool_get_buffer (GstWaveFormSink * wfsink, gpointer data,
80 void CALLBACK waveOutProc (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
81 DWORD_PTR dwParam1, DWORD_PTR dwParam2);
83 static GstStaticPadTemplate waveformsink_sink_factory =
84 GST_STATIC_PAD_TEMPLATE ("sink",
87 GST_STATIC_CAPS ("audio/x-raw, "
88 "format = (string) { " GST_AUDIO_NE (S16) ", S8 }, "
89 "layout = (string) interleaved, "
90 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]"));
92 #define gst_waveform_sink_parent_class parent_class
93 G_DEFINE_TYPE (GstWaveFormSink, gst_waveform_sink, GST_TYPE_AUDIO_SINK);
96 gst_waveform_sink_class_init (GstWaveFormSinkClass * klass)
98 GObjectClass *gobject_class;
99 GstBaseSinkClass *gstbasesink_class;
100 GstAudioSinkClass *gstaudiosink_class;
101 GstElementClass *element_class;
103 gobject_class = (GObjectClass *) klass;
104 gstbasesink_class = (GstBaseSinkClass *) klass;
105 gstaudiosink_class = (GstAudioSinkClass *) klass;
106 element_class = GST_ELEMENT_CLASS (klass);
108 parent_class = g_type_class_peek_parent (klass);
110 gobject_class->finalize = gst_waveform_sink_finalise;
111 gobject_class->get_property = gst_waveform_sink_get_property;
112 gobject_class->set_property = gst_waveform_sink_set_property;
114 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_waveform_sink_getcaps);
116 gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_waveform_sink_prepare);
117 gstaudiosink_class->unprepare =
118 GST_DEBUG_FUNCPTR (gst_waveform_sink_unprepare);
119 gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_waveform_sink_open);
120 gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_waveform_sink_close);
121 gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_waveform_sink_write);
122 gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_waveform_sink_delay);
123 gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_waveform_sink_reset);
125 GST_DEBUG_CATEGORY_INIT (waveformsink_debug, "waveformsink", 0,
128 gst_element_class_set_static_metadata (element_class, "WaveForm Audio Sink",
130 "Output to a sound card via WaveForm API",
131 "Sebastien Moutte <sebastien@moutte.net>");
133 gst_element_class_add_static_pad_template (element_class,
134 &waveformsink_sink_factory);
138 gst_waveform_sink_set_property (GObject * object, guint prop_id,
139 const GValue * value, GParamSpec * pspec)
141 /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); */
145 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
151 gst_waveform_sink_get_property (GObject * object, guint prop_id,
152 GValue * value, GParamSpec * pspec)
154 /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); */
158 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
164 gst_waveform_sink_init (GstWaveFormSink * wfsink)
166 /* initialize members */
167 wfsink->hwaveout = NULL;
168 wfsink->cached_caps = NULL;
169 wfsink->wave_buffers = NULL;
170 wfsink->write_buffer = 0;
171 wfsink->buffer_count = BUFFER_COUNT;
172 wfsink->buffer_size = BUFFER_SIZE;
173 wfsink->free_buffers_count = wfsink->buffer_count;
174 wfsink->bytes_in_queue = 0;
176 InitializeCriticalSection (&wfsink->critic_wave);
180 gst_waveform_sink_finalise (GObject * object)
182 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
184 if (wfsink->cached_caps) {
185 gst_caps_unref (wfsink->cached_caps);
186 wfsink->cached_caps = NULL;
189 DeleteCriticalSection (&wfsink->critic_wave);
191 G_OBJECT_CLASS (parent_class)->finalize (object);
195 gst_waveform_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
197 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (bsink);
200 GstCaps *caps, *caps_temp;
202 /* return the cached caps if already defined */
203 if (wfsink->cached_caps) {
204 return gst_caps_ref (wfsink->cached_caps);
207 /* get the default device caps */
208 mmresult = waveOutGetDevCaps (WAVE_MAPPER, &wocaps, sizeof (wocaps));
209 if (mmresult != MMSYSERR_NOERROR) {
210 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
211 GST_ELEMENT_ERROR (wfsink, RESOURCE, SETTINGS,
212 ("gst_waveform_sink_getcaps: waveOutGetDevCaps failed error=>%s",
213 wfsink->error_string), (NULL));
217 caps = gst_caps_new_empty ();
219 /* create a caps for all wave formats supported by the device
220 starting by the best quality format */
221 if (wocaps.dwFormats & WAVE_FORMAT_96S16) {
222 caps_temp = gst_waveform_sink_create_caps (96000, 2, GST_AUDIO_NE (S16));
224 gst_caps_append (caps, caps_temp);
227 if (wocaps.dwFormats & WAVE_FORMAT_96S08) {
228 caps_temp = gst_waveform_sink_create_caps (96000, 2, "S8");
230 gst_caps_append (caps, caps_temp);
233 if (wocaps.dwFormats & WAVE_FORMAT_96M16) {
234 caps_temp = gst_waveform_sink_create_caps (96000, 1, GST_AUDIO_NE (S16));
236 gst_caps_append (caps, caps_temp);
239 if (wocaps.dwFormats & WAVE_FORMAT_96M08) {
240 caps_temp = gst_waveform_sink_create_caps (96000, 1, "S8");
242 gst_caps_append (caps, caps_temp);
245 if (wocaps.dwFormats & WAVE_FORMAT_4S16) {
246 caps_temp = gst_waveform_sink_create_caps (44100, 2, GST_AUDIO_NE (S16));
248 gst_caps_append (caps, caps_temp);
251 if (wocaps.dwFormats & WAVE_FORMAT_4S08) {
252 caps_temp = gst_waveform_sink_create_caps (44100, 2, "S8");
254 gst_caps_append (caps, caps_temp);
257 if (wocaps.dwFormats & WAVE_FORMAT_4M16) {
258 caps_temp = gst_waveform_sink_create_caps (44100, 1, GST_AUDIO_NE (S16));
260 gst_caps_append (caps, caps_temp);
263 if (wocaps.dwFormats & WAVE_FORMAT_4M08) {
264 caps_temp = gst_waveform_sink_create_caps (44100, 1, "S8");
266 gst_caps_append (caps, caps_temp);
269 if (wocaps.dwFormats & WAVE_FORMAT_2S16) {
270 caps_temp = gst_waveform_sink_create_caps (22050, 2, GST_AUDIO_NE (S16));
272 gst_caps_append (caps, caps_temp);
275 if (wocaps.dwFormats & WAVE_FORMAT_2S08) {
276 caps_temp = gst_waveform_sink_create_caps (22050, 2, "S8");
278 gst_caps_append (caps, caps_temp);
281 if (wocaps.dwFormats & WAVE_FORMAT_2M16) {
282 caps_temp = gst_waveform_sink_create_caps (22050, 1, GST_AUDIO_NE (S16));
284 gst_caps_append (caps, caps_temp);
287 if (wocaps.dwFormats & WAVE_FORMAT_2M08) {
288 caps_temp = gst_waveform_sink_create_caps (22050, 1, "S8");
290 gst_caps_append (caps, caps_temp);
293 if (wocaps.dwFormats & WAVE_FORMAT_1S16) {
294 caps_temp = gst_waveform_sink_create_caps (11025, 2, GST_AUDIO_NE (S16));
296 gst_caps_append (caps, caps_temp);
299 if (wocaps.dwFormats & WAVE_FORMAT_1S08) {
300 caps_temp = gst_waveform_sink_create_caps (11025, 2, "S8");
302 gst_caps_append (caps, caps_temp);
305 if (wocaps.dwFormats & WAVE_FORMAT_1M16) {
306 caps_temp = gst_waveform_sink_create_caps (11025, 1, GST_AUDIO_NE (S16));
308 gst_caps_append (caps, caps_temp);
311 if (wocaps.dwFormats & WAVE_FORMAT_1M08) {
312 caps_temp = gst_waveform_sink_create_caps (11025, 1, "S8");
314 gst_caps_append (caps, caps_temp);
318 if (gst_caps_is_empty (caps)) {
319 gst_caps_unref (caps);
322 wfsink->cached_caps = gst_caps_ref (caps);
325 GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink,
326 "Returning caps %" GST_PTR_FORMAT, caps);
332 gst_waveform_sink_open (GstAudioSink * asink)
334 /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); */
336 /* nothing to do here as the device needs to be opened with the format we will use */
342 gst_waveform_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
344 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
349 /* setup waveformex struture with the input ringbuffer specs */
350 memset (&wfx, 0, sizeof (wfx));
352 wfx.wFormatTag = WAVE_FORMAT_PCM;
353 wfx.nChannels = spec->info.channels;
354 wfx.nSamplesPerSec = spec->info.rate;
355 wfx.wBitsPerSample = (spec->info.bpf * 8) / wfx.nChannels;
356 wfx.nBlockAlign = spec->info.bpf;
357 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
359 /* save bytes per sample to use it in delay */
360 wfsink->bytes_per_sample = spec->info.bpf;
362 /* open the default audio device with the given caps */
363 mmresult = waveOutOpen (&wfsink->hwaveout, WAVE_MAPPER,
364 &wfx, (DWORD_PTR) waveOutProc, (DWORD_PTR) wfsink, CALLBACK_FUNCTION);
365 if (mmresult != MMSYSERR_NOERROR) {
366 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
367 GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
368 ("gst_waveform_sink_prepare: waveOutOpen failed error=>%s",
369 wfsink->error_string), (NULL));
373 /* evaluate the buffer size and the number of buffers needed */
374 wfsink->free_buffers_count = wfsink->buffer_count;
376 /* allocate wave buffers */
377 wfsink->wave_buffers = (WAVEHDR *) g_new0 (WAVEHDR, wfsink->buffer_count);
378 if (!wfsink->wave_buffers) {
379 GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
380 ("gst_waveform_sink_prepare: Failed to allocate wave buffer headers (buffer count=%d)",
381 wfsink->buffer_count), (NULL));
384 memset (wfsink->wave_buffers, 0, sizeof (WAVEHDR) * wfsink->buffer_count);
387 for (index = 0; index < wfsink->buffer_count; index++) {
388 wfsink->wave_buffers[index].dwBufferLength = wfsink->buffer_size;
389 wfsink->wave_buffers[index].lpData = g_new0 (gchar, wfsink->buffer_size);
396 gst_waveform_sink_unprepare (GstAudioSink * asink)
398 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
400 /* free wave buffers */
401 if (wfsink->wave_buffers) {
404 for (index = 0; index < wfsink->buffer_count; index++) {
405 if (wfsink->wave_buffers[index].dwFlags & WHDR_PREPARED) {
406 MMRESULT mmresult = waveOutUnprepareHeader (wfsink->hwaveout,
407 &wfsink->wave_buffers[index], sizeof (WAVEHDR));
408 if (mmresult != MMSYSERR_NOERROR) {
409 waveOutGetErrorText (mmresult, wfsink->error_string,
411 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
412 "gst_waveform_sink_unprepare: Error unpreparing buffer => %s",
413 wfsink->error_string);
416 g_free (wfsink->wave_buffers[index].lpData);
418 g_free (wfsink->wave_buffers);
419 wfsink->wave_buffers = NULL;
422 /* close waveform-audio output device */
423 if (wfsink->hwaveout) {
424 waveOutClose (wfsink->hwaveout);
425 wfsink->hwaveout = NULL;
432 gst_waveform_sink_close (GstAudioSink * asink)
434 /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); */
440 gst_waveform_sink_write (GstAudioSink * asink, gpointer data, guint length)
442 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
445 guint bytes_to_write = length;
446 guint remaining_length = length;
448 wfsink->bytes_in_queue += length;
450 while (remaining_length > 0) {
451 if (wfsink->free_buffers_count == 0) {
452 /* no free buffer available, wait for one */
457 /* get the current write buffer header */
458 waveheader = &wfsink->wave_buffers[wfsink->write_buffer];
460 /* unprepare the header if needed */
461 if (waveheader->dwFlags & WHDR_PREPARED) {
463 waveOutUnprepareHeader (wfsink->hwaveout, waveheader,
465 if (mmresult != MMSYSERR_NOERROR) {
466 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
467 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
468 "Error unpreparing buffer => %s", wfsink->error_string);
472 if (wfsink->buffer_size - waveheader->dwUser >= remaining_length)
473 bytes_to_write = remaining_length;
475 bytes_to_write = wfsink->buffer_size - waveheader->dwUser;
477 memcpy (waveheader->lpData + waveheader->dwUser, data, bytes_to_write);
478 waveheader->dwUser += bytes_to_write;
479 remaining_length -= bytes_to_write;
480 data = (guint8 *) data + bytes_to_write;
482 if (waveheader->dwUser == wfsink->buffer_size) {
483 /* we have filled a buffer, let's prepare it and next write it to the device */
485 waveOutPrepareHeader (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
486 if (mmresult != MMSYSERR_NOERROR) {
487 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
488 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
489 "gst_waveform_sink_write: Error preparing header => %s",
490 wfsink->error_string);
492 mmresult = waveOutWrite (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
493 if (mmresult != MMSYSERR_NOERROR) {
494 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
495 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
496 "gst_waveform_sink_write: Error writting buffer to the device => %s",
497 wfsink->error_string);
500 EnterCriticalSection (&wfsink->critic_wave);
501 wfsink->free_buffers_count--;
502 LeaveCriticalSection (&wfsink->critic_wave);
504 wfsink->write_buffer++;
505 wfsink->write_buffer %= wfsink->buffer_count;
506 waveheader->dwUser = 0;
507 wfsink->bytes_in_queue = 0;
508 GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink,
509 "gst_waveform_sink_write: Writting a buffer to the device (free buffers remaining=%d, write buffer=%d)",
510 wfsink->free_buffers_count, wfsink->write_buffer);
518 gst_waveform_sink_delay (GstAudioSink * asink)
520 /* return the number of samples in queue (device+internal queue) */
521 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
522 guint bytes_in_device =
523 (wfsink->buffer_count - wfsink->free_buffers_count) * wfsink->buffer_size;
525 (bytes_in_device + wfsink->bytes_in_queue) / wfsink->bytes_per_sample;
530 gst_waveform_sink_reset (GstAudioSink * asink)
532 GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
533 MMRESULT mmresult = waveOutReset (wfsink->hwaveout);
535 if (mmresult != MMSYSERR_NOERROR) {
536 waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
537 GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
538 "gst_waveform_sink_reset: Error reseting waveform-audio device => %s",
539 wfsink->error_string);
544 gst_waveform_sink_create_caps (gint rate, gint channels, const gchar * format)
546 GstCaps *caps = NULL;
548 caps = gst_caps_new_simple ("audio/x-raw",
549 "format", G_TYPE_STRING, format,
550 "layout", G_TYPE_STRING, "interleaved",
551 "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL);
556 waveOutProc (HWAVEOUT hwo,
557 UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
559 GstWaveFormSink *wfsink = (GstWaveFormSink *) dwInstance;
561 if (uMsg == WOM_DONE) {
562 EnterCriticalSection (&wfsink->critic_wave);
563 wfsink->free_buffers_count++;
564 LeaveCriticalSection (&wfsink->critic_wave);