gstcutter.c 17 KB
Newer Older
Andy Wingo's avatar
Andy Wingo committed
1
/* GStreamer
Andy Wingo's avatar
Andy Wingo committed
2
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
3 4
 * Copyright (C) 2002,2003,2005
 *           Thomas Vander Stichele <thomas at apestaart dot org>
Andy Wingo's avatar
Andy Wingo committed
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
18 19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Andy Wingo's avatar
Andy Wingo committed
20
 */
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
/**
 * SECTION:element-cutter
 *
 * Analyses the audio signal for periods of silence. The start and end of
 * silence is signalled by bus messages named
 * <classname>&quot;cutter&quot;</classname>.
 * The message's structure contains two fields:
 * <itemizedlist>
 * <listitem>
 *   <para>
 *   #GstClockTime
 *   <classname>&quot;timestamp&quot;</classname>:
 *   the timestamp of the buffer that triggered the message.
 *   </para>
 * </listitem>
 * <listitem>
 *   <para>
 *   gboolean
 *   <classname>&quot;above&quot;</classname>:
 *   %TRUE for begin of silence and %FALSE for end of silence.
 *   </para>
 * </listitem>
 * </itemizedlist>
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
48
 * gst-launch-1.0 -m filesrc location=foo.ogg ! decodebin ! audioconvert ! cutter ! autoaudiosink
49 50 51
 * ]| Show cut messages.
 * </refsect2>
 */
Andy Wingo's avatar
Andy Wingo committed
52

53 54 55
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Andy Wingo's avatar
Andy Wingo committed
56
#include <gst/gst.h>
57 58
#include <gst/audio/audio.h>
#include "gstcutter.h"
Andy Wingo's avatar
Andy Wingo committed
59 60
#include "math.h"

61
GST_DEBUG_CATEGORY_STATIC (cutter_debug);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
62 63 64 65 66 67
#define GST_CAT_DEFAULT cutter_debug

#define CUTTER_DEFAULT_THRESHOLD_LEVEL    0.1
#define CUTTER_DEFAULT_THRESHOLD_LENGTH  (500 * GST_MSECOND)
#define CUTTER_DEFAULT_PRE_LENGTH        (200 * GST_MSECOND)

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68 69 70
static GstStaticPadTemplate cutter_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
71
    GST_STATIC_CAPS ("audio/x-raw, "
72
        "format = (string) { S8," GST_AUDIO_NE (S16) " }, "
73 74
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ], "
        "layout = (string) interleaved")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
75
    );
Andy Wingo's avatar
Andy Wingo committed
76

David Schleef's avatar
David Schleef committed
77
static GstStaticPadTemplate cutter_sink_factory =
78
GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
79 80
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
81
    GST_STATIC_CAPS ("audio/x-raw, "
82
        "format = (string) { S8," GST_AUDIO_NE (S16) " }, "
83 84
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ], "
        "layout = (string) interleaved")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
85 86
    );

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
87 88 89 90 91 92 93 94 95 96
enum
{
  PROP_0,
  PROP_THRESHOLD,
  PROP_THRESHOLD_DB,
  PROP_RUN_LENGTH,
  PROP_PRE_LENGTH,
  PROP_LEAKY
};

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
97 98
#define gst_cutter_parent_class parent_class
G_DEFINE_TYPE (GstCutter, gst_cutter, GST_TYPE_ELEMENT);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
99

Vineeth TM's avatar
Vineeth TM committed
100 101 102
static GstStateChangeReturn
gst_cutter_change_state (GstElement * element, GstStateChange transition);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
103 104 105 106 107
static void gst_cutter_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_cutter_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

Wim Taymans's avatar
Wim Taymans committed
108 109 110 111
static gboolean gst_cutter_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
static GstFlowReturn gst_cutter_chain (GstPad * pad, GstObject * parent,
    GstBuffer * buffer);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
112

