Ejemplo n.º 1
0
static void
setup_peak_filter (GstIirEqualizer * equ, GstIirEqualizerBand * band)
{
  g_return_if_fail (GST_AUDIO_FILTER (equ)->format.rate);

  {
    gdouble gain, omega, bw;
    gdouble alpha, alpha1, alpha2, b0;

    gain = arg_to_scale (band->gain);
    omega = calculate_omega (band->freq, GST_AUDIO_FILTER (equ)->format.rate);
    bw = calculate_bw (band, GST_AUDIO_FILTER (equ)->format.rate);
    if (bw == 0.0)
      goto out;

    alpha = tan (bw / 2.0);

    alpha1 = alpha * gain;
    alpha2 = alpha / gain;

    b0 = (1.0 + alpha2);

    band->a0 = (1.0 + alpha1) / b0;
    band->a1 = (-2.0 * cos (omega)) / b0;
    band->a2 = (1.0 - alpha1) / b0;
    band->b1 = (2.0 * cos (omega)) / b0;
    band->b2 = -(1.0 - alpha2) / b0;

  out:
    GST_INFO
        ("gain = %5.1f, width= %7.2f, freq = %7.2f, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
        band->gain, band->width, band->freq, band->a0, band->a1, band->a2,
        band->b1, band->b2);
  }
}
Ejemplo n.º 2
0
static void
create_fingerprint (GstOFA * ofa)
{
  GstBuffer *buf;
  gint rate = GST_AUDIO_FILTER (ofa)->format.rate;
  gint channels = GST_AUDIO_FILTER (ofa)->format.channels;
  gint endianness =
      (GST_AUDIO_FILTER (ofa)->format.
      bigend) ? OFA_BIG_ENDIAN : OFA_LITTLE_ENDIAN;
  GstTagList *tags;

  GST_DEBUG ("Generating fingerprint");

  buf =
      gst_adapter_take_buffer (ofa->adapter,
      gst_adapter_available (ofa->adapter));

  ofa->fingerprint = g_strdup (ofa_create_print (GST_BUFFER_DATA (buf),
          endianness, GST_BUFFER_SIZE (buf) / 2, rate,
          (channels == 2) ? 1 : 0));

  GST_DEBUG ("Generated fingerprint");

  gst_buffer_unref (buf);

  tags = gst_tag_list_new ();
  gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
      GST_TAG_OFA_FINGERPRINT, ofa->fingerprint, NULL);
  gst_element_found_tags (GST_ELEMENT (ofa), tags);

  ofa->record = FALSE;
}
Ejemplo n.º 3
0
static void
create_fingerprint (GstOFA * ofa)
{
  GstBuffer *buf;
  GstAudioFilter *ofa_filter = GST_AUDIO_FILTER (ofa);
  gint rate = ofa_filter->format.rate;
  gint channels = ofa_filter->format.channels;
  gint endianness =
      ofa_filter->format.bigend ? OFA_BIG_ENDIAN : OFA_LITTLE_ENDIAN;
  GstTagList *tags;
  guint available;

  available = gst_adapter_available (ofa->adapter);

  if (available == 0) {
    GST_WARNING_OBJECT (ofa, "No data to take fingerprint from");
    ofa->record = FALSE;
    return;
  }

  if (GST_AUDIO_FILTER (ofa)->format.bigend)
    endianness = OFA_BIG_ENDIAN;
  else
    endianness = OFA_LITTLE_ENDIAN;


  GST_DEBUG_OBJECT (ofa, "Generating fingerprint for %u samples",
      available / 2);

  buf = gst_adapter_take_buffer (ofa->adapter, available);

  ofa->fingerprint = g_strdup (ofa_create_print (GST_BUFFER_DATA (buf),
          endianness, GST_BUFFER_SIZE (buf) / 2, rate,
          (channels == 2) ? 1 : 0));

  if (ofa->fingerprint) {
    GST_INFO_OBJECT (ofa, "Generated fingerprint: %s", ofa->fingerprint);
  } else {
    GST_WARNING_OBJECT (ofa, "Failed to generate fingerprint");
  }

  gst_buffer_unref (buf);

  if (ofa->fingerprint) {
    tags = gst_tag_list_new ();
    gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
        GST_TAG_OFA_FINGERPRINT, ofa->fingerprint, NULL);
    gst_element_found_tags (GST_ELEMENT (ofa), tags);

    g_object_notify (G_OBJECT (ofa), "fingerprint");
  }

  ofa->record = FALSE;
}
static GstFlowReturn
transform_ip (GstBaseTransform * trans, GstBuffer * buf)
{
  UnaryComplexPow *element = UNARY_COMPLEX_POW (trans);
  GstAudioFilter *audiofilter = GST_AUDIO_FILTER (trans);
  GstAudioFormat format = audiofilter->info.finfo->format;

  GstMapInfo info;
  gst_buffer_map (buf, &info, GST_MAP_READWRITE);
  gpointer data = info.data;
  gpointer data_end = data + info.size;

  const double n = element->exponent;

  if (format >= GST_AUDIO_FORMAT_F64) {
    double complex *ptr, *end = data_end;
    for (ptr = data; ptr < end; ptr++)
      *ptr = cpow (*ptr, n);
  } else if (format >= GST_AUDIO_FORMAT_F32) {
    float complex *ptr, *end = data_end;
    for (ptr = data; ptr < end; ptr++)
      *ptr = cpowf (*ptr, n);
  } else {
    g_assert_not_reached ();
  }

  gst_buffer_unmap (buf, &info);

  return GST_FLOW_OK;
}
static void
gst_spectrum_alloc_channel_data (GstSpectrum * spectrum)
{
  gint i;
  GstSpectrumChannel *cd;
  guint bands = spectrum->bands;
  guint nfft = 2 * bands - 2;

  g_assert (spectrum->channel_data == NULL);

  spectrum->num_channels = (spectrum->multi_channel) ?
      GST_AUDIO_FILTER (spectrum)->format.channels : 1;

  GST_DEBUG_OBJECT (spectrum, "allocating data for %d channels",
      spectrum->num_channels);

  spectrum->channel_data = g_new (GstSpectrumChannel, spectrum->num_channels);
  for (i = 0; i < spectrum->num_channels; i++) {
    cd = &spectrum->channel_data[i];
    cd->fft_ctx = gst_fft_f32_new (nfft, FALSE);
    cd->input = g_new0 (gfloat, nfft);
    cd->input_tmp = g_new0 (gfloat, nfft);
    cd->freqdata = g_new0 (GstFFTF32Complex, bands);
    cd->spect_magnitude = g_new0 (gfloat, bands);
    cd->spect_phase = g_new0 (gfloat, bands);
  }
}
Ejemplo n.º 6
0
static GstFlowReturn
gst_ofa_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
{
  GstOFA *ofa = GST_OFA (trans);
  GstAudioFilter *ofa_filter = GST_AUDIO_FILTER (ofa);
  guint64 nframes;
  GstClockTime duration;
  gint rate = ofa_filter->format.rate;
  gint channels = ofa_filter->format.channels;

  g_return_val_if_fail (rate > 0 && channels > 0, GST_FLOW_NOT_NEGOTIATED);

  if (!ofa->record)
    return GST_FLOW_OK;

  gst_adapter_push (ofa->adapter, gst_buffer_copy (buf));

  nframes = gst_adapter_available (ofa->adapter) / (channels * 2);
  duration = GST_FRAMES_TO_CLOCK_TIME (nframes, rate);

  if (duration >= 135 * GST_SECOND && ofa->fingerprint == NULL)
    create_fingerprint (ofa);

  return GST_FLOW_OK;
}
Ejemplo n.º 7
0
/* we override the state change vfunc here instead of GstBaseTransform's stop
 * vfunc, so GstAudioFilter-derived elements can override ::stop() for their
 * own purposes without having to worry about chaining up */
