Initial import to Tizen
[profile/ivi/pocketsphinx.git] / src / gst-plugin / gstvader.c
1 /* ====================================================================
2  * Copyright (c) 1999-2010 Carnegie Mellon University.  All rights
3  * reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer. 
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * This work was supported in part by funding from the Defense Advanced 
18  * Research Projects Agency and the National Science Foundation of the 
19  * United States of America, and the CMU Sphinx Speech Consortium.
20  *
21  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND 
22  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
23  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
25  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * ====================================================================
34  */
35
36 /*
37  * Modified version of the "cutter" element to do better at VAD.
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43 #include <string.h>
44 #include <gst/gst.h>
45 #include <gst/audio/audio.h>
46 #include "gstvader.h"
47 #include "math.h"
48
49 GST_DEBUG_CATEGORY_STATIC(vader_debug);
50 #define GST_CAT_DEFAULT vader_debug
51
52 static const GstElementDetails vader_details =
53     GST_ELEMENT_DETAILS("VAD element",
54                         "Filter/Editor/Audio",
55                         "Voice Activity DEtectoR to split audio into non-silent bits",
56                         "Thomas <thomas@apestaart.org>, David Huggins-Daines <dhuggins@cs.cmu.edu>");
57
58 static GstStaticPadTemplate vader_src_factory =
59     GST_STATIC_PAD_TEMPLATE("src",
60                             GST_PAD_SRC,
61                             GST_PAD_ALWAYS,
62                             GST_STATIC_CAPS("audio/x-raw-int, "
63                                             "rate = (int) [ 1, MAX ], "
64                                             "channels = (int) [ 1, MAX ], "
65                                             "endianness = (int) BYTE_ORDER, "
66                                             "width = (int) 16, "
67                                             "depth = (int) 16, "
68                                             "signed = (boolean) true")
69         );
70
71 static GstStaticPadTemplate vader_sink_factory =
72     GST_STATIC_PAD_TEMPLATE("sink",
73                             GST_PAD_SINK,
74                             GST_PAD_ALWAYS,
75                             GST_STATIC_CAPS("audio/x-raw-int, "
76                                             /* FIXME: Actually we want this to be negotiable... */
77                                             "rate = (int) 8000, "
78                                             "channels = (int) 1, "
79                                             "endianness = (int) BYTE_ORDER, "
80                                             "width = (int) 16, "
81                                             "depth = (int) 16, "
82                                             "signed = (boolean) true")
83         );
84
85 enum
86 {
87     SIGNAL_VADER_START,
88     SIGNAL_VADER_STOP,
89     LAST_SIGNAL
90 };
91
92 static guint gst_vader_signals[LAST_SIGNAL];
93
94 enum
95 {
96     PROP_0,
97     PROP_THRESHOLD,
98     PROP_AUTO_THRESHOLD,
99     PROP_RUN_LENGTH,
100     PROP_PRE_LENGTH,
101     PROP_SILENT,
102     PROP_DUMPDIR
103 };
104
105 GST_BOILERPLATE(GstVader, gst_vader, GstElement, GST_TYPE_ELEMENT);
106
107 static void gst_vader_set_property(GObject * object, guint prop_id,
108                                    const GValue * value, GParamSpec * pspec);
109 static void gst_vader_get_property(GObject * object, guint prop_id,
110                                    GValue * value, GParamSpec * pspec);
111 static void gst_vader_finalize(GObject *gobject);
112
113 static GstFlowReturn gst_vader_chain(GstPad * pad, GstBuffer * buffer);
114
115 static void
116 gst_vader_base_init(gpointer g_class)
117 {
118     GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
119
120     gst_element_class_add_pad_template(element_class,
121                                        gst_static_pad_template_get(&vader_src_factory));
122     gst_element_class_add_pad_template(element_class,
123                                        gst_static_pad_template_get(&vader_sink_factory));
124     gst_element_class_set_details(element_class, &vader_details);
125 }
126
127 static void
128 gst_vader_class_init(GstVaderClass * klass)
129 {
130     GObjectClass *gobject_class;
131     GstElementClass *gstelement_class;
132
133     gobject_class = (GObjectClass *) klass;
134     gstelement_class = (GstElementClass *) klass;
135
136     gobject_class->set_property = gst_vader_set_property;
137     gobject_class->get_property = gst_vader_get_property;
138     gobject_class->finalize = gst_vader_finalize;
139
140     g_object_class_install_property
141         (G_OBJECT_CLASS(klass), PROP_THRESHOLD,
142          g_param_spec_double("threshold", "Threshold",
143                              "Volume threshold for speech/silence decision",
144                              -1.0, 1.0, 256.0/32768.0, G_PARAM_READWRITE));
145     g_object_class_install_property
146         (G_OBJECT_CLASS(klass), PROP_AUTO_THRESHOLD,
147          g_param_spec_boolean("auto-threshold", "Automatic Threshold",
148                              "Set speech/silence threshold automatically",
149                              FALSE, G_PARAM_READWRITE));
150     g_object_class_install_property
151         (G_OBJECT_CLASS(klass), PROP_RUN_LENGTH,
152          g_param_spec_uint64("run-length", "Run length",
153                              "Length of drop below threshold before cut_stop (in nanoseconds)",
154                              0, G_MAXUINT64, (guint64)(0.5 * GST_SECOND), G_PARAM_READWRITE));
155     g_object_class_install_property
156         (G_OBJECT_CLASS(klass), PROP_PRE_LENGTH,
157          g_param_spec_uint64("pre-length", "Pre-recording buffer length",
158                              "Length of pre-recording buffer (in nanoseconds)",
159                              0, G_MAXUINT64, (guint64)(0.5 * GST_SECOND), G_PARAM_READWRITE));
160     g_object_class_install_property
161         (G_OBJECT_CLASS(klass), PROP_SILENT,
162          g_param_spec_boolean("silent", "Silent",
163                              "Whether the VADER is currently in a silence region",
164                               TRUE, G_PARAM_READWRITE));
165     g_object_class_install_property
166         (gobject_class, PROP_DUMPDIR,
167          g_param_spec_string("dump-dir", "Audio dump directory",
168                              "Directory in which to write audio segments for debugging",
169                              NULL,
170                              G_PARAM_READWRITE));
171
172     gst_vader_signals[SIGNAL_VADER_START] = 
173         g_signal_new("vader_start",
174                      G_TYPE_FROM_CLASS(klass),
175                      G_SIGNAL_RUN_LAST,
176                      G_STRUCT_OFFSET(GstVaderClass, vader_start),
177                      NULL, NULL,
178                      gst_marshal_VOID__INT64,
179                      G_TYPE_NONE,
180                      1, G_TYPE_UINT64
181             );
182
183     gst_vader_signals[SIGNAL_VADER_STOP] = 
184         g_signal_new("vader_stop",
185                      G_TYPE_FROM_CLASS(klass),
186                      G_SIGNAL_RUN_LAST,
187                      G_STRUCT_OFFSET(GstVaderClass, vader_stop),
188                      NULL, NULL,
189                      gst_marshal_VOID__INT64,
190                      G_TYPE_NONE,
191                      1, G_TYPE_UINT64
192             );
193
194     GST_DEBUG_CATEGORY_INIT(vader_debug, "vader", 0, "Voice Activity Detection");
195 }
196
197 static void
198 gst_vader_init(GstVader * filter, GstVaderClass * g_class)
199 {
200     filter->sinkpad =
201         gst_pad_new_from_static_template(&vader_sink_factory, "sink");
202     filter->srcpad =
203         gst_pad_new_from_static_template(&vader_src_factory, "src");
204
205     g_static_rec_mutex_init(&filter->mtx);
206
207     filter->threshold_level = 256;
208     filter->threshold_length = (guint64)(0.5 * GST_SECOND);
209     filter->prior_sample = 0;
210     filter->auto_threshold = FALSE;
211     filter->silence_mean = 0;
212     filter->silence_stddev = 0;
213     filter->silence_frames = 0;
214     filter->dumpdir = NULL;
215     filter->dumpfile = NULL;
216     filter->dumpidx = 0;
217
218     memset(filter->window, 0, VADER_WINDOW * sizeof(*filter->window));
219     filter->silent = TRUE;
220     filter->silent_prev = TRUE;
221     filter->silent_run_length = 0;
222
223     filter->pre_buffer = NULL;
224     filter->pre_length = (guint64)(0.5 * GST_SECOND);
225     filter->pre_run_length = 0;
226
227     gst_element_add_pad(GST_ELEMENT(filter), filter->sinkpad);
228     gst_pad_set_chain_function(filter->sinkpad, gst_vader_chain);
229     gst_pad_use_fixed_caps(filter->sinkpad);
230
231     gst_element_add_pad(GST_ELEMENT(filter), filter->srcpad);
232     gst_pad_use_fixed_caps(filter->srcpad);
233 }
234
235 static void
236 gst_vader_finalize(GObject *gobject)
237 {
238     GstVader *vader = GST_VADER(gobject);
239
240     g_static_rec_mutex_free(&vader->mtx);
241     if (vader->dumpfile)
242         fclose(vader->dumpfile);
243     if (vader->dumpdir)
244         g_free(vader->dumpdir);
245     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (gobject));
246 }
247
248 static GstMessage *
249 gst_vader_message_new(GstVader * c, gboolean above, GstClockTime timestamp)
250 {
251     GstStructure *s;
252
253     s = gst_structure_new("vader",
254                           "above", G_TYPE_BOOLEAN, above,
255                           "timestamp", GST_TYPE_CLOCK_TIME, timestamp, NULL);
256
257     return gst_message_new_element(GST_OBJECT(c), s);
258 }
259
260 static GstEvent *
261 gst_vader_event_new(GstVader *c, GstEventType type, GstClockTime timestamp)
262 {
263     GstEvent *e;
264
265     e = gst_event_new_custom(type, NULL);
266     GST_EVENT_TIMESTAMP(e) = timestamp;
267
268     return e;
269 }
270
271 static guint
272 compute_normed_power(gint16 *in_data, guint num_samples, gint *inout_prior)
273 {
274     guint i, shift, sumsq, prior;
275
276     sumsq = 0;
277     shift = 0;
278     prior = *inout_prior;
279     for (i = 0; i < num_samples; ++i) {
280         guint sq;
281         gint x;
282
283         /* Do pre-emphasis to remove low-frequency noise (this should
284          * be sufficient, although ideally we'd band-pass filter the
285          * data from about 200 to 6000Hz) */
286         x = in_data[i] - prior;
287         prior = in_data[i];
288         sq = x * x;
289         sumsq += (sq >> shift);
290         /* Prevent overflows. */
291         while (sumsq > 0x10000) {
292             sumsq >>= 1;
293             shift += 1;
294         }
295     }
296     *inout_prior = prior;
297
298     /* Normalize it to Q15 (this is equivalent to dividing by (1<<30)
299      * then multiplying by (1<<15)). */
300     if (shift > 15)
301         return (sumsq << (shift - 15)) / num_samples;
302     else
303         return (sumsq / num_samples) >> (15 - shift);
304 }
305
306 /**
307  * Calculate Q15 square root z of a Q15 number x.
308  *
309  * This is equal to 32768 \sqrt(\frac{x}{32768}), which is equal to
310  * 2^{7.5} x^{0.5}, so:
311  *
312  * If y = log_2(x)
313  * z = 2^{7.5 + 0.5y} = 2^{7.5 + 0.5y_{odd}} 2^{0.5y_{remainder})
314  *   = 2^{7.5 + 0.5y_{odd}} + 2^{7.5 + 0.5y_{odd}} (2^{0.5y_{remainder}) - 1)
315  *
316  * Therefore the factor 2^{0.5y_{remainder}) - 1 can be stored in a
317  * table.  Since 0 <= y_{remainder} < 2, this table has size 2^N -
318  * 2^{N-2} for some value of N (7 is a pretty good one...)
319  */
320 #define REMTAB_SIZE 96
321 static const guint16 remtab[REMTAB_SIZE] = {
322 0, 508, 1008, 1501, 1987, 2467, 2940, 3406, 3867, 4322, 4772, 5216, 5655, 6090, 6519, 6944, 7364, 7780, 8191, 8599, 9003, 9402, 9798, 10191, 10579, 10965, 11347, 11725, 12101, 12473, 12843, 13209, 13572, 13933, 14291, 14646, 14999, 15349, 15696, 16041, 16384, 16724, 17061, 17397, 17730, 18062, 18391, 18717, 19042, 19365, 19686, 20005, 20322, 20637, 20950, 21261, 21571, 21879, 22185, 22490, 22792, 23093, 23393, 23691, 23987, 24282, 24576, 24867, 25158, 25447, 25734, 26020, 26305, 26588, 26870, 27151, 27430, 27708, 27985, 28261, 28535, 28808, 29080, 29350, 29620, 29888, 30155, 30422, 30686, 30950, 31213, 31475, 31735, 31995, 32253, 32511
323 };
324 static guint
325 fixpoint_sqrt_q15(guint x)
326 {
327     guint z;
328     int log2, scale, idx;
329
330     /* 0 and one are special cases since they have no closest odd
331      * power of 2. */
332     if (x == 0)
333         return 0;
334     else if (x == 1)
335         return 181; /* 32768 * sqrt(1.0/32768) */
336
337     /* Compute nearest log2. */
338     for (log2 = 31; log2 > 0; --log2)
339         if (x & (1<<log2))
340             break;
341     /* Find nearest odd log2. */
342     if ((log2 & 1) == 0)
343         log2 -= 1;
344     /* Find index into remtab. */
345     idx = x - (1<<log2);
346     /* Scale it to fit remtab. */
347     scale = (1<<(log2 + 2)) - (1<<log2);
348     idx = idx * REMTAB_SIZE / scale;
349     /* Base of square root. */
350     z = 1<<(8 + log2 / 2);
351     /* Add remainder. */
352     return z + ((z * remtab[idx]) >> 15);
353 }
354
355 /**
356  * Very approximate fixed-point square root (for big numbers only!)
357  *
358  * Really simple, sqrt(x) = 2^{\frac{\log_2 x}{2}}.  So approximate
359  * \log_2 x, then divide it by two, and exponentiate :)
360  */
361 static guint
362 fixpoint_bogus_sqrt(guint x)
363 {
364     int log2;
365
366     /* Compute nearest log2. */
367     for (log2 = 31; log2 > 0; --log2)
368         if (x & (1<<log2))
369             break;
370     /* Return "square root" */
371     return 1<<(log2/2+1);
372 }
373
374 static void
375 gst_vader_transition(GstVader *filter, GstClockTime ts)
376 {
377     /* NOTE: This function MUST be called with filter->mtx held! */
378     /* has the silent status changed ? if so, send right signal
379      * and, if from silent -> not silent, flush pre_record buffer
380      */
381     if (filter->silent) {
382         /* Sound to silence transition. */
383         GstMessage *m =
384             gst_vader_message_new(filter, FALSE, ts);
385         GstEvent *e =
386             gst_vader_event_new(filter, GST_EVENT_VADER_STOP, ts);
387         GST_DEBUG_OBJECT(filter, "signaling CUT_STOP");
388         gst_element_post_message(GST_ELEMENT(filter), m);
389         /* Insert a custom event in the stream to mark the end of a cut. */
390         /* This will block if the pipeline is paused so we have to unlock. */
391         g_static_rec_mutex_unlock(&filter->mtx);
392         gst_pad_push_event(filter->srcpad, e);
393         g_static_rec_mutex_lock(&filter->mtx);
394         /* FIXME: That event's timestamp is wrong... as is this one. */
395         g_signal_emit(filter, gst_vader_signals[SIGNAL_VADER_STOP], 0, ts);
396         /* Stop dumping audio */
397         if (filter->dumpfile) {
398             fclose(filter->dumpfile);
399             filter->dumpfile = NULL;
400             ++filter->dumpidx;
401         }
402     } else {
403         /* Silence to sound transition. */
404         gint count = 0;
405         GstMessage *m;
406         GstEvent *e;
407
408         GST_DEBUG_OBJECT(filter, "signaling CUT_START");
409         /* Use the first pre_buffer's timestamp for the signal if possible. */
410         if (filter->pre_buffer) {
411             GstBuffer *prebuf;
412
413             prebuf = (g_list_first(filter->pre_buffer))->data;
414             ts = GST_BUFFER_TIMESTAMP(prebuf);
415         }
416
417         g_signal_emit(filter, gst_vader_signals[SIGNAL_VADER_START],
418                       0, ts);
419         m = gst_vader_message_new(filter, TRUE, ts);
420         e = gst_vader_event_new(filter, GST_EVENT_VADER_START, ts);
421         gst_element_post_message(GST_ELEMENT(filter), m);
422
423         /* Insert a custom event in the stream to mark the beginning of a cut. */
424         /* This will block if the pipeline is paused so we have to unlock. */
425         g_static_rec_mutex_unlock(&filter->mtx);
426         gst_pad_push_event(filter->srcpad, e);
427         g_static_rec_mutex_lock(&filter->mtx);
428
429         /* Start dumping audio */
430         if (filter->dumpdir) {
431             gchar *filename = g_strdup_printf("%s/%08d.raw", filter->dumpdir,
432                                               filter->dumpidx);
433             filter->dumpfile = fopen(filename, "wb");
434             g_free(filename);
435         }
436
437         /* first of all, flush current buffer */
438         GST_DEBUG_OBJECT(filter, "flushing buffer of length %" GST_TIME_FORMAT,
439                          GST_TIME_ARGS(filter->pre_run_length));
440         while (filter->pre_buffer) {
441             GstBuffer *prebuf;
442
443             prebuf = (g_list_first(filter->pre_buffer))->data;
444             filter->pre_buffer = g_list_remove(filter->pre_buffer, prebuf);
445             if (filter->dumpfile)
446                 fwrite(GST_BUFFER_DATA(prebuf), 1, GST_BUFFER_SIZE(prebuf),
447                        filter->dumpfile);
448             /* This will block if the pipeline is paused so we have to unlock. */
449             g_static_rec_mutex_unlock(&filter->mtx);
450             gst_pad_push(filter->srcpad, prebuf);
451             g_static_rec_mutex_lock(&filter->mtx);
452             ++count;
453         }
454         GST_DEBUG_OBJECT(filter, "flushed %d buffers", count);
455         filter->pre_run_length = 0;
456     }
457 }
458
459
460 static GstFlowReturn
461 gst_vader_chain(GstPad * pad, GstBuffer * buf)
462 {
463     GstVader *filter;
464     gint16 *in_data;
465     guint num_samples;
466     gint i, vote;
467     guint power, rms;
468
469     g_return_val_if_fail(pad != NULL, GST_FLOW_ERROR);
470     g_return_val_if_fail(GST_IS_PAD(pad), GST_FLOW_ERROR);
471     g_return_val_if_fail(buf != NULL, GST_FLOW_ERROR);
472
473     filter = GST_VADER(GST_OBJECT_PARENT(pad));
474     g_return_val_if_fail(filter != NULL, GST_FLOW_ERROR);
475     g_return_val_if_fail(GST_IS_VADER(filter), GST_FLOW_ERROR);
476
477     in_data = (gint16 *) GST_BUFFER_DATA(buf);
478     num_samples = GST_BUFFER_SIZE(buf) / 2;
479
480     /* Enter a critical section. */
481     g_static_rec_mutex_lock(&filter->mtx);
482     filter->silent_prev = filter->silent;
483     /* If we are in auto-threshold mode, check to see if we have
484      * enough data to estimate a threshold.  (FIXME: we should be
485      * estimating at the sample level rather than the frame level,
486      * probably) */
487     if (filter->threshold_level == -1) {
488         if (filter->silence_frames > 5) {
489             filter->silence_mean /= filter->silence_frames;
490             filter->silence_stddev /= filter->silence_frames;
491             filter->silence_stddev -= filter->silence_mean * filter->silence_mean;
492             filter->silence_stddev = fixpoint_bogus_sqrt(filter->silence_stddev);
493             /* Set threshold three standard deviations from the mean. */
494             filter->threshold_level = filter->silence_mean + 3 * filter->silence_stddev;
495             GST_DEBUG_OBJECT(filter, "silence_mean %d stddev %d auto_threshold %d\n",
496                              filter->silence_mean, filter->silence_stddev,
497                              filter->threshold_level);
498         }
499     }
500
501     /* Divide buffer into reasonably sized parts. */
502     for (i = 0; i < num_samples; i += VADER_FRAME) {
503         gint frame_len, j;
504
505         frame_len = MIN(num_samples - i, VADER_FRAME);
506         power = compute_normed_power(in_data + i, frame_len, &filter->prior_sample);
507         rms = fixpoint_sqrt_q15(power);
508
509         /* If we are in auto-threshold mode, don't do any voting etc. */
510         if (filter->threshold_level == -1) {
511             filter->silence_mean += rms;
512             filter->silence_stddev += rms * rms;
513             filter->silence_frames += 1;
514             GST_DEBUG_OBJECT(filter, "silence_mean_acc %d silence_stddev_acc %d frames %d\n",
515                              filter->silence_mean, filter->silence_stddev, filter->silence_frames);
516             continue;
517         }
518         /* Shift back window values. */
519         memmove(filter->window, filter->window + 1,
520                 (VADER_WINDOW - 1) * sizeof(*filter->window));
521
522         /* Decide if this buffer is silence or not. */
523         if (rms > filter->threshold_level)
524             filter->window[VADER_WINDOW-1] = TRUE;
525         else
526             filter->window[VADER_WINDOW-1] = FALSE;
527
528         /* Vote on whether we have entered a region of non-silence. */
529         vote = 0;
530         for (j = 0; j < VADER_WINDOW; ++j)
531             vote += filter->window[j];
532
533         GST_DEBUG_OBJECT(filter, "frame_len %d rms power %d threshold %d vote %d\n",
534                          frame_len, rms, filter->threshold_level, vote);
535
536         if (vote > VADER_WINDOW / 2) {
537             filter->silent_run_length = 0;
538             filter->silent = FALSE;
539         }
540         else {
541             filter->silent_run_length
542                 += gst_audio_duration_from_pad_buffer(filter->sinkpad, buf);
543         }
544
545         if (filter->silent_run_length > filter->threshold_length)
546             /* it has been silent long enough, flag it */
547             filter->silent = TRUE;
548     }
549
550     /* Handle transitions between silence and non-silence. */
551     if (filter->silent != filter->silent_prev) {
552         gst_vader_transition(filter, GST_BUFFER_TIMESTAMP(buf));
553     }
554     /* Handling of silence detection is done. */
555     g_static_rec_mutex_unlock(&filter->mtx);
556
557     /* now check if we have to send the new buffer to the internal buffer cache
558      * or to the srcpad */
559     if (filter->silent) {
560         /* Claim the lock while manipulating the queue. */
561         g_static_rec_mutex_lock(&filter->mtx);
562         filter->pre_buffer = g_list_append(filter->pre_buffer, buf);
563         filter->pre_run_length +=
564             gst_audio_duration_from_pad_buffer(filter->sinkpad, buf);
565         while (filter->pre_run_length > filter->pre_length) {
566             GstBuffer *prebuf;
567
568             prebuf = (g_list_first(filter->pre_buffer))->data;
569             g_assert(GST_IS_BUFFER(prebuf));
570             filter->pre_buffer = g_list_remove(filter->pre_buffer, prebuf);
571             filter->pre_run_length -=
572                 gst_audio_duration_from_pad_buffer(filter->sinkpad, prebuf);
573             gst_buffer_unref(prebuf);
574         }
575         g_static_rec_mutex_unlock(&filter->mtx);
576     } else {
577         if (filter->dumpfile)
578             fwrite(GST_BUFFER_DATA(buf), 1, GST_BUFFER_SIZE(buf),
579                    filter->dumpfile);
580         gst_pad_push(filter->srcpad, buf);
581     }
582
583     return GST_FLOW_OK;
584 }
585
586 static void
587 gst_vader_set_property(GObject * object, guint prop_id,
588                        const GValue * value, GParamSpec * pspec)
589 {
590     GstVader *filter;
591
592     g_return_if_fail(GST_IS_VADER(object));
593     filter = GST_VADER(object);
594
595     switch (prop_id) {
596     case PROP_THRESHOLD:
597         filter->threshold_level = (gint)(g_value_get_double(value) * 32768.0);
598         break;
599     case PROP_AUTO_THRESHOLD:
600         /* We are going to muck around with things... */
601         g_static_rec_mutex_lock(&filter->mtx);
602         filter->auto_threshold = g_value_get_boolean(value);
603         /* Setting this to TRUE re-initializes auto calibration. */
604         if (filter->auto_threshold) {
605             /* We have to be in silence mode to calibrate. */
606             filter->silent_prev = filter->silent;
607             filter->silent = TRUE;
608             /* Do "artifical" sil-speech or speech-sil transitions. */
609             if (filter->silent != filter->silent_prev) {
610                 gst_vader_transition(filter, gst_clock_get_time(GST_ELEMENT_CLOCK(filter)));
611             }
612             /* Reset counters and such. */
613             filter->threshold_level = -1;
614             memset(filter->window, 0, sizeof(*filter->window) * VADER_WINDOW);
615             filter->silence_mean = 0;
616             filter->silence_stddev = 0;
617             filter->silence_frames = 0;
618         }
619         g_static_rec_mutex_unlock(&filter->mtx);
620         break;
621     case PROP_SILENT:
622         /* We are going to muck around with things... */
623         g_static_rec_mutex_lock(&filter->mtx);
624         filter->silent_prev = filter->silent;
625         filter->silent = g_value_get_boolean(value);
626         /* Do "artifical" sil-speech or speech-sil transitions. */
627         if (filter->silent != filter->silent_prev) {
628             gst_vader_transition(filter, gst_clock_get_time(GST_ELEMENT_CLOCK(filter)));
629             /* Also flush the voting window so we don't go right back into speech. */
630             memset(filter->window, 0, sizeof(*filter->window) * VADER_WINDOW);
631         }
632         g_static_rec_mutex_unlock(&filter->mtx);
633         break;
634     case PROP_RUN_LENGTH:
635         filter->threshold_length = g_value_get_uint64(value);
636         break;
637     case PROP_PRE_LENGTH:
638         filter->pre_length = g_value_get_uint64(value);
639         break;
640     case PROP_DUMPDIR:
641         g_free(filter->dumpdir);
642         filter->dumpdir = g_strdup(g_value_get_string(value));
643         break;
644     default:
645         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
646         break;
647     }
648 }
649
650 static void
651 gst_vader_get_property(GObject * object, guint prop_id,
652                        GValue * value, GParamSpec * pspec)
653 {
654     GstVader *filter;
655
656     g_return_if_fail(GST_IS_VADER(object));
657     filter = GST_VADER(object);
658
659     switch (prop_id) {
660     case PROP_RUN_LENGTH:
661         g_value_set_uint64(value, filter->threshold_length);
662         break;
663     case PROP_PRE_LENGTH:
664         g_value_set_uint64(value, filter->pre_length);
665         break;
666     case PROP_THRESHOLD:
667         g_value_set_double(value, (gdouble)filter->threshold_level / 32768.0);
668         break;
669     case PROP_AUTO_THRESHOLD:
670         g_value_set_boolean(value, filter->auto_threshold);
671         break;
672     case PROP_SILENT:
673         g_value_set_boolean(value, filter->silent);
674         break;
675     case PROP_DUMPDIR:
676         g_value_set_string(value, filter->dumpdir);
677         break;
678     default:
679         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
680         break;
681     }
682 }