Andy Wingo's avatar
Andy Wingo committed
113
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
114
gst_cutter_class_init (GstCutterClass * klass)
Andy Wingo's avatar
Andy Wingo committed
115 116
{
  GObjectClass *gobject_class;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
117
  GstElementClass *element_class;
Andy Wingo's avatar
Andy Wingo committed
118

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
119
  gobject_class = (GObjectClass *) klass;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
120
  element_class = (GstElementClass *) klass;
Andy Wingo's avatar
Andy Wingo committed
121

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
122 123
  gobject_class->set_property = gst_cutter_set_property;
  gobject_class->get_property = gst_cutter_get_property;
Andy Wingo's avatar
Andy Wingo committed
124

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
125
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_THRESHOLD,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
126
      g_param_spec_double ("threshold", "Threshold",
127
          "Volume threshold before trigger",
128 129
          -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
130 131
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_THRESHOLD_DB,
      g_param_spec_double ("threshold-dB", "Threshold (dB)",
132
          "Volume threshold before trigger (in dB)",
133 134
          -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
135 136 137
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RUN_LENGTH,
      g_param_spec_uint64 ("run-length", "Run length",
          "Length of drop below threshold before cut_stop (in nanoseconds)",
138
          0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
139 140 141
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PRE_LENGTH,
      g_param_spec_uint64 ("pre-length", "Pre-recording buffer length",
          "Length of pre-recording buffer (in nanoseconds)",
142
          0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
143
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEAKY,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144
      g_param_spec_boolean ("leaky", "Leaky",
145
          "do we leak buffers when below threshold ?",
146
          FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Andy Wingo's avatar
Andy Wingo committed
147

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
148
  GST_DEBUG_CATEGORY_INIT (cutter_debug, "cutter", 0, "Audio cutting");
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
149 150 151 152 153

  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&cutter_src_factory));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&cutter_sink_factory));
154
  gst_element_class_set_static_metadata (element_class, "Audio cutter",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
155 156 157
      "Filter/Editor/Audio",
      "Audio Cutter to split audio into non-silent bits",
      "Thomas Vander Stichele <thomas at apestaart dot org>");
Vineeth TM's avatar
Vineeth TM committed
158
  element_class->change_state = gst_cutter_change_state;
Andy Wingo's avatar
Andy Wingo committed
159 160 161
}

static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
162
gst_cutter_init (GstCutter * filter)
Andy Wingo's avatar
Andy Wingo committed
163
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
164
  filter->sinkpad =
165
      gst_pad_new_from_static_template (&cutter_sink_factory, "sink");
166 167 168 169 170
  gst_pad_set_chain_function (filter->sinkpad, gst_cutter_chain);
  gst_pad_set_event_function (filter->sinkpad, gst_cutter_event);
  gst_pad_use_fixed_caps (filter->sinkpad);
  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
171
  filter->srcpad =
172
      gst_pad_new_from_static_template (&cutter_src_factory, "src");
173 174
  gst_pad_use_fixed_caps (filter->srcpad);
  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
Andy Wingo's avatar
Andy Wingo committed
175

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
176 177 178
  filter->threshold_level = CUTTER_DEFAULT_THRESHOLD_LEVEL;
  filter->threshold_length = CUTTER_DEFAULT_THRESHOLD_LENGTH;
  filter->silent_run_length = 0 * GST_SECOND;
Andy Wingo's avatar
Andy Wingo committed
179
  filter->silent = TRUE;
180
  filter->silent_prev = FALSE;  /* previous value of silent */
Andy Wingo's avatar
Andy Wingo committed
181

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
182 183
  filter->pre_length = CUTTER_DEFAULT_PRE_LENGTH;
  filter->pre_run_length = 0 * GST_SECOND;
Andy Wingo's avatar
Andy Wingo committed
184
  filter->pre_buffer = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
185
  filter->leaky = FALSE;
Andy Wingo's avatar
Andy Wingo committed
186 187
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188 189 190 191 192 193 194 195 196 197 198 199
static GstMessage *
gst_cutter_message_new (GstCutter * c, gboolean above, GstClockTime timestamp)
{
  GstStructure *s;

  s = gst_structure_new ("cutter",
      "above", G_TYPE_BOOLEAN, above,
      "timestamp", GST_TYPE_CLOCK_TIME, timestamp, NULL);

  return gst_message_new_element (GST_OBJECT (c), s);
}

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
/* Calculate the Normalized Cumulative Square over a buffer of the given type
 * and over all channels combined */

#define DEFINE_CUTTER_CALCULATOR(TYPE, RESOLUTION)                            \
static void inline                                                            \
gst_cutter_calculate_##TYPE (TYPE * in, guint num,                            \
                            double *NCS)                                      \
{                                                                             \
  register int j;                                                             \
  double squaresum = 0.0;           /* square sum of the integer samples */   \
  register double square = 0.0;     /* Square */                              \
  gdouble normalizer;               /* divisor to get a [-1.0, 1.0] range */  \
                                                                              \
  *NCS = 0.0;                       /* Normalized Cumulative Square */        \
                                                                              \
  normalizer = (double) (1 << (RESOLUTION * 2));                              \
                                                                              \
  for (j = 0; j < num; j++)                                                   \
  {                                                                           \
    square = ((double) in[j]) * in[j];                                        \
    squaresum += square;                                                      \
  }                                                                           \
                                                                              \
                                                                              \
  *NCS = squaresum / normalizer;                                              \
}