static GstStateChangeReturn
gst_audio_filter_change_state (GstElement * element, GstStateChange transition)
{
  GstStateChangeReturn ret;
  GstAudioFilter *filter;

  filter = GST_AUDIO_FILTER (element);

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      memset (&filter->format, 0, sizeof (GstRingBufferSpec));
      /* to make gst_buffer_spec_parse_caps() happy */
      filter->format.latency_time = GST_SECOND;
      break;
    default:
      break;
  }

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

  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
    case GST_STATE_CHANGE_READY_TO_NULL:
      gst_caps_replace (&filter->format.caps, NULL);
      break;
    default:
      break;
  }

  return ret;
}
Ejemplo n.º 8
0
static GstFlowReturn
gst_iir_equalizer_transform_ip (GstBaseTransform * btrans, GstBuffer * buf)
{
  GstAudioFilter *filter = GST_AUDIO_FILTER (btrans);

  GstIirEqualizer *equ = GST_IIR_EQUALIZER (btrans);

  GstClockTime timestamp;

  if (G_UNLIKELY (filter->format.channels < 1 || equ->process == NULL))
    return GST_FLOW_NOT_NEGOTIATED;

  if (equ->need_new_coefficients) {
    update_coefficients (equ);
    set_passthrough (equ);
  }

  if (gst_base_transform_is_passthrough (btrans))
    return GST_FLOW_OK;

  timestamp = GST_BUFFER_TIMESTAMP (buf);
  timestamp =
      gst_segment_to_stream_time (&btrans->segment, GST_FORMAT_TIME, timestamp);

  if (GST_CLOCK_TIME_IS_VALID (timestamp))
    gst_object_sync_values (G_OBJECT (equ), timestamp);

  equ->process (equ, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
      filter->format.channels);

  return GST_FLOW_OK;
}
Ejemplo n.º 9
0
/* GstBaseTransform vmethod implementations */
static GstFlowReturn
gst_audio_amplify_transform_ip (GstBaseTransform * base, GstBuffer * buf)
{
  GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (base);
  guint num_samples;
  GstClockTime timestamp, stream_time;

  timestamp = GST_BUFFER_TIMESTAMP (buf);
  stream_time =
      gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);

  GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (timestamp));

  if (GST_CLOCK_TIME_IS_VALID (stream_time))
    gst_object_sync_values (G_OBJECT (filter), stream_time);

  num_samples =
      GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (filter)->format.width / 8);

  if (gst_base_transform_is_passthrough (base) ||
      G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))
    return GST_FLOW_OK;

  filter->process (filter, GST_BUFFER_DATA (buf), num_samples);

  return GST_FLOW_OK;
}
Ejemplo n.º 10
0
/* GstBaseTransform vmethod implementations */
static GstFlowReturn
gst_audio_echo_transform_ip (GstBaseTransform * base, GstBuffer * buf)
{
  GstAudioEcho *self = GST_AUDIO_ECHO (base);
  guint num_samples;
  GstClockTime timestamp, stream_time;

  timestamp = GST_BUFFER_TIMESTAMP (buf);
  stream_time =
      gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);

  GST_DEBUG_OBJECT (self, "sync to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (timestamp));

  if (GST_CLOCK_TIME_IS_VALID (stream_time))
    gst_object_sync_values (G_OBJECT (self), stream_time);

  num_samples =
      GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (self)->format.width / 8);

  if (self->buffer == NULL) {
    guint width, rate, channels;

    width = GST_AUDIO_FILTER (self)->format.width / 8;
    rate = GST_AUDIO_FILTER (self)->format.rate;
    channels = GST_AUDIO_FILTER (self)->format.channels;

    self->delay_frames =
        MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1);
    self->buffer_size_frames =
        MAX (gst_util_uint64_scale (self->max_delay, rate, GST_SECOND), 1);

    self->buffer_size = self->buffer_size_frames * width * channels;
    self->buffer = g_try_malloc0 (self->buffer_size);
    self->buffer_pos = 0;

    if (self->buffer == NULL) {
      GST_ERROR_OBJECT (self, "Failed to allocate %u bytes", self->buffer_size);
      return GST_FLOW_ERROR;
    }
  }

  self->process (self, GST_BUFFER_DATA (buf), num_samples);

  return GST_FLOW_OK;
}
Ejemplo n.º 11
0
static gboolean
gst_audio_fx_base_fir_filter_query (GstPad * pad, GstQuery * query)
{
  GstAudioFXBaseFIRFilter *self =
      GST_AUDIO_FX_BASE_FIR_FILTER (gst_pad_get_parent (pad));
  gboolean res = TRUE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_LATENCY:
    {
      GstClockTime min, max;
      gboolean live;
      guint64 latency;
      GstPad *peer;
      gint rate = GST_AUDIO_FILTER (self)->format.rate;

      if (rate == 0) {
        res = FALSE;
      } else if ((peer = gst_pad_get_peer (GST_BASE_TRANSFORM (self)->sinkpad))) {
        if ((res = gst_pad_query (peer, query))) {
          gst_query_parse_latency (query, &live, &min, &max);

          GST_DEBUG_OBJECT (self, "Peer latency: min %"
              GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
              GST_TIME_ARGS (min), GST_TIME_ARGS (max));

          if (self->fft && !self->low_latency)
            latency = self->block_length - self->kernel_length + 1;
          else
            latency = self->latency;

          /* add our own latency */
          latency = gst_util_uint64_scale_round (latency, GST_SECOND, rate);

          GST_DEBUG_OBJECT (self, "Our latency: %"
              GST_TIME_FORMAT, GST_TIME_ARGS (latency));

          min += latency;
          if (max != GST_CLOCK_TIME_NONE)
            max += latency;

          GST_DEBUG_OBJECT (self, "Calculated total latency : min %"
              GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
              GST_TIME_ARGS (min), GST_TIME_ARGS (max));

          gst_query_set_latency (query, live, min, max);
        }
        gst_object_unref (peer);
      }
      break;
    }
    default:
      res = gst_pad_query_default (pad, query);
      break;
  }
  gst_object_unref (self);
  return res;
}
Ejemplo n.º 12
0
/* Must be called with transform lock! */
static void
alloc_history (GstIirEqualizer * equ)
{
  /* free + alloc = no memcpy */
  g_free (equ->history);
  equ->history =
      g_malloc0 (equ->history_size * GST_AUDIO_FILTER (equ)->format.channels *
      equ->freq_band_count);
}
Ejemplo n.º 13
0
static GstFlowReturn
gst_aubio_tempo_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
{
  uint j;
  GstAubioTempo *filter = GST_AUBIOTEMPO(trans);
  GstAudioFilter *audiofilter = GST_AUDIO_FILTER(trans);

  gint nsamples = GST_BUFFER_SIZE (buf) / (4 * audiofilter->format.channels);

  /* block loop */
  for (j = 0; j < nsamples; j++) {
    /* copy input to ibuf */
    fvec_write_sample(filter->ibuf, ((smpl_t *) GST_BUFFER_DATA(buf))[j],
        filter->pos);

    if (filter->pos == filter->hop_size - 1) {
      aubio_tempo_do(filter->t, filter->ibuf, filter->out);

      if (filter->out->data[0]> 0.) {
        gdouble now = GST_BUFFER_OFFSET (buf);
        // correction of inside buffer time
        now += (smpl_t)(j - filter->hop_size + 1);
        // correction of float period
        now += (filter->out->data[0] - 1.)*(smpl_t)filter->hop_size;

        if (filter->last_beat != -1 && now > filter->last_beat) {
          filter->bpm = 60./(GST_FRAMES_TO_CLOCK_TIME(now - filter->last_beat, audiofilter->format.rate))*1.e+9;
        } else {
          filter->bpm = 0.;
        }

        if (filter->silent == FALSE) {
          g_print ("beat: %f ", GST_FRAMES_TO_CLOCK_TIME( now, audiofilter->format.rate)*1.e-9);
          g_print ("| bpm: %f\n", filter->bpm);
        }

        GST_LOG_OBJECT (filter, "beat %" GST_TIME_FORMAT ", bpm %3.2f",
            GST_TIME_ARGS(now), filter->bpm);

        if (filter->message) {
          GstMessage *m = gst_aubio_tempo_message_new (filter, now);
          gst_element_post_message (GST_ELEMENT (filter), m);
        }

        filter->last_beat = now;
      }

      filter->pos = -1; /* so it will be zero next j loop */
    }
    filter->pos++;
  }

  return GST_FLOW_OK;
}
Ejemplo n.º 14
0
static void
gst_crossfeed_update_passthrough (GstCrossfeed * crossfeed)
{
  GstAudioFilter *filter = GST_AUDIO_FILTER (crossfeed);
  GstBaseTransform *trans = GST_BASE_TRANSFORM (crossfeed);
  gboolean passthrough;

  passthrough = (
      filter->format.channels != 2 ||
      !crossfeed->active) ? TRUE : FALSE;

  gst_base_transform_set_passthrough (trans, passthrough);
}
Ejemplo n.º 15
0
static GstFlowReturn
gst_iir_equalizer_transform_ip (GstBaseTransform * btrans, GstBuffer * buf)
{
    GstAudioFilter *filter = GST_AUDIO_FILTER (btrans);
    GstIirEqualizer *equ = GST_IIR_EQUALIZER (btrans);
    GstClockTime timestamp;
    GstMapInfo map;
    gint channels = GST_AUDIO_FILTER_CHANNELS (filter);
    gboolean need_new_coefficients;

    if (G_UNLIKELY (channels < 1 || equ->process == NULL))
        return GST_FLOW_NOT_NEGOTIATED;

    BANDS_LOCK (equ);
    need_new_coefficients = equ->need_new_coefficients;
    BANDS_UNLOCK (equ);

    if (!need_new_coefficients && gst_base_transform_is_passthrough (btrans))
        return GST_FLOW_OK;

    timestamp = GST_BUFFER_TIMESTAMP (buf);
    timestamp =
        gst_segment_to_stream_time (&btrans->segment, GST_FORMAT_TIME, timestamp);

    if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
        GstIirEqualizerBand **filters = equ->bands;
        guint f, nf = equ->freq_band_count;

        gst_object_sync_values (GST_OBJECT (equ), timestamp);

        /* sync values for bands too */
        /* FIXME: iterating equ->bands is not thread-safe here */
        for (f = 0; f < nf; f++) {
            gst_object_sync_values (GST_OBJECT (filters[f]), timestamp);
        }
    }

    BANDS_LOCK (equ);
    if (need_new_coefficients) {
        update_coefficients (equ);
        set_passthrough (equ);
    }
    BANDS_UNLOCK (equ);

    gst_buffer_map (buf, &map, GST_MAP_READWRITE);
    equ->process (equ, map.data, map.size, channels);
    gst_buffer_unmap (buf, &map);

    return GST_FLOW_OK;
}
Ejemplo n.º 16
0
static void
setup_high_shelf_filter (GstIirEqualizer * equ, GstIirEqualizerBand * band)
{
  g_return_if_fail (GST_AUDIO_FILTER (equ)->format.rate);

  {
    gdouble gain, omega, bw;
    gdouble alpha, delta, b0;
    gdouble egp, egm;

    gain = arg_to_scale (band->gain);
    omega = calculate_omega (band->freq, GST_AUDIO_FILTER (equ)->format.rate);
    bw = calculate_bw (band, GST_AUDIO_FILTER (equ)->format.rate);
    if (bw == 0.0)
      goto out;

    egm = gain - 1.0;
    egp = gain + 1.0;
    alpha = tan (bw / 2.0);

    delta = 2.0 * sqrt (gain) * alpha;
    b0 = egp - egm * cos (omega) + delta;

    band->a0 = ((egp + egm * cos (omega) + delta) * gain) / b0;
    band->a1 = ((egm + egp * cos (omega)) * -2.0 * gain) / b0;
    band->a2 = ((egp + egm * cos (omega) - delta) * gain) / b0;
    band->b1 = ((egm - egp * cos (omega)) * -2.0) / b0;
    band->b2 = -((egp - egm * cos (omega) - delta)) / b0;


  out:
    GST_INFO
        ("gain = %5.1f, width= %7.2f, freq = %7.2f, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
        band->gain, band->width, band->freq, band->a0, band->a1, band->a2,
        band->b1, band->b2);
  }
}
Ejemplo n.º 17
0
void
gst_audio_fx_base_fir_filter_set_kernel (GstAudioFXBaseFIRFilter * self,
    gdouble * kernel, guint kernel_length, guint64 latency)
{
  g_return_if_fail (kernel != NULL);
  g_return_if_fail (self != NULL);

  GST_BASE_TRANSFORM_LOCK (self);
  if (self->residue) {
    gst_audio_fx_base_fir_filter_push_residue (self);
    self->next_ts = GST_CLOCK_TIME_NONE;
    self->next_off = GST_BUFFER_OFFSET_NONE;
    self->residue_length = 0;
  }

  g_free (self->kernel);
  g_free (self->residue);

  self->kernel = kernel;
  self->kernel_length = kernel_length;

  if (GST_AUDIO_FILTER (self)->format.channels) {
    self->residue =
        g_new0 (gdouble,
        kernel_length * GST_AUDIO_FILTER (self)->format.channels);
    self->residue_length = 0;
  }

  if (self->latency != latency) {
    self->latency = latency;
    gst_element_post_message (GST_ELEMENT (self),
        gst_message_new_latency (GST_OBJECT (self)));
  }

  GST_BASE_TRANSFORM_UNLOCK (self);
}
Ejemplo n.º 18
0
static GstFlowReturn
gst_audio_filter_template_filter_inplace (GstBaseTransform * base_transform,
    GstBuffer * buf)
{
  GstAudioFilterTemplate *audio_filter_template;
  GstAudioFilter *audiofilter;

  audiofilter = GST_AUDIO_FILTER (base_transform);
  audio_filter_template = GST_AUDIO_FILTER_TEMPLATE (base_transform);

  /* do something interesting here.  This simply copies the source
   * to the destination. */

  return GST_FLOW_OK;
}
Ejemplo n.º 19
0
static GstFlowReturn
gst_chromaprint_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
{
  GstChromaprint *chromaprint = GST_CHROMAPRINT (trans);
  GstAudioFilter *filter = GST_AUDIO_FILTER (trans);
  GstMapInfo map_info;
  guint nsamples;
  gint rate, channels;

  rate = GST_AUDIO_INFO_RATE (&filter->info);
  channels = GST_AUDIO_INFO_CHANNELS (&filter->info);

  if (G_UNLIKELY (rate <= 0 || channels <= 0))
    return GST_FLOW_NOT_NEGOTIATED;

  if (!chromaprint->record)
    return GST_FLOW_OK;

  if (!gst_buffer_map (buf, &map_info, GST_MAP_READ))
    return GST_FLOW_ERROR;

  nsamples = map_info.size / (channels * 2);

  if (nsamples == 0)
    goto end;

  if (chromaprint->nsamples == 0) {
    chromaprint_start (chromaprint->context, rate, channels);
  }
  chromaprint->nsamples += nsamples;
  chromaprint->duration = chromaprint->nsamples / rate;

  chromaprint_feed (chromaprint->context, map_info.data,
      map_info.size / sizeof (guint16));

  if (chromaprint->duration >= chromaprint->max_duration
      && !chromaprint->fingerprint) {
    gst_chromaprint_create_fingerprint (chromaprint);
  }

end:
  gst_buffer_unmap (buf, &map_info);

  return GST_FLOW_OK;
}
Ejemplo n.º 20
0
static GstFlowReturn
gst_audio_filter_template_filter (GstBaseTransform * base_transform,
    GstBuffer * inbuf, GstBuffer * outbuf)
{
  GstAudioFilterTemplate *audio_filter_template;
  GstAudioFilter *audiofilter;

  audiofilter = GST_AUDIO_FILTER (base_transform);
  audio_filter_template = GST_AUDIO_FILTER_TEMPLATE (base_transform);

  /* do something interesting here.  This simply copies the source
   * to the destination. */

  memcpy (GST_BUFFER_DATA (outbuf), GST_BUFFER_DATA (inbuf),
      GST_BUFFER_SIZE (inbuf));

  return GST_FLOW_OK;
}
Ejemplo n.º 21
0
/* GstBaseTransform vmethod implementations */
static GstFlowReturn
gst_audio_amplify_transform_ip (GstBaseTransform * base, GstBuffer * buf)
{
  GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (base);
  guint num_samples =
      GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (filter)->format.width / 8);

  if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf)))
    gst_object_sync_values (G_OBJECT (filter), GST_BUFFER_TIMESTAMP (buf));

  if (gst_base_transform_is_passthrough (base) ||
      G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))
    return GST_FLOW_OK;

  filter->process (filter, GST_BUFFER_DATA (buf), num_samples);

  return GST_FLOW_OK;
}
Ejemplo n.º 22
0
static GstFlowReturn
gst_aubio_pitch_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
{
  uint j;
  GstAubioPitch *filter = GST_AUBIO_PITCH (trans);
  GstAudioFilter *audiofilter = GST_AUDIO_FILTER(trans);

  gint nsamples = GST_BUFFER_SIZE (buf) / (4 * audiofilter->format.channels);

  /* block loop */
  for (j = 0; j < nsamples; j++) {
    /* copy input to ibuf */
    fvec_write_sample(filter->ibuf, ((smpl_t *) GST_BUFFER_DATA(buf))[j],
        filter->pos);

    if (filter->pos == filter->hop_size - 1) {
      aubio_pitch_do(filter->t, filter->ibuf, filter->obuf);
      smpl_t pitch = filter->obuf->data[0];
      GstClockTime now = GST_BUFFER_TIMESTAMP (buf);
      // correction of inside buffer time
      now += GST_FRAMES_TO_CLOCK_TIME(j, audiofilter->format.rate);

      if (filter->silent == FALSE) {
        g_print ("%" GST_TIME_FORMAT "\tpitch: %.3f\n",
                GST_TIME_ARGS(now), pitch);
      }

      GST_LOG_OBJECT (filter, "pitch %" GST_TIME_FORMAT ", freq %3.2f",
              GST_TIME_ARGS(now), pitch);

      filter->pos = -1; /* so it will be zero next j loop */
    }
    filter->pos++;
  }

  return GST_FLOW_OK;
}
Ejemplo n.º 23
0
static gboolean
gst_audio_filter_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
    GstCaps * outcaps)
{
  GstAudioFilterClass *klass;
  GstAudioFilter *filter;
  gboolean ret = TRUE;

  filter = GST_AUDIO_FILTER (btrans);

  GST_LOG_OBJECT (filter, "caps: %" GST_PTR_FORMAT, incaps);

  if (!gst_ring_buffer_parse_caps (&filter->format, incaps)) {
    GST_WARNING_OBJECT (filter, "couldn't parse %" GST_PTR_FORMAT, incaps);
    return FALSE;
  }

  klass = GST_AUDIO_FILTER_CLASS (G_OBJECT_GET_CLASS (filter));

  if (klass->setup)
    ret = klass->setup (filter, &filter->format);

  return ret;
}
Ejemplo n.º 24
0
static void
gst_audio_wsinclimit_build_kernel (GstAudioWSincLimit * self)
{
  gint i = 0;
  gdouble sum = 0.0;
  gint len = 0;
  gdouble w;
  gdouble *kernel = NULL;

  len = self->kernel_length;

  if (GST_AUDIO_FILTER (self)->format.rate == 0) {
    GST_DEBUG ("rate not set yet");
    return;
  }

  if (GST_AUDIO_FILTER (self)->format.channels == 0) {
    GST_DEBUG ("channels not set yet");
    return;
  }

  /* Clamp cutoff frequency between 0 and the nyquist frequency */
  self->cutoff =
      CLAMP (self->cutoff, 0.0, GST_AUDIO_FILTER (self)->format.rate / 2);

  GST_DEBUG ("gst_audio_wsinclimit_: initializing filter kernel of length %d "
      "with cutoff %.2lf Hz "
      "for mode %s",
      len, self->cutoff,
      (self->mode == MODE_LOW_PASS) ? "low-pass" : "high-pass");

  /* fill the kernel */
  w = 2 * G_PI * (self->cutoff / GST_AUDIO_FILTER (self)->format.rate);

  kernel = g_new (gdouble, len);

  for (i = 0; i < len; ++i) {
    if (i == (len - 1) / 2.0)
      kernel[i] = w;
    else
      kernel[i] = sin (w * (i - (len - 1) / 2)) / (i - (len - 1) / 2.0);

    /* windowing */
    switch (self->window) {
      case WINDOW_HAMMING:
        kernel[i] *= (0.54 - 0.46 * cos (2 * G_PI * i / (len - 1)));
        break;
      case WINDOW_BLACKMAN:
        kernel[i] *= (0.42 - 0.5 * cos (2 * G_PI * i / (len - 1)) +
            0.08 * cos (4 * G_PI * i / (len - 1)));
        break;
      case WINDOW_GAUSSIAN:
        kernel[i] *= exp (-0.5 * POW2 (3.0 / len * (2 * i - (len - 1))));
        break;
      case WINDOW_COSINE:
        kernel[i] *= cos (G_PI * i / (len - 1) - G_PI / 2);
        break;
      case WINDOW_HANN:
        kernel[i] *= 0.5 * (1 - cos (2 * G_PI * i / (len - 1)));
        break;
    }
  }

  /* normalize for unity gain at DC */
  for (i = 0; i < len; ++i)
    sum += kernel[i];
  for (i = 0; i < len; ++i)
    kernel[i] /= sum;

  /* convert to highpass if specified */
  if (self->mode == MODE_HIGH_PASS) {
    for (i = 0; i < len; ++i)
      kernel[i] = -kernel[i];

    if (len % 2 == 1) {
      kernel[(len - 1) / 2] += 1.0;
    } else {
      kernel[len / 2 - 1] += 0.5;
      kernel[len / 2] += 0.5;
    }
  }

  gst_audio_fx_base_fir_filter_set_kernel (GST_AUDIO_FX_BASE_FIR_FILTER (self),
      kernel, self->kernel_length, (len - 1) / 2);
}
Ejemplo n.º 25
0
void
gst_audio_fx_base_fir_filter_push_residue (GstAudioFXBaseFIRFilter * self)
{
  GstBuffer *outbuf;
  GstFlowReturn res;
  gint rate = GST_AUDIO_FILTER (self)->format.rate;
  gint channels = GST_AUDIO_FILTER (self)->format.channels;
  gint outsize, outsamples;
  gint diffsize, diffsamples;
  guint8 *in, *out;

  if (channels == 0 || rate == 0) {
    self->residue_length = 0;
    return;
  }

  /* Calculate the number of samples and their memory size that
   * should be pushed from the residue */
  outsamples = MIN (self->latency, self->residue_length / channels);
  outsize = outsamples * channels * (GST_AUDIO_FILTER (self)->format.width / 8);
  if (outsize == 0) {
    self->residue_length = 0;
    return;
  }

  /* Process the difference between latency and residue_length samples
   * to start at the actual data instead of starting at the zeros before
   * when we only got one buffer smaller than latency */
  diffsamples = self->latency - self->residue_length / channels;
  diffsize =
      diffsamples * channels * (GST_AUDIO_FILTER (self)->format.width / 8);
  if (diffsize > 0) {
    in = g_new0 (guint8, diffsize);
    out = g_new0 (guint8, diffsize);
    self->process (self, in, out, diffsamples * channels);
    g_free (in);
    g_free (out);
  }

  res = gst_pad_alloc_buffer (GST_BASE_TRANSFORM (self)->srcpad,
      GST_BUFFER_OFFSET_NONE, outsize,
      GST_PAD_CAPS (GST_BASE_TRANSFORM (self)->srcpad), &outbuf);

  if (G_UNLIKELY (res != GST_FLOW_OK)) {
    GST_WARNING_OBJECT (self, "failed allocating buffer of %d bytes", outsize);
    self->residue_length = 0;
    return;
  }

  /* Convolve the residue with zeros to get the actual remaining data */
  in = g_new0 (guint8, outsize);
  self->process (self, in, GST_BUFFER_DATA (outbuf), outsamples * channels);
  g_free (in);

  /* Set timestamp, offset, etc from the values we
   * saved when processing the regular buffers */
  if (GST_CLOCK_TIME_IS_VALID (self->next_ts))
    GST_BUFFER_TIMESTAMP (outbuf) = self->next_ts;
  else
    GST_BUFFER_TIMESTAMP (outbuf) = 0;
  GST_BUFFER_DURATION (outbuf) =
      gst_util_uint64_scale (outsamples, GST_SECOND, rate);
  self->next_ts += gst_util_uint64_scale (outsamples, GST_SECOND, rate);

  if (self->next_off != GST_BUFFER_OFFSET_NONE) {
    GST_BUFFER_OFFSET (outbuf) = self->next_off;
    GST_BUFFER_OFFSET_END (outbuf) = self->next_off + outsamples;
    self->next_off = GST_BUFFER_OFFSET_END (outbuf);
  }

  GST_DEBUG_OBJECT (self, "Pushing residue buffer of size %d with timestamp: %"
      GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %lld,"
      " offset_end: %lld, nsamples: %d", GST_BUFFER_SIZE (outbuf),
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
      GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
      GST_BUFFER_OFFSET_END (outbuf), outsamples);

  res = gst_pad_push (GST_BASE_TRANSFORM (self)->srcpad, outbuf);

  if (G_UNLIKELY (res != GST_FLOW_OK)) {
    GST_WARNING_OBJECT (self, "failed to push residue");
  }

  self->residue_length = 0;
}
Ejemplo n.º 26
0
static GstFlowReturn
gst_audio_fx_base_fir_filter_transform (GstBaseTransform * base,
    GstBuffer * inbuf, GstBuffer * outbuf)
{
  GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (base);
  GstClockTime timestamp;
  gint channels = GST_AUDIO_FILTER (self)->format.channels;
  gint rate = GST_AUDIO_FILTER (self)->format.rate;
  gint input_samples =
      GST_BUFFER_SIZE (outbuf) / (GST_AUDIO_FILTER (self)->format.width / 8);
  gint output_samples = input_samples;
  gint diff = 0;

  timestamp = GST_BUFFER_TIMESTAMP (outbuf);
  if (!GST_CLOCK_TIME_IS_VALID (timestamp)) {
    GST_ERROR_OBJECT (self, "Invalid timestamp");
    return GST_FLOW_ERROR;
  }

  gst_object_sync_values (G_OBJECT (self), timestamp);

  g_return_val_if_fail (self->kernel != NULL, GST_FLOW_ERROR);
  g_return_val_if_fail (channels != 0, GST_FLOW_ERROR);

  if (!self->residue)
    self->residue = g_new0 (gdouble, self->kernel_length * channels);

  /* Reset the residue if already existing on discont buffers */
  if (GST_BUFFER_IS_DISCONT (inbuf) || (GST_CLOCK_TIME_IS_VALID (self->next_ts)
          && timestamp - gst_util_uint64_scale (MIN (self->latency,
                  self->residue_length / channels), GST_SECOND,
              rate) - self->next_ts > 5 * GST_MSECOND)) {
    GST_DEBUG_OBJECT (self, "Discontinuity detected - flushing");
    if (GST_CLOCK_TIME_IS_VALID (self->next_ts))
      gst_audio_fx_base_fir_filter_push_residue (self);
    self->residue_length = 0;
    self->next_ts = timestamp;
    self->next_off = GST_BUFFER_OFFSET (inbuf);
  } else if (!GST_CLOCK_TIME_IS_VALID (self->next_ts)) {
    self->next_ts = timestamp;
    self->next_off = GST_BUFFER_OFFSET (inbuf);
  }

  /* Calculate the number of samples we can push out now without outputting
   * latency zeros in the beginning */
  diff = self->latency * channels - self->residue_length;
  if (diff > 0)
    output_samples -= diff;

  self->process (self, GST_BUFFER_DATA (inbuf), GST_BUFFER_DATA (outbuf),
      input_samples);

  if (output_samples <= 0) {
    return GST_BASE_TRANSFORM_FLOW_DROPPED;
  }

  GST_BUFFER_TIMESTAMP (outbuf) = self->next_ts;
  GST_BUFFER_DURATION (outbuf) =
      gst_util_uint64_scale (output_samples / channels, GST_SECOND, rate);
  GST_BUFFER_OFFSET (outbuf) = self->next_off;
  if (GST_BUFFER_OFFSET_IS_VALID (outbuf))
    GST_BUFFER_OFFSET_END (outbuf) = self->next_off + output_samples / channels;
  else
    GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;

  if (output_samples < input_samples) {
    GST_BUFFER_DATA (outbuf) +=
        diff * (GST_AUDIO_FILTER (self)->format.width / 8);
    GST_BUFFER_SIZE (outbuf) -=
        diff * (GST_AUDIO_FILTER (self)->format.width / 8);
  }

  self->next_ts += GST_BUFFER_DURATION (outbuf);
  self->next_off = GST_BUFFER_OFFSET_END (outbuf);

  GST_DEBUG_OBJECT (self, "Pushing buffer of size %d with timestamp: %"
      GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %lld,"
      " offset_end: %lld, nsamples: %d", GST_BUFFER_SIZE (outbuf),
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
      GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
      GST_BUFFER_OFFSET_END (outbuf), output_samples / channels);

  return GST_FLOW_OK;
}
Ejemplo n.º 27
0
/* Filter taken from
 *
 * The Equivalence of Various Methods of Computing
 * Biquad Coefficients for Audio Parametric Equalizers
 *
 * by Robert Bristow-Johnson
 *
 * http://www.aes.org/e-lib/browse.cfm?elib=6326
 * http://www.musicdsp.org/files/EQ-Coefficients.pdf
 *
 * The bandwidth method that we use here is the preferred
 * one from this article transformed from octaves to frequency
 * in Hz.
 */
