upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / gst / cutter / gstcutter.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2002,2003,2005
4  *           Thomas Vander Stichele <thomas at apestaart dot org>
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  * SECTION:element-cutter
23  *
24  * Analyses the audio signal for periods of silence. The start and end of
25  * silence is signalled by bus messages named
26  * <classname>&quot;cutter&quot;</classname>.
27  * The message's structure contains two fields:
28  * <itemizedlist>
29  * <listitem>
30  *   <para>
31  *   #GstClockTime
32  *   <classname>&quot;timestamp&quot;</classname>:
33  *   the timestamp of the buffer that triggered the message.
34  *   </para>
35  * </listitem>
36  * <listitem>
37  *   <para>
38  *   gboolean
39  *   <classname>&quot;above&quot;</classname>:
40  *   %TRUE for begin of silence and %FALSE for end of silence.
41  *   </para>
42  * </listitem>
43  * </itemizedlist>
44  *
45  * <refsect2>
46  * <title>Example launch line</title>
47  * |[
48  * gst-launch -m filesrc location=foo.ogg ! decodebin ! audioconvert ! cutter ! autoaudiosink
49  * ]| Show cut messages.
50  * </refsect2>
51  */
52
53 #ifdef HAVE_CONFIG_H
54 #include "config.h"
55 #endif
56 #include <gst/gst.h>
57 #include <gst/audio/audio.h>
58 #include "gstcutter.h"
59 #include "math.h"
60
61 GST_DEBUG_CATEGORY_STATIC (cutter_debug);
62 #define GST_CAT_DEFAULT cutter_debug
63
64 #define CUTTER_DEFAULT_THRESHOLD_LEVEL    0.1
65 #define CUTTER_DEFAULT_THRESHOLD_LENGTH  (500 * GST_MSECOND)
66 #define CUTTER_DEFAULT_PRE_LENGTH        (200 * GST_MSECOND)
67
68 static GstStaticPadTemplate cutter_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
69     GST_PAD_SRC,
70     GST_PAD_ALWAYS,
71     GST_STATIC_CAPS ("audio/x-raw-int, "
72         "rate = (int) [ 1, MAX ], "
73         "channels = (int) [ 1, MAX ], "
74         "endianness = (int) BYTE_ORDER, "
75         "width = (int) { 8, 16 }, "
76         "depth = (int) { 8, 16 }, " "signed = (boolean) true")
77     );
78
79 static GstStaticPadTemplate cutter_sink_factory =
80 GST_STATIC_PAD_TEMPLATE ("sink",
81     GST_PAD_SINK,
82     GST_PAD_ALWAYS,
83     GST_STATIC_CAPS ("audio/x-raw-int, "
84         "rate = (int) [ 1, MAX ], "
85         "channels = (int) [ 1, MAX ], "
86         "endianness = (int) BYTE_ORDER, "
87         "width = (int) { 8, 16 }, "
88         "depth = (int) { 8, 16 }, " "signed = (boolean) true")
89     );
90
91 enum
92 {
93   PROP_0,
94   PROP_THRESHOLD,
95   PROP_THRESHOLD_DB,
96   PROP_RUN_LENGTH,
97   PROP_PRE_LENGTH,
98   PROP_LEAKY
99 };
100
101 GST_BOILERPLATE (GstCutter, gst_cutter, GstElement, GST_TYPE_ELEMENT);
102
103 static void gst_cutter_set_property (GObject * object, guint prop_id,
104     const GValue * value, GParamSpec * pspec);
105 static void gst_cutter_get_property (GObject * object, guint prop_id,
106     GValue * value, GParamSpec * pspec);
107
108 static GstFlowReturn gst_cutter_chain (GstPad * pad, GstBuffer * buffer);
109
110 static gboolean gst_cutter_get_caps (GstPad * pad, GstCutter * filter);
111
112 static void
113 gst_cutter_base_init (gpointer g_class)
114 {
115   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
116
117   gst_element_class_add_pad_template (element_class,
118       gst_static_pad_template_get (&cutter_src_factory));
119   gst_element_class_add_pad_template (element_class,
120       gst_static_pad_template_get (&cutter_sink_factory));
121   gst_element_class_set_details_simple (element_class, "Audio cutter",
122       "Filter/Editor/Audio",
123       "Audio Cutter to split audio into non-silent bits",
124       "Thomas Vander Stichele <thomas at apestaart dot org>");
125 }
126
127 static void
128 gst_cutter_class_init (GstCutterClass * klass)
129 {
130   GObjectClass *gobject_class;
131
132   gobject_class = (GObjectClass *) klass;
133
134   gobject_class->set_property = gst_cutter_set_property;
135   gobject_class->get_property = gst_cutter_get_property;
136
137   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_THRESHOLD,
138       g_param_spec_double ("threshold", "Threshold",
139           "Volume threshold before trigger",
140           -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
141           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
142   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_THRESHOLD_DB,
143       g_param_spec_double ("threshold-dB", "Threshold (dB)",
144           "Volume threshold before trigger (in dB)",
145           -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
146           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
147   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RUN_LENGTH,
148       g_param_spec_uint64 ("run-length", "Run length",
149           "Length of drop below threshold before cut_stop (in nanoseconds)",
150           0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PRE_LENGTH,
152       g_param_spec_uint64 ("pre-length", "Pre-recording buffer length",
153           "Length of pre-recording buffer (in nanoseconds)",
154           0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
155   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEAKY,
156       g_param_spec_boolean ("leaky", "Leaky",
157           "do we leak buffers when below threshold ?",
158           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
159
160   GST_DEBUG_CATEGORY_INIT (cutter_debug, "cutter", 0, "Audio cutting");
161 }
162
163 static void
164 gst_cutter_init (GstCutter * filter, GstCutterClass * g_class)
165 {
166   filter->sinkpad =
167       gst_pad_new_from_static_template (&cutter_sink_factory, "sink");
168   filter->srcpad =
169       gst_pad_new_from_static_template (&cutter_src_factory, "src");
170
171   filter->threshold_level = CUTTER_DEFAULT_THRESHOLD_LEVEL;
172   filter->threshold_length = CUTTER_DEFAULT_THRESHOLD_LENGTH;
173   filter->silent_run_length = 0 * GST_SECOND;
174   filter->silent = TRUE;
175   filter->silent_prev = FALSE;  /* previous value of silent */
176
177   filter->pre_length = CUTTER_DEFAULT_PRE_LENGTH;
178   filter->pre_run_length = 0 * GST_SECOND;
179   filter->pre_buffer = NULL;
180   filter->leaky = FALSE;
181
182   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
183   gst_pad_set_chain_function (filter->sinkpad, gst_cutter_chain);
184   gst_pad_use_fixed_caps (filter->sinkpad);
185
186   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
187   gst_pad_use_fixed_caps (filter->srcpad);
188 }
189
190 static GstMessage *
191 gst_cutter_message_new (GstCutter * c, gboolean above, GstClockTime timestamp)
192 {
193   GstStructure *s;
194   GValue v = { 0, };
195
196   g_value_init (&v, GST_TYPE_LIST);
197
198   s = gst_structure_new ("cutter",
199       "above", G_TYPE_BOOLEAN, above,
200       "timestamp", GST_TYPE_CLOCK_TIME, timestamp, NULL);
201
202   return gst_message_new_element (GST_OBJECT (c), s);
203 }
204
205 /* Calculate the Normalized Cumulative Square over a buffer of the given type
206  * and over all channels combined */
207
208 #define DEFINE_CUTTER_CALCULATOR(TYPE, RESOLUTION)                            \
209 static void inline                                                            \
210 gst_cutter_calculate_##TYPE (TYPE * in, guint num,                            \
211                             double *NCS)                                      \
212 {                                                                             \
213   register int j;                                                             \
214   double squaresum = 0.0;           /* square sum of the integer samples */   \
215   register double square = 0.0;     /* Square */                              \
216   gdouble normalizer;               /* divisor to get a [-1.0, 1.0] range */  \
217                                                                               \
218   *NCS = 0.0;                       /* Normalized Cumulative Square */        \
219                                                                               \
220   normalizer = (double) (1 << (RESOLUTION * 2));                              \
221                                                                               \
222   for (j = 0; j < num; j++)                                                   \
223   {                                                                           \
224     square = ((double) in[j]) * in[j];                                        \
225     squaresum += square;                                                      \
226   }                                                                           \
227                                                                               \
228                                                                               \
229   *NCS = squaresum / normalizer;                                              \
230 }
231
232 DEFINE_CUTTER_CALCULATOR (gint16, 15);
233 DEFINE_CUTTER_CALCULATOR (gint8, 7);
234
235
236 static GstFlowReturn
237 gst_cutter_chain (GstPad * pad, GstBuffer * buf)
238 {
239   GstCutter *filter;
240   gint16 *in_data;
241   guint num_samples;
242   gdouble NCS = 0.0;            /* Normalized Cumulative Square of buffer */
243   gdouble RMS = 0.0;            /* RMS of signal in buffer */
244   gdouble NMS = 0.0;            /* Normalized Mean Square of buffer */
245   GstBuffer *prebuf;            /* pointer to a prebuffer element */
246
247   g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
248   g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
249   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
250
251   filter = GST_CUTTER (GST_OBJECT_PARENT (pad));
252   g_return_val_if_fail (filter != NULL, GST_FLOW_ERROR);
253   g_return_val_if_fail (GST_IS_CUTTER (filter), GST_FLOW_ERROR);
254
255   if (!filter->have_caps) {
256     if (!(gst_cutter_get_caps (pad, filter)))
257       return GST_FLOW_NOT_NEGOTIATED;
258   }
259
260   in_data = (gint16 *) GST_BUFFER_DATA (buf);
261   GST_LOG_OBJECT (filter, "length of prerec buffer: %" GST_TIME_FORMAT,
262       GST_TIME_ARGS (filter->pre_run_length));
263
264   /* calculate mean square value on buffer */
265   switch (filter->width) {
266     case 16:
267       num_samples = GST_BUFFER_SIZE (buf) / 2;
268       gst_cutter_calculate_gint16 (in_data, num_samples, &NCS);
269       NMS = NCS / num_samples;
270       break;
271     case 8:
272       num_samples = GST_BUFFER_SIZE (buf);
273       gst_cutter_calculate_gint8 ((gint8 *) in_data, num_samples, &NCS);
274       NMS = NCS / num_samples;
275       break;
276     default:
277       /* this shouldn't happen */
278       g_warning ("no mean square function for width %d\n", filter->width);
279       break;
280   }
281
282   filter->silent_prev = filter->silent;
283
284   RMS = sqrt (NMS);
285   /* if RMS below threshold, add buffer length to silent run length count
286    * if not, reset
287    */
288   GST_LOG_OBJECT (filter, "buffer stats: NMS %f, RMS %f, audio length %f", NMS,
289       RMS,
290       gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer
291           (filter->sinkpad, buf)));
292   if (RMS < filter->threshold_level)
293     filter->silent_run_length +=
294         gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer
295         (filter->sinkpad, buf));
296   else {
297     filter->silent_run_length = 0 * GST_SECOND;
298     filter->silent = FALSE;
299   }
300
301   if (filter->silent_run_length > filter->threshold_length)
302     /* it has been silent long enough, flag it */
303     filter->silent = TRUE;
304
305   /* has the silent status changed ? if so, send right signal
306    * and, if from silent -> not silent, flush pre_record buffer
307    */
308   if (filter->silent != filter->silent_prev) {
309     if (filter->silent) {
310       GstMessage *m =
311           gst_cutter_message_new (filter, FALSE, GST_BUFFER_TIMESTAMP (buf));
312       GST_DEBUG_OBJECT (filter, "signaling CUT_STOP");
313       gst_element_post_message (GST_ELEMENT (filter), m);
314     } else {
315       gint count = 0;
316       GstMessage *m =
317           gst_cutter_message_new (filter, TRUE, GST_BUFFER_TIMESTAMP (buf));
318
319       GST_DEBUG_OBJECT (filter, "signaling CUT_START");
320       gst_element_post_message (GST_ELEMENT (filter), m);
321       /* first of all, flush current buffer */
322       GST_DEBUG_OBJECT (filter, "flushing buffer of length %" GST_TIME_FORMAT,
323           GST_TIME_ARGS (filter->pre_run_length));
324       while (filter->pre_buffer) {
325         prebuf = (g_list_first (filter->pre_buffer))->data;
326         filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
327         gst_pad_push (filter->srcpad, prebuf);
328         ++count;
329       }
330       GST_DEBUG_OBJECT (filter, "flushed %d buffers", count);
331       filter->pre_run_length = 0 * GST_SECOND;
332     }
333   }
334   /* now check if we have to send the new buffer to the internal buffer cache
335    * or to the srcpad */
336   if (filter->silent) {
337     filter->pre_buffer = g_list_append (filter->pre_buffer, buf);
338     filter->pre_run_length +=
339         gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer
340         (filter->sinkpad, buf));
341     while (filter->pre_run_length > filter->pre_length) {
342       prebuf = (g_list_first (filter->pre_buffer))->data;
343       g_assert (GST_IS_BUFFER (prebuf));
344       filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
345       filter->pre_run_length -=
346           gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer
347           (filter->sinkpad, prebuf));
348       /* only pass buffers if we don't leak */
349       if (!filter->leaky)
350         gst_pad_push (filter->srcpad, prebuf);
351       else
352         gst_buffer_unref (prebuf);
353     }
354   } else
355     gst_pad_push (filter->srcpad, buf);
356
357   return GST_FLOW_OK;
358 }
359
360
361 static gboolean
362 gst_cutter_get_caps (GstPad * pad, GstCutter * filter)
363 {
364   GstCaps *caps;
365   GstStructure *structure;
366
367   caps = gst_pad_get_caps (pad);
368   if (!caps) {
369     GST_INFO ("no caps on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
370     return FALSE;
371   }
372   structure = gst_caps_get_structure (caps, 0);
373   gst_structure_get_int (structure, "width", &filter->width);
374   filter->max_sample = 1 << (filter->width - 1);        /* signed */
375   filter->have_caps = TRUE;
376
377   gst_caps_unref (caps);
378   return TRUE;
379 }
380
381
382 static void
383 gst_cutter_set_property (GObject * object, guint prop_id,
384     const GValue * value, GParamSpec * pspec)
385 {
386   GstCutter *filter;
387
388   g_return_if_fail (GST_IS_CUTTER (object));
389   filter = GST_CUTTER (object);
390
391   switch (prop_id) {
392     case PROP_THRESHOLD:
393       filter->threshold_level = g_value_get_double (value);
394       GST_DEBUG ("DEBUG: set threshold level to %f", filter->threshold_level);
395       break;
396     case PROP_THRESHOLD_DB:
397       /* set the level given in dB
398        * value in dB = 20 * log (value)
399        * values in dB < 0 result in values between 0 and 1
400        */
401       filter->threshold_level = pow (10, g_value_get_double (value) / 20);
402       GST_DEBUG_OBJECT (filter, "set threshold level to %f",
403           filter->threshold_level);
404       break;
405     case PROP_RUN_LENGTH:
406       /* set the minimum length of the silent run required */
407       filter->threshold_length =
408           gst_guint64_to_gdouble (g_value_get_uint64 (value));
409       break;
410     case PROP_PRE_LENGTH:
411       /* set the length of the pre-record block */
412       filter->pre_length = gst_guint64_to_gdouble (g_value_get_uint64 (value));
413       break;
414     case PROP_LEAKY:
415       /* set if the pre-record buffer is leaky or not */
416       filter->leaky = g_value_get_boolean (value);
417       break;
418     default:
419       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
420       break;
421   }
422 }
423
424 static void
425 gst_cutter_get_property (GObject * object, guint prop_id,
426     GValue * value, GParamSpec * pspec)
427 {
428   GstCutter *filter;
429
430   g_return_if_fail (GST_IS_CUTTER (object));
431   filter = GST_CUTTER (object);
432
433   switch (prop_id) {
434     case PROP_RUN_LENGTH:
435       g_value_set_uint64 (value, filter->threshold_length);
436       break;
437     case PROP_THRESHOLD:
438       g_value_set_double (value, filter->threshold_level);
439       break;
440     case PROP_THRESHOLD_DB:
441       g_value_set_double (value, 20 * log (filter->threshold_level));
442       break;
443     case PROP_PRE_LENGTH:
444       g_value_set_uint64 (value, filter->pre_length);
445       break;
446     case PROP_LEAKY:
447       g_value_set_boolean (value, filter->leaky);
448       break;
449     default:
450       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
451       break;
452   }
453 }
454
455 static gboolean
456 plugin_init (GstPlugin * plugin)
457 {
458   if (!gst_element_register (plugin, "cutter", GST_RANK_NONE, GST_TYPE_CUTTER))
459     return FALSE;
460
461   return TRUE;
462 }
463
464 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
465     GST_VERSION_MINOR,
466     "cutter",
467     "Audio Cutter to split audio into non-silent bits",
468     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);