DEFINE_CUTTER_CALCULATOR (gint16, 15);
DEFINE_CUTTER_CALCULATOR (gint8, 7);

230 231 232 233 234 235 236 237 238 239 240 241 242
static gboolean
gst_cutter_setcaps (GstCutter * filter, GstCaps * caps)
{
  GstAudioInfo info;

  if (!gst_audio_info_from_caps (&info, caps))
    return FALSE;

  filter->info = info;

  return gst_pad_set_caps (filter->srcpad, caps);
}

Vineeth TM's avatar
Vineeth TM committed
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
static GstStateChangeReturn
gst_cutter_change_state (GstElement * element, GstStateChange transition)
{
  GstStateChangeReturn ret;
  GstCutter *filter = GST_CUTTER (element);

  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      g_list_free_full (filter->pre_buffer, (GDestroyNotify) gst_buffer_unref);
      filter->pre_buffer = NULL;
      break;
    default:
      break;
  }
  return ret;
}

262
static gboolean
Wim Taymans's avatar
Wim Taymans committed
263
gst_cutter_event (GstPad * pad, GstObject * parent, GstEvent * event)
264 265 266 267
{
  gboolean ret;
  GstCutter *filter;

Wim Taymans's avatar
Wim Taymans committed
268
  filter = GST_CUTTER (parent);
269 270 271 272 273 274 275 276

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      ret = gst_cutter_setcaps (filter, caps);
Wim Taymans's avatar
Wim Taymans committed
277
      gst_event_unref (event);
278 279 280
      break;
    }
    default:
Wim Taymans's avatar
Wim Taymans committed
281
      ret = gst_pad_event_default (pad, parent, event);