static void
setup_filter (GstIirEqualizer * equ, GstIirEqualizerBand * band)
{
  g_return_if_fail (GST_AUDIO_FILTER (equ)->format.rate);

  /* FIXME: we need better filters
   * - we need shelf-filter for 1st and last band
   */
  {
    gdouble gain, omega, bw;
    gdouble edge_gain, gamma;
    gdouble alpha, beta;

    gain = arg_to_scale (band->gain);

    if (band->freq / GST_AUDIO_FILTER (equ)->format.rate > 0.5)
      omega = M_PI;
    else if (band->freq < 0.0)
      omega = 0.0;
    else
      omega = 2.0 * M_PI * (band->freq / GST_AUDIO_FILTER (equ)->format.rate);

    if (band->width / GST_AUDIO_FILTER (equ)->format.rate >= 0.5) {
      /* If bandwidth == 0.5 the calculation below fails as tan(M_PI/2)
       * is undefined. So set the bandwidth to a slightly smaller value.
       */
      bw = M_PI - 0.00000001;
    } else if (band->width <= 0.0) {
      /* If bandwidth == 0 this band won't change anything so set
       * the coefficients accordingly. The coefficient calculation
       * below would create coefficients that for some reason amplify
       * the band.
       */
      band->a0 = 1.0;
      band->a1 = 0.0;
      band->a2 = 0.0;
      band->b1 = 0.0;
      band->b2 = 0.0;
      gain = 1.0;
      goto out;
    } else {
      bw = 2.0 * M_PI * (band->width / GST_AUDIO_FILTER (equ)->format.rate);
    }

    edge_gain = sqrt (gain);
    gamma = tan (bw / 2.0);

    alpha = gamma * edge_gain;
    beta = gamma / edge_gain;

    band->a0 = (1.0 + alpha) / (1.0 + beta);
    band->a1 = (-2.0 * cos (omega)) / (1.0 + beta);
    band->a2 = (1.0 - alpha) / (1.0 + beta);
    band->b1 = (2.0 * cos (omega)) / (1.0 + beta);
    band->b2 = -(1.0 - beta) / (1.0 + beta);

  out:
    GST_INFO
        ("gain = %5.1f, width= %7.2f, freq = %7.2f, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
        band->gain, band->width, band->freq, band->a0, band->a1, band->a2,
        band->b1, band->b2);
  }
}
Ejemplo n.º 28
0
static void
gst_audio_wsincband_build_kernel (GstAudioWSincBand * self)
{
  gint i = 0;
  gdouble sum = 0.0;
  gint len = 0;
  gdouble *kernel_lp, *kernel_hp;
  gdouble w;
  gdouble *kernel;

  len = self->kernel_length;

  if (GST_AUDIO_FILTER (self)->format.rate == 0) {
    GST_DEBUG ("rate not set yet");
    return;
  }

  if (GST_AUDIO_FILTER (self)->format.channels == 0) {
    GST_DEBUG ("channels not set yet");
    return;
  }

  /* Clamp frequencies */
  self->lower_frequency =
      CLAMP (self->lower_frequency, 0.0,
      GST_AUDIO_FILTER (self)->format.rate / 2);
  self->upper_frequency =
      CLAMP (self->upper_frequency, 0.0,
      GST_AUDIO_FILTER (self)->format.rate / 2);
  if (self->lower_frequency > self->upper_frequency) {
    gint tmp = self->lower_frequency;

    self->lower_frequency = self->upper_frequency;
    self->upper_frequency = tmp;
  }

  GST_DEBUG ("gst_audio_wsincband: initializing filter kernel of length %d "
      "with lower frequency %.2lf Hz "
      ", upper frequency %.2lf Hz for mode %s",
      len, self->lower_frequency, self->upper_frequency,
      (self->mode == MODE_BAND_PASS) ? "band-pass" : "band-reject");

  /* fill the lp kernel */
  w = 2 * M_PI * (self->lower_frequency / GST_AUDIO_FILTER (self)->format.rate);
  kernel_lp = g_new (gdouble, len);
  for (i = 0; i < len; ++i) {
    if (i == len / 2)
      kernel_lp[i] = w;
    else
      kernel_lp[i] = sin (w * (i - len / 2))
          / (i - len / 2);
    /* Windowing */
    if (self->window == WINDOW_HAMMING)
      kernel_lp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
    else
      kernel_lp[i] *=
          (0.42 - 0.5 * cos (2 * M_PI * i / len) +
          0.08 * cos (4 * M_PI * i / len));
  }

  /* normalize for unity gain at DC */
  sum = 0.0;
  for (i = 0; i < len; ++i)
    sum += kernel_lp[i];
  for (i = 0; i < len; ++i)
    kernel_lp[i] /= sum;

  /* fill the hp kernel */
  w = 2 * M_PI * (self->upper_frequency / GST_AUDIO_FILTER (self)->format.rate);
  kernel_hp = g_new (gdouble, len);
  for (i = 0; i < len; ++i) {
    if (i == len / 2)
      kernel_hp[i] = w;
    else
      kernel_hp[i] = sin (w * (i - len / 2))
          / (i - len / 2);
    /* Windowing */
    if (self->window == WINDOW_HAMMING)
      kernel_hp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
    else
      kernel_hp[i] *=
          (0.42 - 0.5 * cos (2 * M_PI * i / len) +
          0.08 * cos (4 * M_PI * i / len));
  }

  /* normalize for unity gain at DC */
  sum = 0.0;
  for (i = 0; i < len; ++i)
    sum += kernel_hp[i];
  for (i = 0; i < len; ++i)
    kernel_hp[i] /= sum;

  /* do spectral inversion to go from lowpass to highpass */
  for (i = 0; i < len; ++i)
    kernel_hp[i] = -kernel_hp[i];
  kernel_hp[len / 2] += 1;

  /* combine the two kernels */
  kernel = g_new (gdouble, len);

  for (i = 0; i < len; ++i)
    kernel[i] = kernel_lp[i] + kernel_hp[i];

  /* free the helper kernels */
  g_free (kernel_lp);
  g_free (kernel_hp);

  /* do spectral inversion to go from bandreject to bandpass
   * if specified */
  if (self->mode == MODE_BAND_PASS) {
    for (i = 0; i < len; ++i)
      kernel[i] = -kernel[i];
    kernel[len / 2] += 1;
  }

  gst_audio_fx_base_fir_filter_set_kernel (GST_AUDIO_FX_BASE_FIR_FILTER (self),
      kernel, self->kernel_length, (len - 1) / 2);
}
static GstFlowReturn
gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
{
#ifdef GSTREAMER_LITE
  GstSpectrum *spectrum = GST_SPECTRUM (trans);
  GstRingBufferSpec *format;
  GstSpectrumChannel *cd;
  GstSpectrumInputData input_data;
  guint rate, channels, output_channels, c, width, bands, nfft, input_pos, size, frame_size;
  guint fft_todo, msg_todo, block_size;
  gfloat max_value;
  gfloat *input;
  gboolean have_full_interval;
  const guint8 *data;
  GstMessage *m;

  if (!spectrum->post_messages)
      return GST_FLOW_OK;

  format = &GST_AUDIO_FILTER (spectrum)->format;
  rate = format->rate;
  channels = format->channels;
  output_channels = spectrum->multi_channel ? channels : 1;
  width = format->width / 8;
  max_value = (1UL << (format->depth - 1)) - 1;
  bands = spectrum->bands;
  nfft = 2 * bands - 2;
  data = GST_BUFFER_DATA (buffer);
  size = GST_BUFFER_SIZE (buffer);
  frame_size = width * channels;

#else // GSTREAMER_LITE
  GstSpectrum *spectrum = GST_SPECTRUM (trans);
  GstRingBufferSpec *format = &GST_AUDIO_FILTER (spectrum)->format;
  guint rate = format->rate;
  guint channels = format->channels;
  guint output_channels = spectrum->multi_channel ? channels : 1;
  guint c;
  guint width = format->width / 8;
  gfloat max_value = (1UL << (format->depth - 1)) - 1;
  guint bands = spectrum->bands;
  guint nfft = 2 * bands - 2;
  guint input_pos;
  gfloat *input;
  const guint8 *data = GST_BUFFER_DATA (buffer);
  guint size = GST_BUFFER_SIZE (buffer);
  guint frame_size = width * channels;
  guint fft_todo, msg_todo, block_size;
  gboolean have_full_interval;
  GstSpectrumChannel *cd;
  GstSpectrumInputData input_data;
#endif // GSTREAMER_LITE

  GST_LOG_OBJECT (spectrum, "input size: %d bytes", GST_BUFFER_SIZE (buffer));

  if (GST_BUFFER_IS_DISCONT (buffer)) {
    GST_DEBUG_OBJECT (spectrum, "Discontinuity detected -- flushing");
    gst_spectrum_flush (spectrum);
  }

  /* If we don't have a FFT context yet (or it was reset due to parameter
   * changes) get one and allocate memory for everything
   */
  if (spectrum->channel_data == NULL) {
    GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands);

    gst_spectrum_alloc_channel_data (spectrum);

    /* number of sample frames we process before posting a message
     * interval is in ns */
    spectrum->frames_per_interval =
        gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND);
    spectrum->frames_todo = spectrum->frames_per_interval;
    /* rounding error for frames_per_interval in ns,
     * aggregated it in accumulated_error */
    spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
    if (spectrum->frames_per_interval == 0)
      spectrum->frames_per_interval = 1;

    GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
        G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
        GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval,
        GST_TIME_ARGS (spectrum->error_per_interval));

    spectrum->input_pos = 0;

    gst_spectrum_flush (spectrum);
  }

  if (spectrum->num_frames == 0)
    spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);

  input_pos = spectrum->input_pos;
  input_data = spectrum->input_data;

  while (size >= frame_size) {
    /* run input_data for a chunk of data */
    fft_todo = nfft - (spectrum->num_frames % nfft);
    msg_todo = spectrum->frames_todo - spectrum->num_frames;
    GST_LOG_OBJECT (spectrum,
        "message frames todo: %u, fft frames todo: %u, input frames %u",
        msg_todo, fft_todo, (size / frame_size));
    block_size = msg_todo;
    if (block_size > (size / frame_size))
      block_size = (size / frame_size);
    if (block_size > fft_todo)
      block_size = fft_todo;

    for (c = 0; c < output_channels; c++) {
      cd = &spectrum->channel_data[c];
      input = cd->input;
      /* Move the current frames into our ringbuffers */
      input_data (data + c * width, input, block_size, channels, max_value,
          input_pos, nfft);
    }
    data += block_size * frame_size;
    size -= block_size * frame_size;
    input_pos = (input_pos + block_size) % nfft;
    spectrum->num_frames += block_size;

    have_full_interval = (spectrum->num_frames == spectrum->frames_todo);

    GST_LOG_OBJECT (spectrum, "size: %u, do-fft = %d, do-message = %d", size,
        (spectrum->num_frames % nfft == 0), have_full_interval);

    /* If we have enough frames for an FFT or we have all frames required for
     * the interval and we haven't run a FFT, then run an FFT */
    if ((spectrum->num_frames % nfft == 0) ||
        (have_full_interval && !spectrum->num_fft)) {
      for (c = 0; c < output_channels; c++) {
        cd = &spectrum->channel_data[c];
        gst_spectrum_run_fft (spectrum, cd, input_pos);
      }
      spectrum->num_fft++;
    }

    /* Do we have the FFTs for one interval? */
    if (have_full_interval) {
      GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT
          " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft,
          spectrum->num_frames, spectrum->frames_per_interval,
          GST_TIME_ARGS (spectrum->accumulated_error));

      spectrum->frames_todo = spectrum->frames_per_interval;
      if (spectrum->accumulated_error >= GST_SECOND) {
        spectrum->accumulated_error -= GST_SECOND;
        spectrum->frames_todo++;
      }
      spectrum->accumulated_error += spectrum->error_per_interval;

#ifndef GSTREAMER_LITE
      if (spectrum->post_messages) {
        GstMessage *m;
#endif // GSTREAMER_LITE

        for (c = 0; c < output_channels; c++) {
          cd = &spectrum->channel_data[c];
          gst_spectrum_prepare_message_data (spectrum, cd);
        }

        m = gst_spectrum_message_new (spectrum, spectrum->message_ts,
            spectrum->interval);

        gst_element_post_message (GST_ELEMENT (spectrum), m);
#ifndef GSTREAMER_LITE
      }
#endif // GSTREAMER_LITE

      if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts))
        spectrum->message_ts +=
            gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);

