rework configure.ac; make asterisk rtp stuff compile on mingw
[platform/upstream/gstreamer.git] / gst / cutter / gstcutter.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include <gst/gst.h>
24 #include <gst/audio/audio.h>
25 #include "gstcutter.h"
26 #include "math.h"
27
28 /* elementfactory information */
29 static GstElementDetails cutter_details = {
30   "Cutter",
31   "Filter/Editor/Audio",
32   "Audio Cutter to split audio into non-silent bits",
33   "Thomas <thomas@apestaart.org>",
34 };
35
36
37 /* Filter signals and args */
38 enum
39 {
40   /* FILL ME */
41   CUT_START,
42   CUT_STOP,
43   LAST_SIGNAL
44 };
45
46 enum
47 {
48   ARG_0,
49   ARG_THRESHOLD,
50   ARG_THRESHOLD_DB,
51   ARG_RUN_LENGTH,
52   ARG_PRE_LENGTH,
53   ARG_LEAKY
54 };
55
56 static GstStaticPadTemplate cutter_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
57     GST_PAD_SRC,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
60         GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
61     );
62
63 static GstStaticPadTemplate cutter_sink_factory =
64     GST_STATIC_PAD_TEMPLATE ("sink",
65     GST_PAD_SINK,
66     GST_PAD_ALWAYS,
67     GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
68         GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
69     );
70
71 static void gst_cutter_base_init (gpointer g_class);
72 static void gst_cutter_class_init (GstCutterClass * klass);
73 static void gst_cutter_init (GstCutter * filter);
74
75 static void gst_cutter_set_property (GObject * object, guint prop_id,
76     const GValue * value, GParamSpec * pspec);
77 static void gst_cutter_get_property (GObject * object, guint prop_id,
78     GValue * value, GParamSpec * pspec);
79
80 static void gst_cutter_chain (GstPad * pad, GstData * _data);
81 static double inline gst_cutter_16bit_ms (gint16 * data, guint numsamples);
82 static double inline gst_cutter_8bit_ms (gint8 * data, guint numsamples);
83
84 void gst_cutter_get_caps (GstPad * pad, GstCutter * filter);
85
86 static GstElementClass *parent_class = NULL;
87 static guint gst_cutter_signals[LAST_SIGNAL] = { 0 };
88
89
90 GType
91 gst_cutter_get_type (void)
92 {
93   static GType cutter_type = 0;
94
95   if (!cutter_type) {
96     static const GTypeInfo cutter_info = {
97       sizeof (GstCutterClass),
98       gst_cutter_base_init,
99       NULL,
100       (GClassInitFunc) gst_cutter_class_init, NULL, NULL,
101       sizeof (GstCutter), 0,
102       (GInstanceInitFunc) gst_cutter_init,
103     };
104
105     cutter_type = g_type_register_static (GST_TYPE_ELEMENT, "GstCutter",
106         &cutter_info, 0);
107   }
108   return cutter_type;
109 }
110
111 static void
112 gst_cutter_base_init (gpointer g_class)
113 {
114   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
115
116   gst_element_class_add_pad_template (element_class,
117       gst_static_pad_template_get (&cutter_src_factory));
118   gst_element_class_add_pad_template (element_class,
119       gst_static_pad_template_get (&cutter_sink_factory));
120   gst_element_class_set_details (element_class, &cutter_details);
121 }
122
123 static GstPadLinkReturn
124 gst_cutter_link (GstPad * pad, const GstCaps * caps)
125 {
126   GstCutter *filter;
127   GstPad *otherpad;
128
129   filter = GST_CUTTER (gst_pad_get_parent (pad));
130   g_return_val_if_fail (filter != NULL, GST_PAD_LINK_REFUSED);
131   g_return_val_if_fail (GST_IS_CUTTER (filter), GST_PAD_LINK_REFUSED);
132   otherpad = (pad == filter->srcpad ? filter->sinkpad : filter->srcpad);
133
134   return gst_pad_try_set_caps (otherpad, caps);
135 }
136
137 static void
138 gst_cutter_class_init (GstCutterClass * klass)
139 {
140   GObjectClass *gobject_class;
141   GstElementClass *gstelement_class;
142
143   gobject_class = (GObjectClass *) klass;
144   gstelement_class = (GstElementClass *) klass;
145
146   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
147
148   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_THRESHOLD,
149       g_param_spec_double ("threshold", "Threshold",
150           "Volume threshold before trigger",
151           -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE));
152   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_THRESHOLD_DB,
153       g_param_spec_double ("threshold_dB", "Threshold (dB)",
154           "Volume threshold before trigger (in dB)",
155           -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE));
156   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_RUN_LENGTH,
157       g_param_spec_double ("runlength", "Runlength",
158           "Length of drop below threshold before cut_stop (seconds)",
159           0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE));
160   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PRE_LENGTH,
161       g_param_spec_double ("prelength", "prelength",
162           "Length of pre-recording buffer (seconds)",
163           0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE));
164   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LEAKY,
165       g_param_spec_boolean ("leaky", "Leaky",
166           "do we leak buffers when below threshold ?",
167           FALSE, G_PARAM_READWRITE));
168   gst_cutter_signals[CUT_START] =
169       g_signal_new ("cut-start", G_TYPE_FROM_CLASS (klass),
170       G_SIGNAL_RUN_FIRST,
171       G_STRUCT_OFFSET (GstCutterClass, cut_start), NULL, NULL,
172       g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
173   gst_cutter_signals[CUT_STOP] =
174       g_signal_new ("cut-stop", G_TYPE_FROM_CLASS (klass),
175       G_SIGNAL_RUN_FIRST,
176       G_STRUCT_OFFSET (GstCutterClass, cut_stop), NULL, NULL,
177       g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
178
179
180   gobject_class->set_property = gst_cutter_set_property;
181   gobject_class->get_property = gst_cutter_get_property;
182 }
183
184 static void
185 gst_cutter_init (GstCutter * filter)
186 {
187   filter->sinkpad =
188       gst_pad_new_from_template (gst_static_pad_template_get
189       (&cutter_sink_factory), "sink");
190   filter->srcpad =
191       gst_pad_new_from_template (gst_static_pad_template_get
192       (&cutter_src_factory), "src");
193
194   filter->threshold_level = 0.1;
195   filter->threshold_length = 0.5;
196   filter->silent_run_length = 0.0;
197   filter->silent = TRUE;
198
199   filter->pre_length = 0.2;
200   filter->pre_run_length = 0.0;
201   filter->pre_buffer = NULL;
202   filter->leaky = FALSE;
203
204   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
205   gst_pad_set_chain_function (filter->sinkpad, gst_cutter_chain);
206   gst_pad_set_link_function (filter->sinkpad, gst_cutter_link);
207
208   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
209   /*gst_pad_set_link_function (filter->srcpad, gst_cutter_link); */
210 }
211
212 static void
213 gst_cutter_chain (GstPad * pad, GstData * _data)
214 {
215   GstBuffer *buf = GST_BUFFER (_data);
216   GstCutter *filter;
217   gint16 *in_data;
218   double RMS = 0.0;             /* RMS of signal in buffer */
219   double ms = 0.0;              /* mean square value of buffer */
220   static gboolean silent_prev = FALSE;  /* previous value of silent */
221   GstBuffer *prebuf;            /* pointer to a prebuffer element */
222
223   g_return_if_fail (pad != NULL);
224   g_return_if_fail (GST_IS_PAD (pad));
225   g_return_if_fail (buf != NULL);
226
227   filter = GST_CUTTER (GST_OBJECT_PARENT (pad));
228   g_return_if_fail (filter != NULL);
229   g_return_if_fail (GST_IS_CUTTER (filter));
230
231   if (gst_audio_is_buffer_framed (pad, buf) == FALSE)
232     g_warning ("audio buffer is not framed !\n");
233
234   if (!filter->have_caps)
235     gst_cutter_get_caps (pad, filter);
236
237   in_data = (gint16 *) GST_BUFFER_DATA (buf);
238   GST_DEBUG ("length of prerec buffer: %.3f sec", filter->pre_run_length);
239
240   /* calculate mean square value on buffer */
241   switch (filter->width) {
242     case 16:
243       ms = gst_cutter_16bit_ms (in_data, GST_BUFFER_SIZE (buf) / 2);
244       break;
245     case 8:
246       ms = gst_cutter_8bit_ms ((gint8 *) in_data, GST_BUFFER_SIZE (buf));
247       break;
248     default:
249       /* this shouldn't happen */
250       g_print ("WARNING: no mean square function for width %d\n",
251           filter->width);
252       break;
253   }
254
255   silent_prev = filter->silent;
256
257   RMS = sqrt (ms) / (double) filter->max_sample;
258   /* if RMS below threshold, add buffer length to silent run length count
259    * if not, reset
260    */
261   GST_DEBUG ("buffer stats: ms %f, RMS %f, audio length %f",
262       ms, RMS, gst_audio_length (filter->srcpad, buf));
263   if (RMS < filter->threshold_level)
264     filter->silent_run_length += gst_audio_length (filter->srcpad, buf);
265   else {
266     filter->silent_run_length = 0.0;
267     filter->silent = FALSE;
268   }
269
270   if (filter->silent_run_length > filter->threshold_length)
271     /* it has been silent long enough, flag it */
272     filter->silent = TRUE;
273
274   /* has the silent status changed ? if so, send right signal
275    * and, if from silent -> not silent, flush pre_record buffer
276    */
277   if (filter->silent != silent_prev) {
278     if (filter->silent) {
279 /*      g_print ("DEBUG: cutter: cut to here, turning off out\n"); */
280       g_signal_emit (G_OBJECT (filter), gst_cutter_signals[CUT_STOP], 0);
281     } else {
282       gint count = 0;
283
284 /*      g_print ("DEBUG: cutter: start from here, turning on out\n"); */
285       /* first of all, flush current buffer */
286       g_signal_emit (G_OBJECT (filter), gst_cutter_signals[CUT_START], 0);
287       GST_DEBUG ("flushing buffer of length %.3f", filter->pre_run_length);
288       while (filter->pre_buffer) {
289         prebuf = (g_list_first (filter->pre_buffer))->data;
290         filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
291         gst_pad_push (filter->srcpad, GST_DATA (prebuf));
292         ++count;
293       }
294       GST_DEBUG ("flushed %d buffers", count);
295       filter->pre_run_length = 0.0;
296     }
297   }
298   /* now check if we have to send the new buffer to the internal buffer cache
299    * or to the srcpad */
300   if (filter->silent) {
301     /* we ref it before putting it in the pre_buffer */
302     /* FIXME: we shouldn't probably do this, because the buffer
303      * arrives reffed already; the plugin should just push it
304      * or unref it to make it disappear */
305     /*
306        gst_buffer_ref (buf);
307      */
308     filter->pre_buffer = g_list_append (filter->pre_buffer, buf);
309     filter->pre_run_length += gst_audio_length (filter->srcpad, buf);
310     while (filter->pre_run_length > filter->pre_length) {
311       prebuf = (g_list_first (filter->pre_buffer))->data;
312       g_assert (GST_IS_BUFFER (prebuf));
313       filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
314       filter->pre_run_length -= gst_audio_length (filter->srcpad, prebuf);
315       /* only pass buffers if we don't leak */
316       if (!filter->leaky)
317         gst_pad_push (filter->srcpad, GST_DATA (prebuf));
318       /* we unref it after getting it out of the pre_buffer */
319       gst_buffer_unref (prebuf);
320     }
321   } else
322     gst_pad_push (filter->srcpad, GST_DATA (buf));
323 }
324
325 static double inline
326 gst_cutter_16bit_ms (gint16 * data, guint num_samples)
327 #include "filter.func"
328      static double inline gst_cutter_8bit_ms (gint8 * data, guint num_samples)
329 #include "filter.func"
330      static void
331          gst_cutter_set_property (GObject * object, guint prop_id,
332     const GValue * value, GParamSpec * pspec)
333 {
334   GstCutter *filter;
335
336   g_return_if_fail (GST_IS_CUTTER (object));
337   filter = GST_CUTTER (object);
338
339   switch (prop_id) {
340     case ARG_THRESHOLD:
341       /* set the level */
342       filter->threshold_level = g_value_get_double (value);
343       GST_DEBUG ("DEBUG: set threshold level to %f", filter->threshold_level);
344       break;
345     case ARG_THRESHOLD_DB:
346       /* set the level given in dB
347        * value in dB = 20 * log (value)
348        * values in dB < 0 result in values between 0 and 1
349        */
350       filter->threshold_level = pow (10, g_value_get_double (value) / 20);
351       GST_DEBUG ("DEBUG: set threshold level to %f", filter->threshold_level);
352       break;
353     case ARG_RUN_LENGTH:
354       /* set the minimum length of the silent run required */
355       filter->threshold_length = g_value_get_double (value);
356       break;
357     case ARG_PRE_LENGTH:
358       /* set the length of the pre-record block */
359       filter->pre_length = g_value_get_double (value);
360       break;
361     case ARG_LEAKY:
362       /* set if the pre-record buffer is leaky or not */
363       filter->leaky = g_value_get_boolean (value);
364       break;
365     default:
366       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
367       break;
368   }
369 }
370
371 static void
372 gst_cutter_get_property (GObject * object, guint prop_id,
373     GValue * value, GParamSpec * pspec)
374 {
375   GstCutter *filter;
376
377   g_return_if_fail (GST_IS_CUTTER (object));
378   filter = GST_CUTTER (object);
379
380   switch (prop_id) {
381     case ARG_RUN_LENGTH:
382       g_value_set_double (value, filter->threshold_length);
383       break;
384     case ARG_THRESHOLD:
385       g_value_set_double (value, filter->threshold_level);
386       break;
387     case ARG_THRESHOLD_DB:
388       g_value_set_double (value, 20 * log (filter->threshold_level));
389       break;
390     case ARG_PRE_LENGTH:
391       g_value_set_double (value, filter->pre_length);
392       break;
393     case ARG_LEAKY:
394       g_value_set_boolean (value, filter->leaky);
395       break;
396     default:
397       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
398       break;
399   }
400 }
401
402 static gboolean
403 plugin_init (GstPlugin * plugin)
404 {
405   /* load audio support library */
406   if (!gst_library_load ("gstaudio"))
407     return FALSE;
408
409   if (!gst_element_register (plugin, "cutter", GST_RANK_NONE, GST_TYPE_CUTTER))
410     return FALSE;
411
412   return TRUE;
413 }
414
415 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
416     GST_VERSION_MINOR,
417     "cutter",
418     "Audio Cutter to split audio into non-silent bits",
419     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
420
421      void gst_cutter_get_caps (GstPad * pad, GstCutter * filter)
422 {
423   const GstCaps *caps = NULL;
424   GstStructure *structure;
425
426   caps = GST_PAD_CAPS (pad);
427   /* FIXME : Please change this to a better warning method ! */
428   g_assert (caps != NULL);
429   if (caps == NULL)
430     printf ("WARNING: get_caps: Could not get caps of pad !\n");
431   structure = gst_caps_get_structure (caps, 0);
432   gst_structure_get_int (structure, "width", &filter->width);
433   filter->max_sample = gst_audio_highest_sample_value (pad);
434   filter->have_caps = TRUE;
435 }