much build fixage turns out synaesthesia and smoothwav depend on gtk, maybe they...
[platform/upstream/gstreamer.git] / gst / cutter / gstcutter.c
1 /* Gnome-Streamer
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 #include <gst/gst.h>
21 #include <gst/audio/audio.h>
22 #include "gstcutter.h"
23 #include "math.h"
24
25
26 static GstElementDetails cutter_details = {
27   "Cutter",
28   "Filter/Effect",
29   "Audio Cutter to split audio into non-silent bits",
30   VERSION,
31   "Thomas <thomas@apestaart.org>",
32   "(C) 2001",
33 };
34
35
36 /* Filter signals and args */
37 enum {
38   /* FILL ME */
39   CUT_START,
40   CUT_STOP,
41   LAST_SIGNAL
42 };
43
44 enum {
45   ARG_0,
46   ARG_THRESHOLD,
47   ARG_THRESHOLD_DB,
48   ARG_RUN_LENGTH,
49   ARG_PRE_LENGTH
50 };
51
52 GST_PADTEMPLATE_FACTORY (cutter_src_factory,
53   "src",
54   GST_PAD_SRC,
55   GST_PAD_ALWAYS,
56   GST_CAPS_NEW (
57     "test_src",
58     "audio/raw",
59       "channels", GST_PROPS_INT_RANGE (1, 2)
60   )
61 );
62
63 GST_PADTEMPLATE_FACTORY (cutter_sink_factory,
64   "sink",
65   GST_PAD_SINK,
66   GST_PAD_ALWAYS,
67   GST_CAPS_NEW (
68     "test_src",
69     "audio/raw",
70       "channels", GST_PROPS_INT_RANGE (1, 2)
71   )
72 );
73
74 static void             gst_cutter_class_init           (GstCutterClass *klass);
75 static void             gst_cutter_init                 (GstCutter *filter);
76
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);
81
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);
85
86 void                    gst_cutter_get_caps             (GstPad *pad, GstCutter* filter);
87
88 static GstElementClass *parent_class = NULL;
89 static guint gst_cutter_signals[LAST_SIGNAL] = { 0 };
90
91 static GstPadNegotiateReturn
92 cutter_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data)
93 {
94   GstCutter* filter = GST_CUTTER (gst_pad_get_parent (pad));
95   
96   if (*caps==NULL) 
97     return GST_PAD_NEGOTIATE_FAIL;
98   
99   return gst_pad_negotiate_proxy(pad,filter->sinkpad,caps);
100 }
101
102 static GstPadNegotiateReturn
103 cutter_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data)
104 {
105   GstCutter* filter = GST_CUTTER (gst_pad_get_parent (pad));
106   
107   if (*caps==NULL) 
108     return GST_PAD_NEGOTIATE_FAIL;
109   
110   return gst_pad_negotiate_proxy(pad,filter->srcpad,caps);
111 }               
112
113 GType
114 gst_cutter_get_type(void) {
115   static GType cutter_type = 0;
116
117   if (!cutter_type) {
118     static const GTypeInfo cutter_info = {
119       sizeof(GstCutterClass),      NULL,      NULL,      (GClassInitFunc)gst_cutter_class_init,
120       NULL,
121       NULL,
122       sizeof(GstCutter),
123       0,
124       (GInstanceInitFunc)gst_cutter_init,
125     };
126     cutter_type = g_type_register_static(GST_TYPE_ELEMENT, "GstCutter", &cutter_info, 0);
127   }
128   return cutter_type;
129 }
130
131 static void
132 gst_cutter_class_init (GstCutterClass *klass)
133 {
134   GObjectClass *gobject_class;
135   GstElementClass *gstelement_class;
136
137   gobject_class = (GObjectClass*) klass;
138   gstelement_class = (GstElementClass*) klass;
139
140   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
141
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
151
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);
163
164
165   gobject_class->set_property = gst_cutter_set_property;
166   gobject_class->get_property = gst_cutter_get_property;
167 }
168
169 static void
170 gst_cutter_init (GstCutter *filter)
171 {
172   filter->sinkpad = gst_pad_new_from_template (cutter_sink_factory (),"sink");
173   filter->srcpad = gst_pad_new_from_template (cutter_src_factory (),"src");
174
175   filter->threshold_level = 0.1;
176   filter->threshold_length = 0.5;
177   filter->silent_run_length = 0.0;
178   filter->silent = TRUE;
179
180   filter->pre_length = 0.2;
181   filter->pre_run_length = 0.0;
182   filter->pre_buffer = NULL;
183   
184   gst_pad_set_negotiate_function (filter->sinkpad,cutter_negotiate_sink);
185   gst_pad_set_negotiate_function (filter->srcpad,cutter_negotiate_src);
186
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);
191 }
192
193 static void
194 gst_cutter_chain (GstPad *pad, GstBuffer *buf)
195 {
196   GstCutter *filter;
197   gint16 *in_data;
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 */
202  
203   g_return_if_fail (pad != NULL);
204   g_return_if_fail (GST_IS_PAD (pad));
205   g_return_if_fail (buf != NULL);
206
207   filter = GST_CUTTER (GST_OBJECT_PARENT (pad));
208   g_return_if_fail (filter != NULL);
209   g_return_if_fail (GST_IS_CUTTER (filter));
210
211   g_return_if_fail (gst_audio_is_buffer_framed (pad, buf) == TRUE);
212
213   if (!filter->have_caps) gst_cutter_get_caps (pad, filter);
214
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);
218
219   /* calculate mean square value on buffer */
220   switch (filter->width) 
221   {
222     case 16:
223       ms = gst_cutter_16bit_ms (in_data, GST_BUFFER_SIZE (buf) / 2);
224       break;
225     case 8:
226       ms = gst_cutter_8bit_ms ((gint8 *) in_data, GST_BUFFER_SIZE (buf));
227       break;
228     default:
229       /* this shouldn't happen */
230       g_print ("WARNING: no mean square function for width %d\n",
231                 filter->width);
232       break;
233   }
234
235   silent_prev = filter->silent;
236
237   RMS = sqrt (ms) / (double) filter->max_sample;
238   /* if RMS below threshold, add buffer length to silent run length count 
239    * if not, reset
240    */
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);
244   else
245   {  
246     filter->silent_run_length = 0.0;
247     filter->silent = FALSE;
248   }
249
250   if (filter->silent_run_length > filter->threshold_length)
251     /* it has been silent long enough, flag it */
252     filter->silent = TRUE;
253
254   /* has the silent status changed ? if so, send right signal 
255    * and, if from silent -> not silent, flush pre_record buffer 
256    */
257   if (filter->silent != silent_prev)
258   {
259     if (filter->silent)
260     {
261 //      g_print ("DEBUG: cutter: cut to here, turning off out\n");
262       gtk_signal_emit (G_OBJECT (filter), gst_cutter_signals[CUT_STOP]);
263     }
264     else
265     {
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)
271       {
272         g_print (".");
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;
277       }
278       g_print ("\n");
279     } 
280   }
281   /* now check if we have to add the new buffer to the cache or to the pad */
282   if (filter->silent)
283   {
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)
287       {
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);
292       }
293   }
294   else
295     gst_pad_push (filter->srcpad, buf);
296 }
297
298 static double inline
299 gst_cutter_16bit_ms (gint16* data, guint num_samples)
300 #include "filter.func"
301
302 static double inline
303 gst_cutter_8bit_ms (gint8* data, guint num_samples)
304 #include "filter.func"
305
306 static void
307 gst_cutter_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
308 {
309   GstCutter *filter;
310
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);
314
315   switch (prop_id)
316   {
317     case ARG_THRESHOLD:
318         /* set the level */
319       filter->threshold_level = g_value_get_double (value);
320       g_print ("DEBUG: cutter: set threshold level to %f\n",
321                 filter->threshold_level);
322       break;
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
327        */
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);
331       break;
332     case ARG_RUN_LENGTH:
333       /* set the minimum length of the silent run required */
334       filter->threshold_length = g_value_get_double (value);
335       break;    
336     case ARG_PRE_LENGTH:
337       /* set the length of the pre-record block */
338       filter->pre_length = g_value_get_double (value);
339       break;
340     default:
341       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
342       break;
343   }
344 }
345
346 static void
347 gst_cutter_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
348 {
349   GstCutter *filter;
350
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);
354
355   switch (prop_id)
356   {
357     case ARG_RUN_LENGTH:
358      g_value_set_double (value, filter->threshold_length);
359      break;
360     case ARG_THRESHOLD:
361      g_value_set_double (value, filter->threshold_level);
362      break;
363     case ARG_THRESHOLD_DB:
364      g_value_set_double (value, 20 * log (filter->threshold_level));
365      break;
366     case ARG_PRE_LENGTH:
367       g_value_set_double (value, filter->pre_length);
368       break;
369     default:
370       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371       break;
372   }
373 }
374
375 static gboolean
376 plugin_init (GModule *module, GstPlugin *plugin)
377 {
378   GstElementFactory *factory;
379
380   factory = gst_elementfactory_new("cutter",GST_TYPE_CUTTER,
381                                    &cutter_details);
382   g_return_val_if_fail(factory != NULL, FALSE);
383   
384   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (cutter_src_factory));
385   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (cutter_sink_factory));
386
387   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
388
389   /* load audio support library */
390   if (!gst_library_load ("gstaudio"))
391   {
392     gst_info ("cutter: could not load support library: 'gstaudio'\n");
393     return FALSE;
394   }
395
396   return TRUE;
397 }
398
399 GstPluginDesc plugin_desc = 
400 {
401   GST_VERSION_MAJOR,
402   GST_VERSION_MINOR,
403   "cutter",
404   plugin_init
405 };
406
407 void
408 gst_cutter_get_caps (GstPad *pad, GstCutter* filter)
409 {
410   GstCaps *caps = NULL;
411
412   caps = GST_PAD_CAPS (pad);
413     // FIXME : Please change this to a better warning method !
414   if (caps == NULL)
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;
419 }