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