282 283 284 285
      break;
  }
  return ret;
}
286

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
287
static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
288
gst_cutter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
Andy Wingo's avatar
Andy Wingo committed
289
{
290
  GstFlowReturn ret = GST_FLOW_OK;
Andy Wingo's avatar
Andy Wingo committed
291
  GstCutter *filter;
Wim Taymans's avatar
Wim Taymans committed
292
  GstMapInfo map;
Andy Wingo's avatar
Andy Wingo committed
293
  gint16 *in_data;
294
  gint bpf, rate;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
295
  gsize in_size;
296 297 298 299
  guint num_samples;
  gdouble NCS = 0.0;            /* Normalized Cumulative Square of buffer */
  gdouble RMS = 0.0;            /* RMS of signal in buffer */
  gdouble NMS = 0.0;            /* Normalized Mean Square of buffer */
300
  GstBuffer *prebuf;            /* pointer to a prebuffer element */
301
  GstClockTime duration;
Andy Wingo's avatar
Andy Wingo committed
302

Wim Taymans's avatar
Wim Taymans committed
303
  filter = GST_CUTTER (parent);
Andy Wingo's avatar
Andy Wingo committed
304

305 306 307 308 309 310
  if (GST_AUDIO_INFO_FORMAT (&filter->info) == GST_AUDIO_FORMAT_UNKNOWN)
    goto not_negotiated;

  bpf = GST_AUDIO_INFO_BPF (&filter->info);
  rate = GST_AUDIO_INFO_RATE (&filter->info);

Wim Taymans's avatar
Wim Taymans committed
311 312 313
  gst_buffer_map (buf, &map, GST_MAP_READ);
  in_data = (gint16 *) map.data;
  in_size = map.size;
Andy Wingo's avatar
Andy Wingo committed
314

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
315 316
  GST_LOG_OBJECT (filter, "length of prerec buffer: %" GST_TIME_FORMAT,
      GST_TIME_ARGS (filter->pre_run_length));
Andy Wingo's avatar
Andy Wingo committed
317 318

  /* calculate mean square value on buffer */
319 320
  switch (GST_AUDIO_INFO_FORMAT (&filter->info)) {
    case GST_AUDIO_FORMAT_S16:
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
321
      num_samples = in_size / 2;
322 323
      gst_cutter_calculate_gint16 (in_data, num_samples, &NCS);
      NMS = NCS / num_samples;
Andy Wingo's avatar
Andy Wingo committed
324
      break;
325
    case GST_AUDIO_FORMAT_S8:
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
326
      num_samples = in_size;
327 328
      gst_cutter_calculate_gint8 ((gint8 *) in_data, num_samples, &NCS);
      NMS = NCS / num_samples;
Andy Wingo's avatar
Andy Wingo committed
329 330 331
      break;
    default:
      /* this shouldn't happen */
332
      g_warning ("no mean square function for format");
Andy Wingo's avatar
Andy Wingo committed
333 334 335
      break;
  }

Wim Taymans's avatar
Wim Taymans committed
336
  gst_buffer_unmap (buf, &map);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
337

338
  filter->silent_prev = filter->silent;
Andy Wingo's avatar
Andy Wingo committed
339

340 341
  duration = gst_util_uint64_scale (in_size / bpf, GST_SECOND, rate);

342
  RMS = sqrt (NMS);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
343
  /* if RMS below threshold, add buffer length to silent run length count
Andy Wingo's avatar
Andy Wingo committed
344 345
   * if not, reset
   */
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
346
  GST_LOG_OBJECT (filter, "buffer stats: NMS %f, RMS %f, audio length %f", NMS,
347 348
      RMS, gst_guint64_to_gdouble (duration));

Andy Wingo's avatar
Andy Wingo committed
349
  if (RMS < filter->threshold_level)
350
    filter->silent_run_length += gst_guint64_to_gdouble (duration);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
351
  else {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
352
    filter->silent_run_length = 0 * GST_SECOND;
Andy Wingo's avatar
Andy Wingo committed
353 354 355 356 357 358 359
    filter->silent = FALSE;
  }

  if (filter->silent_run_length > filter->threshold_length)
    /* it has been silent long enough, flag it */
    filter->silent = TRUE;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
360 361
  /* has the silent status changed ? if so, send right signal
   * and, if from silent -> not silent, flush pre_record buffer
Andy Wingo's avatar
Andy Wingo committed
362
   */
363
  if (filter->silent != filter->silent_prev) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
364
    if (filter->silent) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
365 366 367 368
      GstMessage *m =
          gst_cutter_message_new (filter, FALSE, GST_BUFFER_TIMESTAMP (buf));
      GST_DEBUG_OBJECT (filter, "signaling CUT_STOP");
      gst_element_post_message (GST_ELEMENT (filter), m);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
369
    } else {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
370
      gint count = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
371 372
      GstMessage *m =
          gst_cutter_message_new (filter, TRUE, GST_BUFFER_TIMESTAMP (buf));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
373

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
374 375
      GST_DEBUG_OBJECT (filter, "signaling CUT_START");
      gst_element_post_message (GST_ELEMENT (filter), m);
Andy Wingo's avatar
Andy Wingo committed
376
      /* first of all, flush current buffer */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
377 378
      GST_DEBUG_OBJECT (filter, "flushing buffer of length %" GST_TIME_FORMAT,
          GST_TIME_ARGS (filter->pre_run_length));
379

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
380
      while (filter->pre_buffer) {
381 382
        prebuf = (g_list_first (filter->pre_buffer))->data;
        filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
383
        gst_pad_push (filter->srcpad, prebuf);
384
        ++count;
Andy Wingo's avatar
Andy Wingo committed
385
      }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386 387
      GST_DEBUG_OBJECT (filter, "flushed %d buffers", count);
      filter->pre_run_length = 0 * GST_SECOND;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
388
    }
Andy Wingo's avatar
Andy Wingo committed
389
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
390 391
  /* now check if we have to send the new buffer to the internal buffer cache
   * or to the srcpad */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
392 393
  if (filter->silent) {
    filter->pre_buffer = g_list_append (filter->pre_buffer, buf);
394 395
    filter->pre_run_length += gst_guint64_to_gdouble (duration);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
396
    while (filter->pre_run_length > filter->pre_length) {
397 398 399
      GstClockTime pduration;
      gsize psize;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
400 401
      prebuf = (g_list_first (filter->pre_buffer))->data;
      g_assert (GST_IS_BUFFER (prebuf));
402 403 404 405

      psize = gst_buffer_get_size (prebuf);
      pduration = gst_util_uint64_scale (psize / bpf, GST_SECOND, rate);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
406
      filter->pre_buffer = g_list_remove (filter->pre_buffer, prebuf);
407 408
      filter->pre_run_length -= gst_guint64_to_gdouble (pduration);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
409 410
      /* only pass buffers if we don't leak */
      if (!filter->leaky)
411
        ret = gst_pad_push (filter->srcpad, prebuf);
412 413
      else
        gst_buffer_unref (prebuf);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
414 415
    }
  } else
