e709d649aee4b18eba71597116f2f13d06614e91
[platform/upstream/gst-plugins-good.git] / sys / waveform / gstwaveformsink.c
1 /* GStreamer
2 * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
3 *
4 * gstwaveformsink.c:
5 *
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.
10 *
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.
15 *
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.
20 */
21
22 /**
23  * SECTION:element-waveformsink
24  * @short_description: output sound using WaveForm API
25  *
26  * <refsect2>
27  * <para>
28  * This element lets you output sound using the WaveForm API.
29  * </para>
30  * <para>
31  * Note that you should almost always use generic audio conversion elements
32  * like audioconvert and audioresample in front of an audiosink to make sure
33  * your pipeline works under all circumstances (those conversion elements will
34  * act in passthrough-mode if no conversion is necessary).
35  * </para>
36  * <title>Example pipelines</title>
37  * <para>
38  * <programlisting>
39  * gst-launch-0.10 -v audiotestsrc ! audioconvert ! volume volume=0.1 ! waveformsink
40  * </programlisting>
41  * will output a sine wave (continuous beep sound) to your sound card (with
42  * a very low volume as precaution).
43  * </para>
44  * <para>
45  * <programlisting>
46  * gst-launch-0.10 -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! waveformsink
47  * </programlisting>
48  * will play an Ogg/Vorbis audio file and output it.
49  * </para>
50  * </refsect2>
51  */
52
53 #ifdef HAVE_CONFIG_H
54 #include "config.h"
55 #endif
56
57 #include "gstwaveformsink.h"
58
59 GST_DEBUG_CATEGORY_STATIC (waveformsink_debug);
60
61 /* elementfactory information */
62 static const GstElementDetails gst_waveform_sink_details =
63 GST_ELEMENT_DETAILS ("WaveForm Audio Sink",
64     "Sink/Audio",
65     "Output to a sound card via WaveForm API",
66     "Sebastien Moutte <sebastien@moutte.net>");
67
68 static void gst_waveform_sink_base_init (gpointer g_class);
69 static void gst_waveform_sink_class_init (GstWaveFormSinkClass * klass);
70 static void gst_waveform_sink_init (GstWaveFormSink * wfsink,
71     GstWaveFormSinkClass * g_class);
72 static void gst_waveform_sink_finalise (GObject * object);
73 static void gst_waveform_sink_set_property (GObject * object,
74     guint prop_id, const GValue * value, GParamSpec * pspec);
75 static void gst_waveform_sink_get_property (GObject * object,
76     guint prop_id, GValue * value, GParamSpec * pspec);
77 static GstCaps *gst_waveform_sink_getcaps (GstBaseSink * bsink);
78
79 /************************************************************************/
80 /* GstAudioSink functions                                               */
81 /************************************************************************/
82 static gboolean gst_waveform_sink_prepare (GstAudioSink * asink,
83     GstRingBufferSpec * spec);
84 static gboolean gst_waveform_sink_unprepare (GstAudioSink * asink);
85 static gboolean gst_waveform_sink_open (GstAudioSink * asink);
86 static gboolean gst_waveform_sink_close (GstAudioSink * asink);
87 static guint gst_waveform_sink_write (GstAudioSink * asink, gpointer data,
88     guint length);
89 static guint gst_waveform_sink_delay (GstAudioSink * asink);
90 static void gst_waveform_sink_reset (GstAudioSink * asink);
91
92 /************************************************************************/
93 /* Utils                                                                */
94 /************************************************************************/
95 GstCaps *gst_waveform_sink_create_caps (gint rate, gint channels,
96     gint bits_per_sample);
97 WAVEHDR *bufferpool_get_buffer (GstWaveFormSink * wfsink, gpointer data,
98     guint length);
99 void CALLBACK waveOutProc (HWAVEOUT hwo, UINT uMsg, unsigned long dwInstance,
100     DWORD dwParam1, DWORD dwParam2);
101
102 static GstStaticPadTemplate waveformsink_sink_factory =
103     GST_STATIC_PAD_TEMPLATE ("sink",
104     GST_PAD_SINK,
105     GST_PAD_ALWAYS,
106     GST_STATIC_CAPS ("audio/x-raw-int, "
107         "signed = (boolean) { TRUE, FALSE }, "
108         "width = (int) 16, "
109         "depth = (int) 16, "
110         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
111         "audio/x-raw-int, "
112         "signed = (boolean) { TRUE, FALSE }, "
113         "width = (int) 8, "
114         "depth = (int) 8, "
115         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]"));
116
117 GST_BOILERPLATE (GstWaveFormSink, gst_waveform_sink, GstAudioSink,
118     GST_TYPE_AUDIO_SINK);
119
120 static void
121 gst_waveform_sink_base_init (gpointer g_class)
122 {
123   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
124
125   gst_element_class_set_details (element_class, &gst_waveform_sink_details);
126   gst_element_class_add_pad_template (element_class,
127       gst_static_pad_template_get (&waveformsink_sink_factory));
128 }
129
130 static void
131 gst_waveform_sink_class_init (GstWaveFormSinkClass * klass)
132 {
133   GObjectClass *gobject_class;
134   GstElementClass *gstelement_class;
135   GstBaseSinkClass *gstbasesink_class;
136   GstBaseAudioSinkClass *gstbaseaudiosink_class;
137   GstAudioSinkClass *gstaudiosink_class;
138
139   gobject_class = (GObjectClass *) klass;
140   gstelement_class = (GstElementClass *) klass;
141   gstbasesink_class = (GstBaseSinkClass *) klass;
142   gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
143   gstaudiosink_class = (GstAudioSinkClass *) klass;
144
145   parent_class = g_type_class_peek_parent (klass);
146
147   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_waveform_sink_finalise);
148   gobject_class->get_property =
149       GST_DEBUG_FUNCPTR (gst_waveform_sink_get_property);
150   gobject_class->set_property =
151       GST_DEBUG_FUNCPTR (gst_waveform_sink_set_property);
152
153   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_waveform_sink_getcaps);
154
155   gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_waveform_sink_prepare);
156   gstaudiosink_class->unprepare =
157       GST_DEBUG_FUNCPTR (gst_waveform_sink_unprepare);
158   gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_waveform_sink_open);
159   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_waveform_sink_close);
160   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_waveform_sink_write);
161   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_waveform_sink_delay);
162   gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_waveform_sink_reset);
163
164   GST_DEBUG_CATEGORY_INIT (waveformsink_debug, "waveformsink", 0,
165       "Waveform sink");
166 }
167
168 static void
169 gst_waveform_sink_set_property (GObject * object, guint prop_id,
170     const GValue * value, GParamSpec * pspec)
171 {
172   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
173
174   switch (prop_id) {
175     default:
176       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177       break;
178   }
179 }
180
181 static void
182 gst_waveform_sink_get_property (GObject * object, guint prop_id,
183     GValue * value, GParamSpec * pspec)
184 {
185   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
186
187   switch (prop_id) {
188     default:
189       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
190       break;
191   }
192 }
193
194 static void
195 gst_waveform_sink_init (GstWaveFormSink * wfsink,
196     GstWaveFormSinkClass * g_class)
197 {
198   /* initialize members */
199   wfsink->hwaveout = NULL;
200   wfsink->cached_caps = NULL;
201   wfsink->wave_buffers = NULL;
202   wfsink->write_buffer = 0;
203   wfsink->buffer_count = BUFFER_COUNT;
204   wfsink->buffer_size = BUFFER_SIZE;
205   wfsink->free_buffers_count = wfsink->buffer_count;
206   wfsink->bytes_in_queue = 0;
207
208   InitializeCriticalSection (&wfsink->critic_wave);
209 }
210
211 static void
212 gst_waveform_sink_finalise (GObject * object)
213 {
214   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
215
216   if (wfsink->cached_caps) {
217     gst_caps_unref (wfsink->cached_caps);
218     wfsink->cached_caps = NULL;
219   }
220
221   DeleteCriticalSection (&wfsink->critic_wave);
222
223   G_OBJECT_CLASS (parent_class)->finalize (object);
224 }
225
226 static GstCaps *
227 gst_waveform_sink_getcaps (GstBaseSink * bsink)
228 {
229   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (bsink);
230   MMRESULT mmresult;
231   WAVEOUTCAPS wocaps;
232   GstCaps *caps, *caps_temp;
233
234   /* return the cached caps if already defined */
235   if (wfsink->cached_caps) {
236     return gst_caps_ref (wfsink->cached_caps);
237   }
238
239   /* get the default device caps */
240   mmresult = waveOutGetDevCaps (WAVE_MAPPER, &wocaps, sizeof (wocaps));
241   if (mmresult != MMSYSERR_NOERROR) {
242     waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
243     GST_ELEMENT_ERROR (wfsink, RESOURCE, SETTINGS,
244         ("gst_waveform_sink_getcaps: waveOutGetDevCaps failed error=>%s",
245             wfsink->error_string), (NULL));
246     return NULL;
247   }
248
249   caps = gst_caps_new_empty ();
250
251   /* create a caps for all wave formats supported by the device 
252      starting by the best quality format */
253   if (wocaps.dwFormats & WAVE_FORMAT_96S16) {
254     caps_temp = gst_waveform_sink_create_caps (96000, 2, 16);
255     if (caps_temp) {
256       gst_caps_append (caps, caps_temp);
257     }
258   }
259   if (wocaps.dwFormats & WAVE_FORMAT_96S08) {
260     caps_temp = gst_waveform_sink_create_caps (96000, 2, 8);
261     if (caps_temp) {
262       gst_caps_append (caps, caps_temp);
263     }
264   }
265   if (wocaps.dwFormats & WAVE_FORMAT_96M16) {
266     caps_temp = gst_waveform_sink_create_caps (96000, 1, 16);
267     if (caps_temp) {
268       gst_caps_append (caps, caps_temp);
269     }
270   }
271   if (wocaps.dwFormats & WAVE_FORMAT_96M08) {
272     caps_temp = gst_waveform_sink_create_caps (96000, 1, 8);
273     if (caps_temp) {
274       gst_caps_append (caps, caps_temp);
275     }
276   }
277   if (wocaps.dwFormats & WAVE_FORMAT_4S16) {
278     caps_temp = gst_waveform_sink_create_caps (44100, 2, 16);
279     if (caps_temp) {
280       gst_caps_append (caps, caps_temp);
281     }
282   }
283   if (wocaps.dwFormats & WAVE_FORMAT_4S08) {
284     caps_temp = gst_waveform_sink_create_caps (44100, 2, 8);
285     if (caps_temp) {
286       gst_caps_append (caps, caps_temp);
287     }
288   }
289   if (wocaps.dwFormats & WAVE_FORMAT_4M16) {
290     caps_temp = gst_waveform_sink_create_caps (44100, 1, 16);
291     if (caps_temp) {
292       gst_caps_append (caps, caps_temp);
293     }
294   }
295   if (wocaps.dwFormats & WAVE_FORMAT_4M08) {
296     caps_temp = gst_waveform_sink_create_caps (44100, 1, 8);
297     if (caps_temp) {
298       gst_caps_append (caps, caps_temp);
299     }
300   }
301   if (wocaps.dwFormats & WAVE_FORMAT_2S16) {
302     caps_temp = gst_waveform_sink_create_caps (22050, 2, 16);
303     if (caps_temp) {
304       gst_caps_append (caps, caps_temp);
305     }
306   }
307   if (wocaps.dwFormats & WAVE_FORMAT_2S08) {
308     caps_temp = gst_waveform_sink_create_caps (22050, 2, 8);
309     if (caps_temp) {
310       gst_caps_append (caps, caps_temp);
311     }
312   }
313   if (wocaps.dwFormats & WAVE_FORMAT_2M16) {
314     caps_temp = gst_waveform_sink_create_caps (22050, 1, 16);
315     if (caps_temp) {
316       gst_caps_append (caps, caps_temp);
317     }
318   }
319   if (wocaps.dwFormats & WAVE_FORMAT_2M08) {
320     caps_temp = gst_waveform_sink_create_caps (22050, 1, 8);
321     if (caps_temp) {
322       gst_caps_append (caps, caps_temp);
323     }
324   }
325   if (wocaps.dwFormats & WAVE_FORMAT_1S16) {
326     caps_temp = gst_waveform_sink_create_caps (11025, 2, 16);
327     if (caps_temp) {
328       gst_caps_append (caps, caps_temp);
329     }
330   }
331   if (wocaps.dwFormats & WAVE_FORMAT_1S08) {
332     caps_temp = gst_waveform_sink_create_caps (11025, 2, 8);
333     if (caps_temp) {
334       gst_caps_append (caps, caps_temp);
335     }
336   }
337   if (wocaps.dwFormats & WAVE_FORMAT_1M16) {
338     caps_temp = gst_waveform_sink_create_caps (11025, 1, 16);
339     if (caps_temp) {
340       gst_caps_append (caps, caps_temp);
341     }
342   }
343   if (wocaps.dwFormats & WAVE_FORMAT_1M08) {
344     caps_temp = gst_waveform_sink_create_caps (11025, 1, 8);
345     if (caps_temp) {
346       gst_caps_append (caps, caps_temp);
347     }
348   }
349
350   if (gst_caps_is_empty (caps)) {
351     gst_caps_unref (caps);
352     caps = NULL;
353   } else {
354     wfsink->cached_caps = gst_caps_ref (caps);
355   }
356
357   GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink, "Returning caps %s",
358       gst_caps_to_string (caps));
359
360   return caps;
361 }
362
363 static gboolean
364 gst_waveform_sink_open (GstAudioSink * asink)
365 {
366   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
367
368   /* nothing to do here as the device needs to be opened with the format we will use */
369
370   return TRUE;
371 }
372
373 static gboolean
374 gst_waveform_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
375 {
376   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
377   WAVEFORMATEX wfx;
378   MMRESULT mmresult;
379   guint index;
380
381   /* setup waveformex struture with the input ringbuffer specs */
382   memset (&wfx, 0, sizeof (wfx));
383   wfx.cbSize = 0;
384   wfx.wFormatTag = WAVE_FORMAT_PCM;
385   wfx.nChannels = spec->channels;
386   wfx.nSamplesPerSec = spec->rate;
387   wfx.wBitsPerSample = (spec->bytes_per_sample * 8) / wfx.nChannels;
388   wfx.nBlockAlign = spec->bytes_per_sample;
389   wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
390
391   /* save bytes per sample to use it in delay */
392   wfsink->bytes_per_sample = spec->bytes_per_sample;
393
394   /* open the default audio device with the given caps */
395   mmresult = waveOutOpen (&wfsink->hwaveout, WAVE_MAPPER,
396       &wfx, (DWORD) waveOutProc, (DWORD) wfsink, CALLBACK_FUNCTION);
397   if (mmresult != MMSYSERR_NOERROR) {
398     waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
399     GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
400         ("gst_waveform_sink_prepare: waveOutOpen failed error=>%s",
401             wfsink->error_string), (NULL));
402     return FALSE;
403   }
404
405   /* evaluate the buffer size and the number of buffers needed */
406   wfsink->free_buffers_count = wfsink->buffer_count;
407
408   /* allocate wave buffers */
409   wfsink->wave_buffers = (WAVEHDR *) g_new0 (WAVEHDR, wfsink->buffer_count);
410   if (!wfsink->wave_buffers) {
411     GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
412         ("gst_waveform_sink_prepare: Failed to allocate wave buffer headers (buffer count=%d)",
413             wfsink->buffer_count), (NULL));
414     return FALSE;
415   }
416   memset (wfsink->wave_buffers, 0, sizeof (WAVEHDR) * wfsink->buffer_count);
417
418   /* setup headers */
419   for (index = 0; index < wfsink->buffer_count; index++) {
420     wfsink->wave_buffers[index].dwBufferLength = wfsink->buffer_size;
421     wfsink->wave_buffers[index].lpData = g_new0 (gchar, wfsink->buffer_size);
422   }
423
424   return TRUE;
425 }
426
427 static gboolean
428 gst_waveform_sink_unprepare (GstAudioSink * asink)
429 {
430   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
431
432   /* free wave buffers */
433   if (wfsink->wave_buffers) {
434     guint index;
435
436     for (index = 0; index < wfsink->buffer_count; index++) {
437       if (wfsink->wave_buffers[index].dwFlags & WHDR_PREPARED) {
438         MMRESULT mmresult =
439             waveOutUnprepareHeader (wfsink->hwaveout,
440             &wfsink->wave_buffers[index], sizeof (WAVEHDR));
441         if (mmresult != MMSYSERR_NOERROR) {
442           waveOutGetErrorText (mmresult, wfsink->error_string,
443               ERROR_LENGTH - 1);
444           GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
445               "gst_waveform_sink_unprepare: Error unpreparing buffer => %s",
446               wfsink->error_string);
447         }
448       }
449       g_free (wfsink->wave_buffers[index].lpData);
450     }
451     g_free (wfsink->wave_buffers);
452     wfsink->wave_buffers = NULL;
453   }
454
455   /* close waveform-audio output device */
456   if (wfsink->hwaveout) {
457     waveOutClose (wfsink->hwaveout);
458     wfsink->hwaveout = NULL;
459   }
460
461   return TRUE;
462 }
463
464 static gboolean
465 gst_waveform_sink_close (GstAudioSink * asink)
466 {
467   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
468
469   return TRUE;
470 }
471
472 static guint
473 gst_waveform_sink_write (GstAudioSink * asink, gpointer data, guint length)
474 {
475   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
476   WAVEHDR *waveheader;
477   MMRESULT mmresult;
478   guint bytes_to_write = length;
479   guint remaining_length = length;
480
481   wfsink->bytes_in_queue += length;
482
483   while (remaining_length > 0) {
484     if (wfsink->free_buffers_count == 0) {
485       /* no free buffer available, wait for one */
486       Sleep (10);
487       continue;
488     }
489
490     /* get the current write buffer header */
491     waveheader = &wfsink->wave_buffers[wfsink->write_buffer];
492
493     /* unprepare the header if needed */
494     if (waveheader->dwFlags & WHDR_PREPARED) {
495       mmresult =
496           waveOutUnprepareHeader (wfsink->hwaveout, waveheader,
497           sizeof (WAVEHDR));
498       if (mmresult != MMSYSERR_NOERROR) {
499         waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
500         GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
501             "Error unpreparing buffer => %s", wfsink->error_string);
502       }
503     }
504
505     if (wfsink->buffer_size - waveheader->dwUser >= remaining_length)
506       bytes_to_write = remaining_length;
507     else
508       bytes_to_write = wfsink->buffer_size - waveheader->dwUser;
509
510     memcpy (waveheader->lpData + waveheader->dwUser, data, bytes_to_write);
511     waveheader->dwUser += bytes_to_write;
512     remaining_length -= bytes_to_write;
513     data = (byte *) data + bytes_to_write;
514
515     if (waveheader->dwUser == wfsink->buffer_size) {
516       /* we have filled a buffer, let's prepare it and next write it to the device */
517       mmresult =
518           waveOutPrepareHeader (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
519       if (mmresult != MMSYSERR_NOERROR) {
520         waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
521         GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
522             "gst_waveform_sink_write: Error preparing header => %s",
523             wfsink->error_string);
524       }
525       mmresult = waveOutWrite (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
526       if (mmresult != MMSYSERR_NOERROR) {
527         waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
528         GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
529             "gst_waveform_sink_write: Error writting buffer to the device => %s",
530             wfsink->error_string);
531       }
532
533       EnterCriticalSection (&wfsink->critic_wave);
534       wfsink->free_buffers_count--;
535       LeaveCriticalSection (&wfsink->critic_wave);
536
537       wfsink->write_buffer++;
538       wfsink->write_buffer %= wfsink->buffer_count;
539       waveheader->dwUser = 0;
540       wfsink->bytes_in_queue = 0;
541       GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink,
542           "gst_waveform_sink_write: Writting a buffer to the device (free buffers remaining=%d, write buffer=%d)",
543           wfsink->free_buffers_count, wfsink->write_buffer);
544     }
545   }
546
547   return length;
548 }
549
550 static guint
551 gst_waveform_sink_delay (GstAudioSink * asink)
552 {
553   /* return the number of samples in queue (device+internal queue) */
554   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
555   guint bytes_in_device =
556       (wfsink->buffer_count - wfsink->free_buffers_count) * wfsink->buffer_size;
557   guint delay =
558       (bytes_in_device + wfsink->bytes_in_queue) / wfsink->bytes_per_sample;
559   return delay;
560 }
561
562 static void
563 gst_waveform_sink_reset (GstAudioSink * asink)
564 {
565   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
566   MMRESULT mmresult = waveOutReset (wfsink->hwaveout);
567
568   if (mmresult != MMSYSERR_NOERROR) {
569     waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
570     GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
571         "gst_waveform_sink_reset: Error reseting waveform-audio device => %s",
572         wfsink->error_string);
573   }
574 }
575
576 GstCaps *
577 gst_waveform_sink_create_caps (gint rate, gint channels, gint bits_per_sample)
578 {
579   GstCaps *caps = NULL;
580
581   caps = gst_caps_new_simple ("audio/x-raw-int",
582       "width", G_TYPE_INT, bits_per_sample,
583       "depth", G_TYPE_INT, bits_per_sample,
584       "endianness", G_TYPE_INT, G_BYTE_ORDER,
585       "signed", G_TYPE_BOOLEAN, TRUE,
586       "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL);
587   return caps;
588 }
589
590 void CALLBACK
591 waveOutProc (HWAVEOUT hwo,
592     UINT uMsg, unsigned long dwInstance, DWORD dwParam1, DWORD dwParam2)
593 {
594   GstWaveFormSink *wfsink = (GstWaveFormSink *) dwInstance;
595
596   if (uMsg == WOM_DONE) {
597     EnterCriticalSection (&wfsink->critic_wave);
598     wfsink->free_buffers_count++;
599     LeaveCriticalSection (&wfsink->critic_wave);
600   }
601 }