2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #include <gst/audio/audio.h>
25 #include "gstcutter.h"
28 /* elementfactory information */
29 static GstElementDetails cutter_details = {
31 "Filter/Editor/Audio",
32 "Audio Cutter to split audio into non-silent bits",
33 "Thomas <thomas@apestaart.org>",
37 /* Filter signals and args */
54 static GstStaticPadTemplate cutter_src_factory =
55 GST_STATIC_PAD_TEMPLATE (
60 GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
61 GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS
65 static GstStaticPadTemplate cutter_sink_factory =
66 GST_STATIC_PAD_TEMPLATE (
71 GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
72 GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS
76 static void gst_cutter_base_init (gpointer g_class);
77 static void gst_cutter_class_init (GstCutterClass *klass);
78 static void gst_cutter_init (GstCutter *filter);
80 static void gst_cutter_set_property (GObject *object, guint prop_id,
83 static void gst_cutter_get_property (GObject *object, guint prop_id,
84 GValue *value, GParamSpec *pspec);
86 static void gst_cutter_chain (GstPad *pad, GstData *_data);
88 inline gst_cutter_16bit_ms (gint16* data, guint numsamples);
90 inline gst_cutter_8bit_ms (gint8* data, guint numsamples);
92 void gst_cutter_get_caps (GstPad *pad, GstCutter* filter);
94 static GstElementClass *parent_class = NULL;
95 static guint gst_cutter_signals[LAST_SIGNAL] = { 0 };
99 gst_cutter_get_type (void) {
100 static GType cutter_type = 0;
103 static const GTypeInfo cutter_info = {
104 sizeof (GstCutterClass),
105 gst_cutter_base_init,
107 (GClassInitFunc) gst_cutter_class_init, NULL, NULL,
108 sizeof (GstCutter), 0,
109 (GInstanceInitFunc) gst_cutter_init,
111 cutter_type = g_type_register_static (GST_TYPE_ELEMENT, "GstCutter",
118 gst_cutter_base_init (gpointer g_class)
120 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
122 gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&cutter_src_factory));
123 gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&cutter_sink_factory));
124 gst_element_class_set_details (element_class, &cutter_details);
127 static GstPadLinkReturn
128 gst_cutter_link (GstPad *pad, const GstCaps*caps)
133 filter = GST_CUTTER (gst_pad_get_parent (pad));
134 g_return_val_if_fail (filter != NULL, GST_PAD_LINK_REFUSED);
135 g_return_val_if_fail (GST_IS_CUTTER (filter), GST_PAD_LINK_REFUSED);
136 otherpad = (pad == filter->srcpad ? filter->sinkpad : filter->srcpad);
138 return gst_pad_try_set_caps (otherpad, caps);
142 gst_cutter_class_init (GstCutterClass *klass)
144 GObjectClass *gobject_class;
145 GstElementClass *gstelement_class;
147 gobject_class = (GObjectClass*) klass;
148 gstelement_class = (GstElementClass*) klass;
150 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
152 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_THRESHOLD,
153 g_param_spec_double ("threshold", "Threshold",
154 "Volume threshold before trigger",
155 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE));
156 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_THRESHOLD_DB,
157 g_param_spec_double ("threshold_dB", "Threshold (dB)",
158 "Volume threshold before trigger (in dB)",
159 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE));
160 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_RUN_LENGTH,
161 g_param_spec_double ("runlength", "Runlength",
162 "Length of drop below threshold before cut_stop (seconds)",
163 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE));
164 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PRE_LENGTH,
165 g_param_spec_double ("prelength", "prelength",
166 "Length of pre-recording buffer (seconds)",
167 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE));
168 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LEAKY,
169 g_param_spec_boolean ("leaky", "Leaky",
170 "do we leak buffers when below threshold ?",
171 FALSE, G_PARAM_READWRITE));
172 gst_cutter_signals[CUT_START] =
173 g_signal_new ("cut-start", G_TYPE_FROM_CLASS (klass),
175 G_STRUCT_OFFSET (GstCutterClass, cut_start), NULL, NULL,
176 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
177 gst_cutter_signals[CUT_STOP] =
178 g_signal_new ("cut-stop", G_TYPE_FROM_CLASS (klass),
180 G_STRUCT_OFFSET (GstCutterClass, cut_stop), NULL, NULL,
181 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
184 gobject_class->set_property = gst_cutter_set_property;
185 gobject_class->get_property = gst_cutter_get_property;
189 gst_cutter_init (GstCutter *filter)
191 filter->sinkpad = gst_pad_new_from_template (
192 gst_static_pad_template_get(&cutter_sink_factory),"sink");
193 filter->srcpad = gst_pad_new_from_template (
194 gst_static_pad_template_get(&cutter_src_factory),"src");
196 filter->threshold_level = 0.1;
197 filter->threshold_length = 0.5;
198 filter->silent_run_length = 0.0;
199 filter->silent = TRUE;
201 filter->pre_length = 0.2;
202 filter->pre_run_length = 0.0;
203 filter->pre_buffer = NULL;
204 filter->leaky = FALSE;
206 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
207 gst_pad_set_chain_function (filter->sinkpad, gst_cutter_chain);
208 gst_pad_set_link_function (filter->sinkpad, gst_cutter_link);
210 gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
211 /*gst_pad_set_link_function (filter->srcpad, gst_cutter_link);*/
215 gst_cutter_chain (GstPad *pad, GstData *_data)
217 GstBuffer *buf = GST_BUFFER (_data);
220 double RMS = 0.0; /* RMS of signal in buffer */
221 double ms = 0.0; /* mean square value of buffer */
222 static gboolean silent_prev = FALSE; /* previous value of silent */
223 GstBuffer *prebuf; /* pointer to a prebuffer element */
225 g_return_if_fail (pad != NULL);
226 g_return_if_fail (GST_IS_PAD (pad));
227 g_return_if_fail (buf != NULL);
229 filter = GST_CUTTER (GST_OBJECT_PARENT (pad));
230 g_return_if_fail (filter != NULL);
231 g_return_if_fail (GST_IS_CUTTER (filter));
233 if (gst_audio_is_buffer_framed (pad, buf) == FALSE)
234 g_warning ("audio buffer is not framed !\n");
236 if (!filter->have_caps) gst_cutter_get_caps (pad, filter);
238 in_data = (gint16 *) GST_BUFFER_DATA (buf);
240 "length of prerec buffer: %.3f sec",
241 filter->pre_run_length);
243 /* calculate mean square value on buffer */
244 switch (filter->width)
247 ms = gst_cutter_16bit_ms (in_data, GST_BUFFER_SIZE (buf) / 2);
250 ms = gst_cutter_8bit_ms ((gint8 *) in_data, GST_BUFFER_SIZE (buf));
253 /* this shouldn't happen */
254 g_print ("WARNING: no mean square function for width %d\n",
259 silent_prev = filter->silent;
261 RMS = sqrt (ms) / (double) filter->max_sample;
262 /* if RMS below threshold, add buffer length to silent run length count
266 "buffer stats: ms %f, RMS %f, audio length %f",
267 ms, RMS, gst_audio_length (filter->srcpad, buf));
268 if (RMS < filter->threshold_level)
269 filter->silent_run_length += gst_audio_length (filter->srcpad, buf);
272 filter->silent_run_length = 0.0;
273 filter->silent = FALSE;
276 if (filter->silent_run_length > filter->threshold_length)
277 /* it has been silent long enough, flag it */
278 filter->silent = TRUE;
280 /* has the silent status changed ? if so, send right signal
281 * and, if from silent -> not silent, flush pre_record buffer
283 if (filter->silent != silent_prev)
287 /* g_print ("DEBUG: cutter: cut to here, turning off out\n"); */
288 g_signal_emit (G_OBJECT (filter), gst_cutter_signals[CUT_STOP], 0);
293 /* g_print ("DEBUG: cutter: start from here, turning on out\n"); */
294 /* first of all, flush current buffer */
295 g_signal_emit (G_OBJECT (filter), gst_cutter_signals[CUT_START], 0);
297 "flushing buffer of length %.3f",
298 filter->pre_run_length);
299 while (filter->pre_buffer)
301 prebuf = (g_list_first (filter->pre_buffer))->data;
302 filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
303 gst_pad_push (filter->srcpad, GST_DATA (prebuf));
306 GST_DEBUG ("flushed %d buffers", count);
307 filter->pre_run_length = 0.0;
310 /* now check if we have to send the new buffer to the internal buffer cache
311 * or to the srcpad */
314 /* we ref it before putting it in the pre_buffer */
315 /* FIXME: we shouldn't probably do this, because the buffer
316 * arrives reffed already; the plugin should just push it
317 * or unref it to make it disappear */
319 gst_buffer_ref (buf);
321 filter->pre_buffer = g_list_append (filter->pre_buffer, buf);
322 filter->pre_run_length += gst_audio_length (filter->srcpad, buf);
323 while (filter->pre_run_length > filter->pre_length)
325 prebuf = (g_list_first (filter->pre_buffer))->data;
326 g_assert (GST_IS_BUFFER (prebuf));
327 filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
328 filter->pre_run_length -= gst_audio_length (filter->srcpad, prebuf);
329 /* only pass buffers if we don't leak */
331 gst_pad_push (filter->srcpad, GST_DATA (prebuf));
332 /* we unref it after getting it out of the pre_buffer */
333 gst_buffer_unref (prebuf);
337 gst_pad_push (filter->srcpad, GST_DATA (buf));
341 gst_cutter_16bit_ms (gint16* data, guint num_samples)
342 #include "filter.func"
345 gst_cutter_8bit_ms (gint8* data, guint num_samples)
346 #include "filter.func"
349 gst_cutter_set_property (GObject *object, guint prop_id,
350 const GValue *value, GParamSpec *pspec)
354 g_return_if_fail (GST_IS_CUTTER (object));
355 filter = GST_CUTTER (object);
361 filter->threshold_level = g_value_get_double (value);
363 "DEBUG: set threshold level to %f",
364 filter->threshold_level);
366 case ARG_THRESHOLD_DB:
367 /* set the level given in dB
368 * value in dB = 20 * log (value)
369 * values in dB < 0 result in values between 0 and 1
371 filter->threshold_level = pow (10, g_value_get_double (value) / 20);
373 "DEBUG: set threshold level to %f",
374 filter->threshold_level);
377 /* set the minimum length of the silent run required */
378 filter->threshold_length = g_value_get_double (value);
381 /* set the length of the pre-record block */
382 filter->pre_length = g_value_get_double (value);
385 /* set if the pre-record buffer is leaky or not */
386 filter->leaky = g_value_get_boolean (value);
389 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
395 gst_cutter_get_property (GObject *object, guint prop_id,
396 GValue *value, GParamSpec *pspec)
400 g_return_if_fail (GST_IS_CUTTER (object));
401 filter = GST_CUTTER (object);
406 g_value_set_double (value, filter->threshold_length);
409 g_value_set_double (value, filter->threshold_level);
411 case ARG_THRESHOLD_DB:
412 g_value_set_double (value, 20 * log (filter->threshold_level));
415 g_value_set_double (value, filter->pre_length);
418 g_value_set_boolean (value, filter->leaky);
421 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
427 plugin_init (GstPlugin *plugin)
429 /* load audio support library */
430 if (!gst_library_load ("gstaudio"))
433 if (!gst_element_register (plugin, "cutter", GST_RANK_NONE, GST_TYPE_CUTTER))
443 "Audio Cutter to split audio into non-silent bits",
451 gst_cutter_get_caps (GstPad *pad, GstCutter* filter)
453 const GstCaps *caps = NULL;
454 GstStructure *structure;
456 caps = GST_PAD_CAPS (pad);
457 /* FIXME : Please change this to a better warning method ! */
458 g_assert (caps != NULL);
460 printf ("WARNING: get_caps: Could not get caps of pad !\n");
461 structure = gst_caps_get_structure (caps, 0);
462 gst_structure_get_int (structure, "width", &filter->width);
463 filter->max_sample = gst_audio_highest_sample_value (pad);
464 filter->have_caps = TRUE;