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.
21 #include <gst/audio/audio.h>
22 #include "gstcutter.h"
26 static GstElementDetails cutter_details = {
29 "Audio Cutter to split audio into non-silent bits",
31 "Thomas <thomas@apestaart.org>",
36 /* Filter signals and args */
52 GST_PADTEMPLATE_FACTORY (cutter_src_factory,
59 "channels", GST_PROPS_INT_RANGE (1, 2)
63 GST_PADTEMPLATE_FACTORY (cutter_sink_factory,
70 "channels", GST_PROPS_INT_RANGE (1, 2)
74 static void gst_cutter_class_init (GstCutterClass *klass);
75 static void gst_cutter_init (GstCutter *filter);
77 static void gst_cutter_set_property (GObject *object, guint prop_id,
78 const GValue *value, GParamSpec *pspec);
79 static void gst_cutter_get_property (GObject *object, guint prop_id,
80 GValue *value, GParamSpec *pspec);
82 static void gst_cutter_chain (GstPad *pad, GstBuffer *buf);
83 static double inline gst_cutter_16bit_ms (gint16* data, guint numsamples);
84 static double inline gst_cutter_8bit_ms (gint8* data, guint numsamples);
86 void gst_cutter_get_caps (GstPad *pad, GstCutter* filter);
88 static GstElementClass *parent_class = NULL;
89 static guint gst_cutter_signals[LAST_SIGNAL] = { 0 };
91 static GstPadNegotiateReturn
92 cutter_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data)
94 GstCutter* filter = GST_CUTTER (gst_pad_get_parent (pad));
97 return GST_PAD_NEGOTIATE_FAIL;
99 return gst_pad_negotiate_proxy(pad,filter->sinkpad,caps);
102 static GstPadNegotiateReturn
103 cutter_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data)
105 GstCutter* filter = GST_CUTTER (gst_pad_get_parent (pad));
108 return GST_PAD_NEGOTIATE_FAIL;
110 return gst_pad_negotiate_proxy(pad,filter->srcpad,caps);
114 gst_cutter_get_type(void) {
115 static GType cutter_type = 0;
118 static const GTypeInfo cutter_info = {
119 sizeof(GstCutterClass), NULL, NULL, (GClassInitFunc)gst_cutter_class_init,
124 (GInstanceInitFunc)gst_cutter_init,
126 cutter_type = g_type_register_static(GST_TYPE_ELEMENT, "GstCutter", &cutter_info, 0);
132 gst_cutter_class_init (GstCutterClass *klass)
134 GObjectClass *gobject_class;
135 GstElementClass *gstelement_class;
137 gobject_class = (GObjectClass*) klass;
138 gstelement_class = (GstElementClass*) klass;
140 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
142 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_THRESHOLD,
143 g_param_spec_double ("threshold", "threshold", "threshold",
144 G_MINDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE)); // CHECKME
145 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_THRESHOLD_DB,
146 g_param_spec_double ("threshold_dB", "threshold_dB", "threshold_dB",
147 G_MINDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE)); // CHECKME
148 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_RUN_LENGTH,
149 g_param_spec_double ("runlength", "runlength", "runlength",
150 G_MINDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE)); // CHECKME
152 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PRE_LENGTH,
153 g_param_spec_double ("prelength", "prelength", "prelength",
154 G_MINDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE)); // CHECKME
155 gst_cutter_signals[CUT_START] =
156 g_signal_new ("cut_start", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
157 G_STRUCT_OFFSET (GstCutterClass, cut_start), NULL, NULL,
158 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
159 gst_cutter_signals[CUT_STOP] =
160 g_signal_new ("cut_stop", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
161 G_STRUCT_OFFSET (GstCutterClass, cut_stop), NULL, NULL,
162 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
165 gobject_class->set_property = gst_cutter_set_property;
166 gobject_class->get_property = gst_cutter_get_property;
170 gst_cutter_init (GstCutter *filter)
172 filter->sinkpad = gst_pad_new_from_template (cutter_sink_factory (),"sink");
173 filter->srcpad = gst_pad_new_from_template (cutter_src_factory (),"src");
175 filter->threshold_level = 0.1;
176 filter->threshold_length = 0.5;
177 filter->silent_run_length = 0.0;
178 filter->silent = TRUE;
180 filter->pre_length = 0.2;
181 filter->pre_run_length = 0.0;
182 filter->pre_buffer = NULL;
184 gst_pad_set_negotiate_function (filter->sinkpad,cutter_negotiate_sink);
185 gst_pad_set_negotiate_function (filter->srcpad,cutter_negotiate_src);
187 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
188 gst_pad_set_chain_function (filter->sinkpad, gst_cutter_chain);
189 filter->srcpad = gst_pad_new ("src", GST_PAD_SRC);
190 gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
194 gst_cutter_chain (GstPad *pad, GstBuffer *buf)
198 double RMS = 0.0; /* RMS of signal in buffer */
199 double ms = 0.0; /* mean square value of buffer */
200 static gboolean silent_prev = FALSE; /* previous value of silent */
201 GstBuffer* prebuf; /* pointer to a prebuffer element */
203 g_return_if_fail (pad != NULL);
204 g_return_if_fail (GST_IS_PAD (pad));
205 g_return_if_fail (buf != NULL);
207 filter = GST_CUTTER (GST_OBJECT_PARENT (pad));
208 g_return_if_fail (filter != NULL);
209 g_return_if_fail (GST_IS_CUTTER (filter));
211 g_return_if_fail (gst_audio_is_buffer_framed (pad, buf) == TRUE);
213 if (!filter->have_caps) gst_cutter_get_caps (pad, filter);
215 in_data = (gint16 *) GST_BUFFER_DATA (buf);
216 g_print ("DEBUG: cutter: length of prerec buffer: %.3f sec\n",
217 filter->pre_run_length);
219 /* calculate mean square value on buffer */
220 switch (filter->width)
223 ms = gst_cutter_16bit_ms (in_data, GST_BUFFER_SIZE (buf) / 2);
226 ms = gst_cutter_8bit_ms ((gint8 *) in_data, GST_BUFFER_SIZE (buf));
229 /* this shouldn't happen */
230 g_print ("WARNING: no mean square function for width %d\n",
235 silent_prev = filter->silent;
237 RMS = sqrt (ms) / (double) filter->max_sample;
238 /* if RMS below threshold, add buffer length to silent run length count
241 //g_print ("DEBUG: cutter: ms %f, RMS %f\n", ms, RMS);
242 if (RMS < filter->threshold_level)
243 filter->silent_run_length += gst_audio_length (filter->srcpad, buf);
246 filter->silent_run_length = 0.0;
247 filter->silent = FALSE;
250 if (filter->silent_run_length > filter->threshold_length)
251 /* it has been silent long enough, flag it */
252 filter->silent = TRUE;
254 /* has the silent status changed ? if so, send right signal
255 * and, if from silent -> not silent, flush pre_record buffer
257 if (filter->silent != silent_prev)
261 // g_print ("DEBUG: cutter: cut to here, turning off out\n");
262 gtk_signal_emit (G_OBJECT (filter), gst_cutter_signals[CUT_STOP]);
266 // g_print ("DEBUG: cutter: start from here, turning on out\n");
267 /* first of all, flush current buffer */
268 gtk_signal_emit (G_OBJECT (filter), gst_cutter_signals[CUT_START]);
269 g_print ("DEBUG: cutter: flushing buffer ");
270 while (filter->pre_buffer)
273 prebuf = (g_list_first (filter->pre_buffer))->data;
274 filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
275 gst_pad_push (filter->srcpad, prebuf);
276 filter->pre_run_length = 0.0;
281 /* now check if we have to add the new buffer to the cache or to the pad */
284 filter->pre_buffer = g_list_append (filter->pre_buffer, buf);
285 filter->pre_run_length += gst_audio_length (filter->srcpad, buf);
286 while (filter->pre_run_length > filter->pre_length)
288 prebuf = (g_list_first (filter->pre_buffer))->data;
289 filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
290 gst_pad_push (filter->srcpad, prebuf);
291 filter->pre_run_length -= gst_audio_length (filter->srcpad, prebuf);
295 gst_pad_push (filter->srcpad, buf);
299 gst_cutter_16bit_ms (gint16* data, guint num_samples)
300 #include "filter.func"
303 gst_cutter_8bit_ms (gint8* data, guint num_samples)
304 #include "filter.func"
307 gst_cutter_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
311 /* it's not null if we got it, but it might not be ours */
312 g_return_if_fail(GST_IS_CUTTER (object));
313 filter = GST_CUTTER (object);
319 filter->threshold_level = g_value_get_double (value);
320 g_print ("DEBUG: cutter: set threshold level to %f\n",
321 filter->threshold_level);
323 case ARG_THRESHOLD_DB:
324 /* set the level given in dB
325 * value in dB = 20 * log (value)
326 * values in dB < 0 result in values between 0 and 1
328 filter->threshold_level = pow (10, g_value_get_double (value) / 20);
329 g_print ("DEBUG: cutter: set threshold level to %f\n",
330 filter->threshold_level);
333 /* set the minimum length of the silent run required */
334 filter->threshold_length = g_value_get_double (value);
337 /* set the length of the pre-record block */
338 filter->pre_length = g_value_get_double (value);
341 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
347 gst_cutter_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
351 /* it's not null if we got it, but it might not be ours */
352 g_return_if_fail (GST_IS_CUTTER(object));
353 filter = GST_CUTTER (object);
358 g_value_set_double (value, filter->threshold_length);
361 g_value_set_double (value, filter->threshold_level);
363 case ARG_THRESHOLD_DB:
364 g_value_set_double (value, 20 * log (filter->threshold_level));
367 g_value_set_double (value, filter->pre_length);
370 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
376 plugin_init (GModule *module, GstPlugin *plugin)
378 GstElementFactory *factory;
380 factory = gst_elementfactory_new("cutter",GST_TYPE_CUTTER,
382 g_return_val_if_fail(factory != NULL, FALSE);
384 gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (cutter_src_factory));
385 gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (cutter_sink_factory));
387 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
389 /* load audio support library */
390 if (!gst_library_load ("gstaudio"))
392 gst_info ("cutter: could not load support library: 'gstaudio'\n");
399 GstPluginDesc plugin_desc =
408 gst_cutter_get_caps (GstPad *pad, GstCutter* filter)
410 GstCaps *caps = NULL;
412 caps = GST_PAD_CAPS (pad);
413 // FIXME : Please change this to a better warning method !
415 printf ("WARNING: cutter: get_caps: Could not get caps of pad !\n");
416 filter->width = gst_caps_get_int (caps, "width");
417 filter->max_sample = gst_audio_highest_sample_value (pad);
418 filter->have_caps = TRUE;