matroska: add GstToc support for muxer
[platform/upstream/gstreamer.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  *
25  * This element lets you output sound using the Windows WaveForm API.
26  *
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).
31  *
32  * <refsect2>
33  * <title>Example pipelines</title>
34  * |[
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).
38  * |[
39  * gst-launch -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! waveformsink
40  * ]| will play an Ogg/Vorbis audio file and output it.
41  * </refsect2>
42  */
43
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
48 #include "gstwaveformsink.h"
49
50 GST_DEBUG_CATEGORY_STATIC (waveformsink_debug);
51
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);
58
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,
68     guint length);
69 static guint gst_waveform_sink_delay (GstAudioSink * asink);
70 static void gst_waveform_sink_reset (GstAudioSink * asink);
71
72 /************************************************************************/
73 /* Utils                                                                */
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,
78     guint length);
79 void CALLBACK waveOutProc (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
80     DWORD_PTR dwParam1, DWORD_PTR dwParam2);
81
82 static GstStaticPadTemplate waveformsink_sink_factory =
83     GST_STATIC_PAD_TEMPLATE ("sink",
84     GST_PAD_SINK,
85     GST_PAD_ALWAYS,
86     GST_STATIC_CAPS ("audio/x-raw-int, "
87         "signed = (boolean) { TRUE, FALSE }, "
88         "width = (int) 16, "
89         "depth = (int) 16, "
90         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
91         "audio/x-raw-int, "
92         "signed = (boolean) { TRUE, FALSE }, "
93         "width = (int) 8, "
94         "depth = (int) 8, "
95         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]"));
96
97 GST_BOILERPLATE (GstWaveFormSink, gst_waveform_sink, GstAudioSink,
98     GST_TYPE_AUDIO_SINK);
99
100 static void
101 gst_waveform_sink_base_init (gpointer g_class)
102 {
103   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
104
105   gst_element_class_set_details_simple (element_class, "WaveForm Audio Sink",
106       "Sink/Audio",
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);
111 }
112
113 static void
114 gst_waveform_sink_class_init (GstWaveFormSinkClass * klass)
115 {
116   GObjectClass *gobject_class;
117   GstElementClass *gstelement_class;
118   GstBaseSinkClass *gstbasesink_class;
119   GstBaseAudioSinkClass *gstbaseaudiosink_class;
120   GstAudioSinkClass *gstaudiosink_class;
121
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;
127
128   parent_class = g_type_class_peek_parent (klass);
129
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;
133
134   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_waveform_sink_getcaps);
135
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);
144
145   GST_DEBUG_CATEGORY_INIT (waveformsink_debug, "waveformsink", 0,
146       "Waveform sink");
147 }
148
149 static void
150 gst_waveform_sink_set_property (GObject * object, guint prop_id,
151     const GValue * value, GParamSpec * pspec)
152 {
153   /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); */
154
155   switch (prop_id) {
156     default:
157       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
158       break;
159   }
160 }
161
162 static void
163 gst_waveform_sink_get_property (GObject * object, guint prop_id,
164     GValue * value, GParamSpec * pspec)
165 {
166   /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); */
167
168   switch (prop_id) {
169     default:
170       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
171       break;
172   }
173 }
174
175 static void
176 gst_waveform_sink_init (GstWaveFormSink * wfsink,
177     GstWaveFormSinkClass * g_class)
178 {
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;
188
189   InitializeCriticalSection (&wfsink->critic_wave);
190 }
191
192 static void
193 gst_waveform_sink_finalise (GObject * object)
194 {
195   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
196
197   if (wfsink->cached_caps) {
198     gst_caps_unref (wfsink->cached_caps);
199     wfsink->cached_caps = NULL;
200   }
201
202   DeleteCriticalSection (&wfsink->critic_wave);
203
204   G_OBJECT_CLASS (parent_class)->finalize (object);
205 }
206
207 static GstCaps *
208 gst_waveform_sink_getcaps (GstBaseSink * bsink)
209 {
210   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (bsink);
211   MMRESULT mmresult;
212   WAVEOUTCAPS wocaps;
213   GstCaps *caps, *caps_temp;
214
215   /* return the cached caps if already defined */
216   if (wfsink->cached_caps) {
217     return gst_caps_ref (wfsink->cached_caps);
218   }
219
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));
227     return NULL;
228   }
229
230   caps = gst_caps_new_empty ();
231
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);
236     if (caps_temp) {
237       gst_caps_append (caps, caps_temp);
238     }
239   }
240   if (wocaps.dwFormats & WAVE_FORMAT_96S08) {
241     caps_temp = gst_waveform_sink_create_caps (96000, 2, 8);
242     if (caps_temp) {
243       gst_caps_append (caps, caps_temp);
244     }
245   }
246   if (wocaps.dwFormats & WAVE_FORMAT_96M16) {
247     caps_temp = gst_waveform_sink_create_caps (96000, 1, 16);
248     if (caps_temp) {
249       gst_caps_append (caps, caps_temp);
250     }
251   }
252   if (wocaps.dwFormats & WAVE_FORMAT_96M08) {
253     caps_temp = gst_waveform_sink_create_caps (96000, 1, 8);
254     if (caps_temp) {
255       gst_caps_append (caps, caps_temp);
256     }
257   }
258   if (wocaps.dwFormats & WAVE_FORMAT_4S16) {
259     caps_temp = gst_waveform_sink_create_caps (44100, 2, 16);
260     if (caps_temp) {
261       gst_caps_append (caps, caps_temp);
262     }
263   }
264   if (wocaps.dwFormats & WAVE_FORMAT_4S08) {
265     caps_temp = gst_waveform_sink_create_caps (44100, 2, 8);
266     if (caps_temp) {
267       gst_caps_append (caps, caps_temp);
268     }
269   }
270   if (wocaps.dwFormats & WAVE_FORMAT_4M16) {
271     caps_temp = gst_waveform_sink_create_caps (44100, 1, 16);
272     if (caps_temp) {
273       gst_caps_append (caps, caps_temp);
274     }
275   }
276   if (wocaps.dwFormats & WAVE_FORMAT_4M08) {
277     caps_temp = gst_waveform_sink_create_caps (44100, 1, 8);
278     if (caps_temp) {
279       gst_caps_append (caps, caps_temp);
280     }
281   }
282   if (wocaps.dwFormats & WAVE_FORMAT_2S16) {
283     caps_temp = gst_waveform_sink_create_caps (22050, 2, 16);
284     if (caps_temp) {
285       gst_caps_append (caps, caps_temp);
286     }
287   }
288   if (wocaps.dwFormats & WAVE_FORMAT_2S08) {
289     caps_temp = gst_waveform_sink_create_caps (22050, 2, 8);
290     if (caps_temp) {
291       gst_caps_append (caps, caps_temp);
292     }
293   }
294   if (wocaps.dwFormats & WAVE_FORMAT_2M16) {
295     caps_temp = gst_waveform_sink_create_caps (22050, 1, 16);
296     if (caps_temp) {
297       gst_caps_append (caps, caps_temp);
298     }
299   }
300   if (wocaps.dwFormats & WAVE_FORMAT_2M08) {
301     caps_temp = gst_waveform_sink_create_caps (22050, 1, 8);
302     if (caps_temp) {
303       gst_caps_append (caps, caps_temp);
304     }
305   }
306   if (wocaps.dwFormats & WAVE_FORMAT_1S16) {
307     caps_temp = gst_waveform_sink_create_caps (11025, 2, 16);
308     if (caps_temp) {
309       gst_caps_append (caps, caps_temp);
310     }
311   }
312   if (wocaps.dwFormats & WAVE_FORMAT_1S08) {
313     caps_temp = gst_waveform_sink_create_caps (11025, 2, 8);
314     if (caps_temp) {
315       gst_caps_append (caps, caps_temp);
316     }
317   }
318   if (wocaps.dwFormats & WAVE_FORMAT_1M16) {
319     caps_temp = gst_waveform_sink_create_caps (11025, 1, 16);
320     if (caps_temp) {
321       gst_caps_append (caps, caps_temp);
322     }
323   }
324   if (wocaps.dwFormats & WAVE_FORMAT_1M08) {
325     caps_temp = gst_waveform_sink_create_caps (11025, 1, 8);
326     if (caps_temp) {
327       gst_caps_append (caps, caps_temp);
328     }
329   }
330
331   if (gst_caps_is_empty (caps)) {
332     gst_caps_unref (caps);
333     caps = NULL;
334   } else {
335     wfsink->cached_caps = gst_caps_ref (caps);
336   }
337
338   GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink, "Returning caps %s",
339       gst_caps_to_string (caps));
340
341   return caps;
342 }
343
344 static gboolean
345 gst_waveform_sink_open (GstAudioSink * asink)
346 {
347   /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); */
348
349   /* nothing to do here as the device needs to be opened with the format we will use */
350
351   return TRUE;
352 }
353
354 static gboolean
355 gst_waveform_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
356 {
357   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
358   WAVEFORMATEX wfx;
359   MMRESULT mmresult;
360   guint index;
361
362   /* setup waveformex struture with the input ringbuffer specs */
363   memset (&wfx, 0, sizeof (wfx));
364   wfx.cbSize = 0;
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;
371
372   /* save bytes per sample to use it in delay */
373   wfsink->bytes_per_sample = spec->bytes_per_sample;
374
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));
383     return FALSE;
384   }
385
386   /* evaluate the buffer size and the number of buffers needed */
387   wfsink->free_buffers_count = wfsink->buffer_count;
388
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));
395     return FALSE;
396   }
397   memset (wfsink->wave_buffers, 0, sizeof (WAVEHDR) * wfsink->buffer_count);
398
399   /* setup headers */
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);
403   }
404
405   return TRUE;
406 }
407
408 static gboolean
409 gst_waveform_sink_unprepare (GstAudioSink * asink)
410 {
411   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
412
413   /* free wave buffers */
414   if (wfsink->wave_buffers) {
415     guint index;
416
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,
423               ERROR_LENGTH - 1);
424           GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
425               "gst_waveform_sink_unprepare: Error unpreparing buffer => %s",
426               wfsink->error_string);
427         }
428       }
429       g_free (wfsink->wave_buffers[index].lpData);
430     }
431     g_free (wfsink->wave_buffers);
432     wfsink->wave_buffers = NULL;
433   }
434
435   /* close waveform-audio output device */
436   if (wfsink->hwaveout) {
437     waveOutClose (wfsink->hwaveout);
438     wfsink->hwaveout = NULL;
439   }
440
441   return TRUE;
442 }
443
444 static gboolean
445 gst_waveform_sink_close (GstAudioSink * asink)
446 {
447   /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); */
448
449   return TRUE;
450 }
451
452 static guint
453 gst_waveform_sink_write (GstAudioSink * asink, gpointer data, guint length)
454 {
455   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
456   WAVEHDR *waveheader;
457   MMRESULT mmresult;
458   guint bytes_to_write = length;
459   guint remaining_length = length;
460
461   wfsink->bytes_in_queue += length;
462
463   while (remaining_length > 0) {
464     if (wfsink->free_buffers_count == 0) {
465       /* no free buffer available, wait for one */
466       Sleep (10);
467       continue;
468     }
469
470     /* get the current write buffer header */
471     waveheader = &wfsink->wave_buffers[wfsink->write_buffer];
472
473     /* unprepare the header if needed */
474     if (waveheader->dwFlags & WHDR_PREPARED) {
475       mmresult =
476           waveOutUnprepareHeader (wfsink->hwaveout, waveheader,
477           sizeof (WAVEHDR));
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);
482       }
483     }
484
485     if (wfsink->buffer_size - waveheader->dwUser >= remaining_length)
486       bytes_to_write = remaining_length;
487     else
488       bytes_to_write = wfsink->buffer_size - waveheader->dwUser;
489
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;
494
495     if (waveheader->dwUser == wfsink->buffer_size) {
496       /* we have filled a buffer, let's prepare it and next write it to the device */
497       mmresult =
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);
504       }
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);
511       }
512
513       EnterCriticalSection (&wfsink->critic_wave);
514       wfsink->free_buffers_count--;
515       LeaveCriticalSection (&wfsink->critic_wave);
516
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);
524     }
525   }
526
527   return length;
528 }
529
530 static guint
531 gst_waveform_sink_delay (GstAudioSink * asink)
532 {
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;
537   guint delay =
538       (bytes_in_device + wfsink->bytes_in_queue) / wfsink->bytes_per_sample;
539   return delay;
540 }
541
542 static void
543 gst_waveform_sink_reset (GstAudioSink * asink)
544 {
545   GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
546   MMRESULT mmresult = waveOutReset (wfsink->hwaveout);
547
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);
553   }
554 }
555
556 GstCaps *
557 gst_waveform_sink_create_caps (gint rate, gint channels, gint bits_per_sample)
558 {
559   GstCaps *caps = NULL;
560
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);
567   return caps;
568 }
569
570 void CALLBACK
571 waveOutProc (HWAVEOUT hwo,
572     UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
573 {
574   GstWaveFormSink *wfsink = (GstWaveFormSink *) dwInstance;
575
576   if (uMsg == WOM_DONE) {
577     EnterCriticalSection (&wfsink->critic_wave);
578     wfsink->free_buffers_count++;
579     LeaveCriticalSection (&wfsink->critic_wave);
580   }
581 }