tizen 2.0 init
[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_static_pad_template (element_class,
118       &cutter_src_factory);
119   gst_element_class_add_static_pad_template (element_class,
120       &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
195   s = gst_structure_new ("cutter",
196       "above", G_TYPE_BOOLEAN, above,
197       "timestamp", GST_TYPE_CLOCK_TIME, timestamp, NULL);
198
199   return gst_message_new_element (GST_OBJECT (c), s);
200 }
201
202 /* Calculate the Normalized Cumulative Square over a buffer of the given type
203  * and over all channels combined */
204
205 #define DEFINE_CUTTER_CALCULATOR(TYPE, RESOLUTION)                            \
206 static void inline                                                            \
207 gst_cutter_calculate_##TYPE (TYPE * in, guint num,                            \
208                             double *NCS)                                      \
209 {                                                                             \
210   register int j;                                                             \
211   double squaresum = 0.0;           /* square sum of the integer samples */   \
212   register double square = 0.0;     /* Square */                              \
213   gdouble normalizer;               /* divisor to get a [-1.0, 1.0] range */  \
214                                                                               \
215   *NCS = 0.0;                       /* Normalized Cumulative Square */        \
216                                                                               \
217   normalizer = (double) (1 << (RESOLUTION * 2));                              \
218                                                                               \
219   for (j = 0; j < num; j++)                                                   \
220   {                                                                           \
221     square = ((double) in[j]) * in[j];                                        \
222     squaresum += square;                                                      \
223   }                                                                           \
224                                                                               \
225                                                                               \
226   *NCS = squaresum / normalizer;                                              \
227 }
228
229 DEFINE_CUTTER_CALCULATOR (gint16, 15);
230 DEFINE_CUTTER_CALCULATOR (gint8, 7);
231
232
233 static GstFlowReturn
234 gst_cutter_chain (GstPad * pad, GstBuffer * buf)
235 {
236   GstCutter *filter;
237   gint16 *in_data;
238   guint num_samples;
239   gdouble NCS = 0.0;            /* Normalized Cumulative Square of buffer */
240   gdouble RMS = 0.0;            /* RMS of signal in buffer */
241   gdouble NMS = 0.0;            /* Normalized Mean Square of buffer */
242   GstBuffer *prebuf;            /* pointer to a prebuffer element */
243
244   g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
245   g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
246   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
247
248   filter = GST_CUTTER (GST_OBJECT_PARENT (pad));
249   g_return_val_if_fail (filter != NULL, GST_FLOW_ERROR);
250   g_return_val_if_fail (GST_IS_CUTTER (filter), GST_FLOW_ERROR);
251
252   if (!filter->have_caps) {
253     if (!(gst_cutter_get_caps (pad, filter)))
254       return GST_FLOW_NOT_NEGOTIATED;
255   }
256
257   in_data = (gint16 *) GST_BUFFER_DATA (buf);
258   GST_LOG_OBJECT (filter, "length of prerec buffer: %" GST_TIME_FORMAT,
259       GST_TIME_ARGS (filter->pre_run_length));
260
261   /* calculate mean square value on buffer */
262   switch (filter->width) {
263     case 16:
264       num_samples = GST_BUFFER_SIZE (buf) / 2;
265       gst_cutter_calculate_gint16 (in_data, num_samples, &NCS);
266       NMS = NCS / num_samples;
267       break;
268     case 8:
269       num_samples = GST_BUFFER_SIZE (buf);
270       gst_cutter_calculate_gint8 ((gint8 *) in_data, num_samples, &NCS);
271       NMS = NCS / num_samples;
272       break;
273     default:
274       /* this shouldn't happen */
275       g_warning ("no mean square function for width %d\n", filter->width);
276       break;
277   }
278
279   filter->silent_prev = filter->silent;
280
281   RMS = sqrt (NMS);
282   /* if RMS below threshold, add buffer length to silent run length count
283    * if not, reset
284    */
285   GST_LOG_OBJECT (filter, "buffer stats: NMS %f, RMS %f, audio length %f", NMS,
286       RMS,
287       gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer
288           (filter->sinkpad, buf)));
289   if (RMS < filter->threshold_level)
290     filter->silent_run_length +=
291         gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer
292         (filter->sinkpad, buf));
293   else {
294     filter->silent_run_length = 0 * GST_SECOND;
295     filter->silent = FALSE;
296   }
297
298   if (filter->silent_run_length > filter->threshold_length)
299     /* it has been silent long enough, flag it */
300     filter->silent = TRUE;
301
302   /* has the silent status changed ? if so, send right signal
303    * and, if from silent -> not silent, flush pre_record buffer
304    */
305   if (filter->silent != filter->silent_prev) {
306     if (filter->silent) {
307       GstMessage *m =
308           gst_cutter_message_new (filter, FALSE, GST_BUFFER_TIMESTAMP (buf));
309       GST_DEBUG_OBJECT (filter, "signaling CUT_STOP");
310       gst_element_post_message (GST_ELEMENT (filter), m);
311     } else {
312       gint count = 0;
313       GstMessage *m =
314           gst_cutter_message_new (filter, TRUE, GST_BUFFER_TIMESTAMP (buf));
315
316       GST_DEBUG_OBJECT (filter, "signaling CUT_START");
317       gst_element_post_message (GST_ELEMENT (filter), m);
318       /* first of all, flush current buffer */
319       GST_DEBUG_OBJECT (filter, "flushing buffer of length %" GST_TIME_FORMAT,
320           GST_TIME_ARGS (filter->pre_run_length));
321       while (filter->pre_buffer) {
322         prebuf = (g_list_first (filter->pre_buffer))->data;
323         filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
324         gst_pad_push (filter->srcpad, prebuf);
325         ++count;
326       }
327       GST_DEBUG_OBJECT (filter, "flushed %d buffers", count);
328       filter->pre_run_length = 0 * GST_SECOND;
329     }
330   }
331   /* now check if we have to send the new buffer to the internal buffer cache
332    * or to the srcpad */
333   if (filter->silent) {
334     filter->pre_buffer = g_list_append (filter->pre_buffer, buf);
335     filter->pre_run_length +=
336         gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer
337         (filter->sinkpad, buf));
338     while (filter->pre_run_length > filter->pre_length) {
339       prebuf = (g_list_first (filter->pre_buffer))->data;
340       g_assert (GST_IS_BUFFER (prebuf));
341       filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
342       filter->pre_run_length -=
343           gst_guint64_to_gdouble (gst_audio_duration_from_pad_buffer
344           (filter->sinkpad, prebuf));
345       /* only pass buffers if we don't leak */
346       if (!filter->leaky)
347         gst_pad_push (filter->srcpad, prebuf);
348       else
349         gst_buffer_unref (prebuf);
350     }
351   } else
352     gst_pad_push (filter->srcpad, buf);
353
354   return GST_FLOW_OK;
355 }
356
357
358 static gboolean
359 gst_cutter_get_caps (GstPad * pad, GstCutter * filter)
360 {
361   GstCaps *caps;
362   GstStructure *structure;
363
364   caps = gst_pad_get_caps (pad);
365   if (!caps) {
366     GST_INFO ("no caps on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
367     return FALSE;
368   }
369   structure = gst_caps_get_structure (caps, 0);
370   gst_structure_get_int (structure, "width", &filter->width);
371   filter->max_sample = 1 << (filter->width - 1);        /* signed */
372   filter->have_caps = TRUE;
373
374   gst_caps_unref (caps);
375   return TRUE;
376 }
377
378
379 static void
380 gst_cutter_set_property (GObject * object, guint prop_id,
381     const GValue * value, GParamSpec * pspec)
382 {
383   GstCutter *filter;
384
385   g_return_if_fail (GST_IS_CUTTER (object));
386   filter = GST_CUTTER (object);
387
388   switch (prop_id) {
389     case PROP_THRESHOLD:
390       filter->threshold_level = g_value_get_double (value);
391       GST_DEBUG ("DEBUG: set threshold level to %f", filter->threshold_level);
392       break;
393     case PROP_THRESHOLD_DB:
394       /* set the level given in dB
395        * value in dB = 20 * log (value)
396        * values in dB < 0 result in values between 0 and 1
397        */
398       filter->threshold_level = pow (10, g_value_get_double (value) / 20);
399       GST_DEBUG_OBJECT (filter, "set threshold level to %f",
400           filter->threshold_level);
401       break;
402     case PROP_RUN_LENGTH:
403       /* set the minimum length of the silent run required */
404       filter->threshold_length =
405           gst_guint64_to_gdouble (g_value_get_uint64 (value));
406       break;
407     case PROP_PRE_LENGTH:
408       /* set the length of the pre-record block */
409       filter->pre_length = gst_guint64_to_gdouble (g_value_get_uint64 (value));
410       break;
411     case PROP_LEAKY:
412       /* set if the pre-record buffer is leaky or not */
413       filter->leaky = g_value_get_boolean (value);
414       break;
415     default:
416       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417       break;
418   }
419 }
420
421 static void
422 gst_cutter_get_property (GObject * object, guint prop_id,
423     GValue * value, GParamSpec * pspec)
424 {
425   GstCutter *filter;
426
427   g_return_if_fail (GST_IS_CUTTER (object));
428   filter = GST_CUTTER (object);
429
430   switch (prop_id) {
431     case PROP_RUN_LENGTH:
432       g_value_set_uint64 (value, filter->threshold_length);
433       break;
434     case PROP_THRESHOLD:
435       g_value_set_double (value, filter->threshold_level);
436       break;
437     case PROP_THRESHOLD_DB:
438       g_value_set_double (value, 20 * log (filter->threshold_level));
439       break;
440     case PROP_PRE_LENGTH:
441       g_value_set_uint64 (value, filter->pre_length);
442       break;
443     case PROP_LEAKY:
444       g_value_set_boolean (value, filter->leaky);
445       break;
446     default:
447       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
448       break;
449   }
450 }
451
452 static gboolean
453 plugin_init (GstPlugin * plugin)
454 {
455   if (!gst_element_register (plugin, "cutter", GST_RANK_NONE, GST_TYPE_CUTTER))
456     return FALSE;
457
458   return TRUE;
459 }
460
461 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
462     GST_VERSION_MINOR,
463     "cutter",
464     "Audio Cutter to split audio into non-silent bits",
465     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);