416
    ret = gst_pad_push (filter->srcpad, buf);
Andy Wingo's avatar
Andy Wingo committed
417

418
  return ret;
419

420 421 422 423
  /* ERRORS */
not_negotiated:
  {
    return GST_FLOW_NOT_NEGOTIATED;
424 425 426
  }
}

427 428
static void
gst_cutter_set_property (GObject * object, guint prop_id,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
429
    const GValue * value, GParamSpec * pspec)
Andy Wingo's avatar
Andy Wingo committed
430 431 432
{
  GstCutter *filter;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
433
  g_return_if_fail (GST_IS_CUTTER (object));
Andy Wingo's avatar
Andy Wingo committed
434 435
  filter = GST_CUTTER (object);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
436
  switch (prop_id) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
437
    case PROP_THRESHOLD:
Andy Wingo's avatar
Andy Wingo committed
438
      filter->threshold_level = g_value_get_double (value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
439
      GST_DEBUG ("DEBUG: set threshold level to %f", filter->threshold_level);
Andy Wingo's avatar
Andy Wingo committed
440
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
441
    case PROP_THRESHOLD_DB:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
442 443
      /* set the level given in dB
       * value in dB = 20 * log (value)
Andy Wingo's avatar
Andy Wingo committed
444 445 446
       * values in dB < 0 result in values between 0 and 1
       */
      filter->threshold_level = pow (10, g_value_get_double (value) / 20);
447 448
      GST_DEBUG_OBJECT (filter, "set threshold level to %f",
          filter->threshold_level);
Andy Wingo's avatar
Andy Wingo committed
449
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
450
    case PROP_RUN_LENGTH:
Andy Wingo's avatar
Andy Wingo committed
451
      /* set the minimum length of the silent run required */
452 453
      filter->threshold_length =
          gst_guint64_to_gdouble (g_value_get_uint64 (value));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
454
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
455
    case PROP_PRE_LENGTH:
Andy Wingo's avatar
Andy Wingo committed
456
      /* set the length of the pre-record block */
457
      filter->pre_length = gst_guint64_to_gdouble (g_value_get_uint64 (value));
Andy Wingo's avatar
Andy Wingo committed
458
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
459
    case PROP_LEAKY:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
460 461 462
      /* set if the pre-record buffer is leaky or not */
      filter->leaky = g_value_get_boolean (value);
      break;
Andy Wingo's avatar
Andy Wingo committed
463 464 465 466 467 468 469
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
470 471
gst_cutter_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
Andy Wingo's avatar
Andy Wingo committed
472 473 474
{
  GstCutter *filter;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
475
  g_return_if_fail (GST_IS_CUTTER (object));
Andy Wingo's avatar
Andy Wingo committed
476 477
  filter = GST_CUTTER (object);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
478
  switch (prop_id) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
479 480
    case PROP_RUN_LENGTH:
      g_value_set_uint64 (value, filter->threshold_length);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
481
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
482
    case PROP_THRESHOLD:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483 484
      g_value_set_double (value, filter->threshold_level);
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
485
    case PROP_THRESHOLD_DB:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
486 487
      g_value_set_double (value, 20 * log (filter->threshold_level));
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
488 489
    case PROP_PRE_LENGTH:
      g_value_set_uint64 (value, filter->pre_length);
Andy Wingo's avatar
Andy Wingo committed
490
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
491
    case PROP_LEAKY:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
492 493
      g_value_set_boolean (value, filter->leaky);
      break;
Andy Wingo's avatar
Andy Wingo committed
494 495 496 497 498 499 500
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
501
plugin_init (GstPlugin * plugin)
Andy Wingo's avatar
Andy Wingo committed
502
{
Iain Holmes's avatar
Iain Holmes committed
503 504 505
  if (!gst_element_register (plugin, "cutter", GST_RANK_NONE, GST_TYPE_CUTTER))
    return FALSE;

Andy Wingo's avatar
Andy Wingo committed
506 507 508
  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
509 510
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
511
    cutter,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
512
    "Audio Cutter to split audio into non-silent bits",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
513
    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);