#ifdef GSTREAMER_LITE
      for (c = 0; c < spectrum->num_channels; c++) {
#else // GSTREAMER_LITE
      for (c = 0; c < channels; c++) {
#endif // GSTREAMER_LITE
        cd = &spectrum->channel_data[c];
        gst_spectrum_reset_message_data (spectrum, cd);
      }
      spectrum->num_frames = 0;
      spectrum->num_fft = 0;
    }
  }

  spectrum->input_pos = input_pos;

  g_assert (size == 0);

  return GST_FLOW_OK;
}

#ifdef GSTREAMER_LITE
gboolean 
plugin_init_spectrum (GstPlugin * plugin)
#else // GSTREAMER_LITE
static gboolean
plugin_init (GstPlugin * plugin)
#endif // GSTREAMER_LITE
{
  return gst_element_register (plugin, "spectrum", GST_RANK_NONE,
      GST_TYPE_SPECTRUM);
}

#ifndef GSTREAMER_LITE
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "spectrum",
    "Run an FFT on the audio signal, output spectrum data",
    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
static GstMessage *
gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime timestamp,
    GstClockTime duration)
{
  GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (spectrum);
  GstSpectrumChannel *cd;
  GstStructure *s;
  GValue *mcv = NULL, *pcv = NULL;
  GstClockTime endtime, running_time, stream_time;

  GST_DEBUG_OBJECT (spectrum, "preparing message, bands =%d ", spectrum->bands);

  running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
      timestamp);
  stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
      timestamp);
  /* endtime is for backwards compatibility */
  endtime = stream_time + duration;

  s = gst_structure_new ("spectrum",
      "endtime", GST_TYPE_CLOCK_TIME, endtime,
      "timestamp", G_TYPE_UINT64, timestamp,
      "stream-time", G_TYPE_UINT64, stream_time,
      "running-time", G_TYPE_UINT64, running_time,
      "duration", G_TYPE_UINT64, duration, NULL);

  if (!spectrum->multi_channel) {
    cd = &spectrum->channel_data[0];

    if (spectrum->message_magnitude) {
      /* FIXME 0.11: this should be an array, not a list */
      mcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "magnitude");
      gst_spectrum_message_add_list (mcv, cd->spect_magnitude, spectrum->bands);
    }
    if (spectrum->message_phase) {
      /* FIXME 0.11: this should be an array, not a list */
      pcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "phase");
      gst_spectrum_message_add_list (pcv, cd->spect_phase, spectrum->bands);
    }
  } else {
    guint c;
    guint channels = GST_AUDIO_FILTER (spectrum)->format.channels;

    if (spectrum->message_magnitude) {
      mcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "magnitude");
    }
    if (spectrum->message_phase) {
      pcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "phase");
    }

    for (c = 0; c < channels; c++) {
      cd = &spectrum->channel_data[c];

      if (spectrum->message_magnitude) {
        gst_spectrum_message_add_array (mcv, cd->spect_magnitude,
            spectrum->bands);
      }
      if (spectrum->message_phase) {
        gst_spectrum_message_add_array (pcv, cd->spect_magnitude,
            spectrum->bands);
      }
    }
  }
  return gst_message_new_element (GST_OBJECT (spectrum), s);
}