From f5a3e61e69af1c030aec87b6e23660a526640f2d Mon Sep 17 00:00:00 2001 From: Sebastian Dröge Date: Thu, 6 Sep 2007 07:21:22 +0000 Subject: Port GstSpectrum to GstAudioFilter and libgstfft, add support for int32, float and double, use floats for the message... Original commit message from CVS: * configure.ac: * gst/spectrum/Makefile.am: * gst/spectrum/demo-audiotest.c: (draw_spectrum), (message_handler), (main): * gst/spectrum/demo-osssrc.c: (draw_spectrum), (message_handler): * gst/spectrum/gstspectrum.c: (gst_spectrum_base_init), (gst_spectrum_class_init), (gst_spectrum_init), (gst_spectrum_dispose), (gst_spectrum_set_property), (gst_spectrum_get_property), (gst_spectrum_start), (gst_spectrum_setup), (gst_spectrum_message_new), (gst_spectrum_transform_ip): * gst/spectrum/gstspectrum.h: Port GstSpectrum to GstAudioFilter and libgstfft, add support for int32, float and double, use floats for the message contents, average all FFTs done in one interval for better results, use a better windowing function, allow posting the phase in the message and actually do an FFT with the requested number of bands instead of interpolating. * tests/check/elements/spectrum.c: (GST_START_TEST), (spectrum_suite): Improve the units tests by checking for a 11025Hz sine wave and add unit tests for all 4 supported sample types. --- gst/spectrum/Makefile.am | 4 +- gst/spectrum/gstspectrum.c | 470 +++++++++++++++++++++++++++++++++------------ gst/spectrum/gstspectrum.h | 23 ++- 3 files changed, 365 insertions(+), 132 deletions(-) (limited to 'gst/spectrum') diff --git a/gst/spectrum/Makefile.am b/gst/spectrum/Makefile.am index db66dd6c..e161abbf 100644 --- a/gst/spectrum/Makefile.am +++ b/gst/spectrum/Makefile.am @@ -1,10 +1,10 @@ plugin_LTLIBRARIES = libgstspectrum.la -libgstspectrum_la_SOURCES = gstspectrum.c fix_fft.c +libgstspectrum_la_SOURCES = gstspectrum.c libgstspectrum_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \ $(GST_CFLAGS) -libgstspectrum_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) +libgstspectrum_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) -lgstaudio-$(GST_MAJORMINOR) -lgstfft-$(GST_MAJORMINOR) $(LIBM) libgstspectrum_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) noinst_HEADERS = gstspectrum.h diff --git a/gst/spectrum/gstspectrum.c b/gst/spectrum/gstspectrum.c index 75d3e2dd..949b98dc 100644 --- a/gst/spectrum/gstspectrum.c +++ b/gst/spectrum/gstspectrum.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen * <2006> Stefan Kost + * <2007> Sebastian Dröge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -37,18 +38,26 @@ * * * - * #GstValueList of #guchar - * "spectrum": + * #GstValueList of #gfloat + * "magnitude": * the level for each frequency band. A value of 0 maps to the * db value given by the * threshold property.. * * + * + * + * #GstValueList of #gfloat + * "phase": + * the phase for each frequency band. The value is between -pi and pi. + * + * + * * This element cannot be used with the gst-launch command in a sensible way. * The included demo shows how to use it in an application. * - * Last reviewed on 2006-05-21 (0.10.3) + * Last reviewed on 2007-08-18 (0.10.5) * */ @@ -56,9 +65,18 @@ #include "config.h" #endif #include +#include #include +#include +#include #include "gstspectrum.h" +#include +#include +#include +#include +#include + GST_DEBUG_CATEGORY_STATIC (gst_spectrum_debug); #define GST_CAT_DEFAULT gst_spectrum_debug @@ -68,32 +86,34 @@ GST_ELEMENT_DETAILS ("Spectrum analyzer", "Filter/Analyzer/Audio", "Run an FFT on the audio signal, output spectrum data", "Erik Walthinsen , " - "Stefan Kost "); - -static GstStaticPadTemplate sink_template_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [1, MAX], " - "endianness = (int) BYTE_ORDER, " - "width = (int) 16, " "depth = (int) 16, " "signed = (boolean) true") - ); - -static GstStaticPadTemplate src_template_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [1, MAX], " - "endianness = (int) BYTE_ORDER, " - "width = (int) 16, " "depth = (int) 16, " "signed = (boolean) true") - ); + "Stefan Kost , " + "Sebastian Dröge "); + +#define ALLOWED_CAPS \ + "audio/x-raw-int, " \ + " width = (int) 16, " \ + " depth = (int) 16, " \ + " signed = (boolean) true, " \ + " endianness = (int) BYTE_ORDER, " \ + " rate = (int) [ 1, MAX ], " \ + " channels = (int) [ 1, MAX ]; " \ + "audio/x-raw-int, " \ + " width = (int) 32, " \ + " depth = (int) 32, " \ + " signed = (boolean) true, " \ + " endianness = (int) BYTE_ORDER, " \ + " rate = (int) [ 1, MAX ], " \ + " channels = (int) [ 1, MAX ]; " \ + "audio/x-raw-float, " \ + " width = (int) { 32, 64 }, " \ + " endianness = (int) BYTE_ORDER, " \ + " rate = (int) [ 1, MAX ], " \ + " channels = (int) [ 1, MAX ]" /* Spectrum properties */ #define DEFAULT_SIGNAL_SPECTRUM TRUE +#define DEFAULT_SIGNAL_MAGNITUDE TRUE +#define DEFAULT_SIGNAL_PHASE FALSE #define DEFAULT_SIGNAL_INTERVAL (GST_SECOND / 10) #define DEFAULT_BANDS 128 #define DEFAULT_THRESHOLD -60 @@ -105,44 +125,46 @@ enum { PROP_0, PROP_SIGNAL_SPECTRUM, + PROP_SIGNAL_MAGNITUDE, + PROP_SIGNAL_PHASE, PROP_SIGNAL_INTERVAL, PROP_BANDS, PROP_THRESHOLD }; -GST_BOILERPLATE (GstSpectrum, gst_spectrum, GstBaseTransform, - GST_TYPE_BASE_TRANSFORM); +GST_BOILERPLATE (GstSpectrum, gst_spectrum, GstAudioFilter, + GST_TYPE_AUDIO_FILTER); static void gst_spectrum_dispose (GObject * object); static void gst_spectrum_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_spectrum_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static gboolean gst_spectrum_set_caps (GstBaseTransform * trans, GstCaps * in, - GstCaps * out); static gboolean gst_spectrum_start (GstBaseTransform * trans); static gboolean gst_spectrum_stop (GstBaseTransform * trans); static gboolean gst_spectrum_event (GstBaseTransform * trans, GstEvent * event); static GstFlowReturn gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * in); +static gboolean gst_spectrum_setup (GstAudioFilter * base, + GstRingBufferSpec * format); - -#define fixed short -extern int gst_spectrum_fix_fft (fixed fr[], fixed fi[], int m, int inverse); -extern void gst_spectrum_fix_loud (fixed loud[], fixed fr[], fixed fi[], int n, - int scale_shift); -extern void gst_spectrum_window (fixed fr[], int n); +static void process_s16 (GstSpectrum * spectrum, const gint16 * samples); +static void process_s32 (GstSpectrum * spectrum, const gint32 * samples); +static void process_f32 (GstSpectrum * spectrum, const gfloat * samples); +static void process_f64 (GstSpectrum * spectrum, const gdouble * samples); static void gst_spectrum_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstCaps *caps; - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template_factory)); gst_element_class_set_details (element_class, &gst_spectrum_details); + + caps = gst_caps_from_string (ALLOWED_CAPS); + gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (g_class), + caps); + gst_caps_unref (caps); } static void @@ -150,23 +172,35 @@ gst_spectrum_class_init (GstSpectrumClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass); + GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (klass); gobject_class->set_property = gst_spectrum_set_property; gobject_class->get_property = gst_spectrum_get_property; gobject_class->dispose = gst_spectrum_dispose; - trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_spectrum_set_caps); trans_class->start = GST_DEBUG_FUNCPTR (gst_spectrum_start); trans_class->stop = GST_DEBUG_FUNCPTR (gst_spectrum_stop); trans_class->event = GST_DEBUG_FUNCPTR (gst_spectrum_event); trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_spectrum_transform_ip); trans_class->passthrough_on_same_caps = TRUE; + filter_class->setup = GST_DEBUG_FUNCPTR (gst_spectrum_setup); + g_object_class_install_property (gobject_class, PROP_SIGNAL_SPECTRUM, g_param_spec_boolean ("message", "Message", "Post a level message for each passed interval", DEFAULT_SIGNAL_SPECTRUM, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_SIGNAL_MAGNITUDE, + g_param_spec_boolean ("message-magnitude", "Magnitude", + "Post the magnitude of the spectrum", + DEFAULT_SIGNAL_MAGNITUDE, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_SIGNAL_PHASE, + g_param_spec_boolean ("message-phase", "Phase", + "Post the phase of the spectrum", + DEFAULT_SIGNAL_PHASE, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_SIGNAL_INTERVAL, g_param_spec_uint64 ("interval", "Interval", "Interval of time between message posts (in nanoseconds)", @@ -191,14 +225,14 @@ gst_spectrum_init (GstSpectrum * spectrum, GstSpectrumClass * g_class) spectrum->adapter = gst_adapter_new (); spectrum->message = DEFAULT_SIGNAL_SPECTRUM; + spectrum->message_magnitude = DEFAULT_SIGNAL_MAGNITUDE; + spectrum->message_phase = DEFAULT_SIGNAL_PHASE; spectrum->interval = DEFAULT_SIGNAL_INTERVAL; spectrum->bands = DEFAULT_BANDS; spectrum->threshold = DEFAULT_THRESHOLD; - spectrum->loud = g_malloc (SPECTRUM_WINDOW_LEN * sizeof (gint16)); - spectrum->im = g_malloc0 (SPECTRUM_WINDOW_LEN * sizeof (gint16)); - spectrum->re = g_malloc0 (SPECTRUM_WINDOW_LEN * sizeof (gint16)); - spectrum->spect = g_malloc (spectrum->bands * sizeof (guchar)); + spectrum->spect_magnitude = g_new0 (gfloat, spectrum->bands); + spectrum->spect_phase = g_new0 (gfloat, spectrum->bands); } static void @@ -211,15 +245,20 @@ gst_spectrum_dispose (GObject * object) spectrum->adapter = NULL; } - g_free (spectrum->re); - g_free (spectrum->im); - g_free (spectrum->loud); - g_free (spectrum->spect); + g_free (spectrum->in); + if (spectrum->fft_free_func) { + spectrum->fft_free_func (spectrum->fft_ctx); + spectrum->fft_ctx = NULL; + spectrum->fft_free_func = NULL; + } + g_free (spectrum->freqdata); + g_free (spectrum->spect_magnitude); + g_free (spectrum->spect_phase); - spectrum->re = NULL; - spectrum->im = NULL; - spectrum->loud = NULL; - spectrum->spect = NULL; + spectrum->in = NULL; + spectrum->spect_magnitude = NULL; + spectrum->spect_phase = NULL; + spectrum->freqdata = NULL; G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -234,17 +273,39 @@ gst_spectrum_set_property (GObject * object, guint prop_id, case PROP_SIGNAL_SPECTRUM: filter->message = g_value_get_boolean (value); break; + case PROP_SIGNAL_MAGNITUDE: + filter->message_magnitude = g_value_get_boolean (value); + break; + case PROP_SIGNAL_PHASE: + filter->message_phase = g_value_get_boolean (value); + break; case PROP_SIGNAL_INTERVAL: filter->interval = g_value_get_uint64 (value); break; case PROP_BANDS: GST_BASE_TRANSFORM_LOCK (filter); + filter->bands = g_value_get_uint (value); - g_free (filter->spect); - filter->spect = g_malloc (filter->bands * sizeof (guchar)); + g_free (filter->spect_magnitude); + g_free (filter->spect_phase); + g_free (filter->in); + g_free (filter->freqdata); + + if (filter->fft_free_func) { + filter->fft_free_func (filter->fft_ctx); + filter->fft_ctx = NULL; + filter->fft_free_func = NULL; + } + + filter->in = NULL; + filter->freqdata = NULL; + filter->spect_magnitude = g_new0 (gfloat, filter->bands); + filter->spect_phase = g_new0 (gfloat, filter->bands); + filter->num_frames = 0; + filter->num_fft = 0; GST_BASE_TRANSFORM_UNLOCK (filter); GST_DEBUG_OBJECT (filter, "reallocation, spect = %p, bands =%d ", - filter->spect, filter->bands); + filter->spect_magnitude, filter->bands); break; case PROP_THRESHOLD: filter->threshold = g_value_get_int (value); @@ -265,6 +326,12 @@ gst_spectrum_get_property (GObject * object, guint prop_id, case PROP_SIGNAL_SPECTRUM: g_value_set_boolean (value, filter->message); break; + case PROP_SIGNAL_MAGNITUDE: + g_value_set_boolean (value, filter->message_magnitude); + break; + case PROP_SIGNAL_PHASE: + g_value_set_boolean (value, filter->message_phase); + break; case PROP_SIGNAL_INTERVAL: g_value_set_uint64 (value, filter->interval); break; @@ -280,25 +347,17 @@ gst_spectrum_get_property (GObject * object, guint prop_id, } } -static gboolean -gst_spectrum_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out) -{ - GstSpectrum *filter = GST_SPECTRUM (trans); - GstStructure *structure; - - structure = gst_caps_get_structure (in, 0); - gst_structure_get_int (structure, "rate", &filter->rate); - gst_structure_get_int (structure, "channels", &filter->channels); - - return TRUE; -} - static gboolean gst_spectrum_start (GstBaseTransform * trans) { GstSpectrum *filter = GST_SPECTRUM (trans); filter->num_frames = 0; + filter->num_fft = 0; + if (filter->spect_magnitude) + memset (filter->spect_magnitude, filter->bands * sizeof (gfloat), 0); + if (filter->spect_phase) + memset (filter->spect_phase, filter->bands * sizeof (gfloat), 0); gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED); return TRUE; @@ -347,6 +406,41 @@ gst_spectrum_event (GstBaseTransform * trans, GstEvent * event) return TRUE; } +static gboolean +gst_spectrum_setup (GstAudioFilter * base, GstRingBufferSpec * format) +{ + GstSpectrum *filter = GST_SPECTRUM (base); + + if (filter->in) { + g_free (filter->in); + filter->in = NULL; + } + + if (filter->fft_free_func) { + filter->fft_free_func (filter->fft_ctx); + filter->fft_ctx = NULL; + filter->fft_free_func = NULL; + } + + if (filter->freqdata) { + g_free (filter->freqdata); + filter->freqdata = NULL; + } + + if (format->type == GST_BUFTYPE_LINEAR && format->width == 32) + filter->process = (GstSpectrumProcessFunc) process_s32; + else if (format->type == GST_BUFTYPE_LINEAR && format->width == 16) + filter->process = (GstSpectrumProcessFunc) process_s16; + else if (format->type == GST_BUFTYPE_FLOAT && format->width == 64) + filter->process = (GstSpectrumProcessFunc) process_f64; + else if (format->type == GST_BUFTYPE_FLOAT && format->width == 32) + filter->process = (GstSpectrumProcessFunc) process_f32; + else + g_assert_not_reached (); + + return TRUE; +} + static GstMessage * gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime endtime) { @@ -354,99 +448,231 @@ gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime endtime) GValue v = { 0, }; GValue *l; guint i; - guchar *spect = spectrum->spect; + gfloat *spect_magnitude = spectrum->spect_magnitude; + gfloat *spect_phase = spectrum->spect_phase; GST_DEBUG_OBJECT (spectrum, "preparing message, spect = %p, bands =%d ", - spect, spectrum->bands); + spect_magnitude, spectrum->bands); s = gst_structure_new ("spectrum", "endtime", GST_TYPE_CLOCK_TIME, endtime, NULL); - g_value_init (&v, GST_TYPE_LIST); - /* will copy-by-value */ - gst_structure_set_value (s, "spectrum", &v); - g_value_unset (&v); + if (spectrum->message_magnitude) { + g_value_init (&v, GST_TYPE_LIST); + /* will copy-by-value */ + gst_structure_set_value (s, "magnitude", &v); + g_value_unset (&v); + + g_value_init (&v, G_TYPE_FLOAT); + l = (GValue *) gst_structure_get_value (s, "magnitude"); + for (i = 0; i < spectrum->bands; i++) { + g_value_set_float (&v, spect_magnitude[i]); + gst_value_list_append_value (l, &v); /* copies by value */ + } + g_value_unset (&v); + } - g_value_init (&v, G_TYPE_UCHAR); - l = (GValue *) gst_structure_get_value (s, "spectrum"); - for (i = 0; i < spectrum->bands; i++) { - g_value_set_uchar (&v, spect[i]); - gst_value_list_append_value (l, &v); /* copies by value */ + if (spectrum->message_phase) { + g_value_init (&v, GST_TYPE_LIST); + /* will copy-by-value */ + gst_structure_set_value (s, "phase", &v); + g_value_unset (&v); + + g_value_init (&v, G_TYPE_FLOAT); + l = (GValue *) gst_structure_get_value (s, "phase"); + for (i = 0; i < spectrum->bands; i++) { + g_value_set_float (&v, spect_phase[i]); + gst_value_list_append_value (l, &v); /* copies by value */ + } + g_value_unset (&v); } - g_value_unset (&v); return gst_message_new_element (GST_OBJECT (spectrum), s); } +#define DEFINE_PROCESS_FUNC_INT(width,next_width,max) \ +static void \ +process_s##width (GstSpectrum *spectrum, const gint##width *samples) \ +{ \ + gfloat *spect_magnitude = spectrum->spect_magnitude; \ + gfloat *spect_phase = spectrum->spect_phase; \ + gint channels = GST_AUDIO_FILTER (spectrum)->format.channels; \ + gint i, j, k; \ + gint##next_width acc; \ + GstFFTS##width##Complex *freqdata; \ + GstFFTS##width *ctx; \ + gint##width *in; \ + gint nfft = 2 * spectrum->bands - 2; \ + \ + if (!spectrum->in) \ + spectrum->in = (guint8 *) g_new (gint##width, nfft); \ + \ + in = (gint##width *) spectrum->in; \ + \ + for (i = 0, j = 0; i < nfft; i++) { \ + /* convert to mono */ \ + for (k = 0, acc = 0; k < channels; k++) \ + acc += samples[j++]; \ + in[i] = (gint##width) (acc / channels); \ + } \ + \ + if (!spectrum->fft_ctx) { \ + spectrum->fft_ctx = gst_fft_s##width##_new (nfft, FALSE); \ + spectrum->fft_free_func = (GstSpectrumFFTFreeFunc) gst_fft_s##width##_free; \ + } \ + ctx = spectrum->fft_ctx; \ + \ + gst_fft_s##width##_window (ctx, in, GST_FFT_WINDOW_HAMMING); \ + \ + if (!spectrum->freqdata) \ + spectrum->freqdata = g_new (GstFFTS##width##Complex, spectrum->bands); \ + \ + freqdata = (GstFFTS##width##Complex *) spectrum->freqdata; \ + \ + gst_fft_s##width##_fft (ctx, in, freqdata); \ + spectrum->num_fft++; \ + \ + /* Calculate magnitude in db */ \ + for (i = 0; i < spectrum->bands; i++) { \ + gdouble val = (gdouble) freqdata[i].r * (gdouble) freqdata[i].r \ + + (gdouble) freqdata[i].i * (gdouble) freqdata[i].i; \ + val = sqrt (val); \ + val = 20.0 * log10 (val / max); \ + if (val > spectrum->threshold) \ + val -= spectrum->threshold; \ + else \ + val = 0.0; \ + spect_magnitude[i] += val; \ + } \ + \ + /* Calculate phase */ \ + for (i = 0; i < spectrum->bands; i++) \ + spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r); \ + \ +} + +DEFINE_PROCESS_FUNC_INT (16, 32, 32767.0); +DEFINE_PROCESS_FUNC_INT (32, 64, 2147483647.0); + +#define DEFINE_PROCESS_FUNC_FLOAT(width,type) \ +static void \ +process_f##width (GstSpectrum *spectrum, const g##type *samples) \ +{ \ + gfloat *spect_magnitude = spectrum->spect_magnitude; \ + gfloat *spect_phase = spectrum->spect_phase; \ + gint channels = GST_AUDIO_FILTER (spectrum)->format.channels; \ + gint i, j, k; \ + g##type acc; \ + GstFFTF##width##Complex *freqdata; \ + GstFFTF##width *ctx; \ + g##type *in; \ + gint nfft = 2 * spectrum->bands - 2; \ + \ + if (!spectrum->in) \ + spectrum->in = (guint8 *) g_new (g##type, nfft); \ + \ + in = (g##type *) spectrum->in; \ + \ + for (i = 0, j = 0; i < nfft; i++) { \ + /* convert to mono */ \ + for (k = 0, acc = 0; k < channels; k++) \ + acc += samples[j++]; \ + in[i] = (g##type) (acc / channels); \ + if (abs (in[i]) > 1.0) \ + g_assert_not_reached(); \ + } \ + \ + if (!spectrum->fft_ctx) { \ + spectrum->fft_ctx = gst_fft_f##width##_new (nfft, FALSE); \ + spectrum->fft_free_func = (GstSpectrumFFTFreeFunc) gst_fft_f##width##_free; \ + } \ + ctx = spectrum->fft_ctx; \ + \ + gst_fft_f##width##_window (ctx, in, GST_FFT_WINDOW_HAMMING); \ + \ + if (!spectrum->freqdata) \ + spectrum->freqdata = g_new (GstFFTF##width##Complex, spectrum->bands); \ + \ + freqdata = (GstFFTF##width##Complex *) spectrum->freqdata; \ + \ + gst_fft_f##width##_fft (ctx, in, freqdata); \ + spectrum->num_fft++; \ + \ + /* Calculate magnitude in db */ \ + for (i = 0; i < spectrum->bands; i++) { \ + gdouble val = freqdata[i].r * freqdata[i].r + freqdata[i].i * freqdata[i].i; \ + val = sqrt (val); \ + val = 20.0 * log10 (val / nfft); \ + if (val > spectrum->threshold) \ + val -= spectrum->threshold; \ + else \ + val = 0.0; \ + spect_magnitude[i] += val; \ + } \ + \ + /* Calculate phase */ \ + for (i = 0; i < spectrum->bands; i++) \ + spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r); \ + \ +} + +DEFINE_PROCESS_FUNC_FLOAT (32, float); +DEFINE_PROCESS_FUNC_FLOAT (64, double); + static GstFlowReturn gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * in) { GstSpectrum *spectrum = GST_SPECTRUM (trans); gint wanted; - gint i, j, k; - gint32 acc; - gfloat pos, step; - guchar *spect = spectrum->spect; + gint i; + gfloat *spect_magnitude = spectrum->spect_magnitude; + gfloat *spect_phase = spectrum->spect_phase; + gint rate = GST_AUDIO_FILTER (spectrum)->format.rate; + gint channels = GST_AUDIO_FILTER (spectrum)->format.channels; + gint width = GST_AUDIO_FILTER (spectrum)->format.width / 8; + gint nfft = 2 * spectrum->bands - 2; GstClockTime endtime = gst_segment_to_running_time (&spectrum->segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (in)); - GstClockTime blktime = - GST_FRAMES_TO_CLOCK_TIME (SPECTRUM_WINDOW_LEN, spectrum->rate); + GstClockTime blktime = GST_FRAMES_TO_CLOCK_TIME (nfft, rate); GST_LOG_OBJECT (spectrum, "input size: %d bytes", GST_BUFFER_SIZE (in)); /* can we do this nicer? */ gst_adapter_push (spectrum->adapter, gst_buffer_copy (in)); /* required number of bytes */ - wanted = spectrum->channels * SPECTRUM_WINDOW_LEN * sizeof (gint16); - /* FIXME: 4.0 was 2.0 before, but that include the mirrored spectrum */ - step = (gfloat) SPECTRUM_WINDOW_LEN / (spectrum->bands * 4.0); + wanted = channels * nfft * width; while (gst_adapter_available (spectrum->adapter) >= wanted) { - const gint16 *samples; - - samples = (const gint16 *) gst_adapter_peek (spectrum->adapter, wanted); + const guint8 *samples; - /* the current fft code is gint16 based, so supporting other formats would - * not really benefit now */ - for (i = 0, j = 0; i < SPECTRUM_WINDOW_LEN; i++) { - /* convert to mono */ - for (k = 0, acc = 0; k < spectrum->channels; k++) - acc += samples[j++]; - spectrum->re[i] = (gint16) (acc / spectrum->channels); - } + samples = gst_adapter_peek (spectrum->adapter, wanted); - gst_spectrum_window (spectrum->re, SPECTRUM_WINDOW_LEN); - gst_spectrum_fix_fft (spectrum->re, spectrum->im, SPECTRUM_WINDOW_BASE, - FALSE); - gst_spectrum_fix_loud (spectrum->loud, spectrum->re, spectrum->im, - SPECTRUM_WINDOW_LEN, 0); - - /* resample to requested number of bands and offset by threshold */ - for (i = 0, pos = 0.0; i < spectrum->bands; i++, pos += step) { - if (spectrum->loud[(gint) pos] > spectrum->threshold) { - spect[i] = spectrum->loud[(gint) pos] - spectrum->threshold; - /* - if (spect[i] > 15); - spect[i] = 15; - */ - } else - /* treat as silence */ - spect[i] = 0; - } + spectrum->process (spectrum, samples); - spectrum->num_frames += SPECTRUM_WINDOW_LEN; + spectrum->num_frames += nfft; endtime += blktime; /* do we need to message ? */ if (spectrum->num_frames >= - GST_CLOCK_TIME_TO_FRAMES (spectrum->interval, spectrum->rate)) { + GST_CLOCK_TIME_TO_FRAMES (spectrum->interval, rate)) { if (spectrum->message) { - GstMessage *m = gst_spectrum_message_new (spectrum, endtime); + GstMessage *m; + + /* Calculate average */ + for (i = 0; i < spectrum->bands; i++) { + spect_magnitude[i] /= spectrum->num_fft; + spect_phase[i] /= spectrum->num_fft; + } + + m = gst_spectrum_message_new (spectrum, endtime); gst_element_post_message (GST_ELEMENT (spectrum), m); } + memset (spect_magnitude, spectrum->bands * sizeof (gfloat), 0); + memset (spect_phase, spectrum->bands * sizeof (gfloat), 0); spectrum->num_frames = 0; + spectrum->num_fft = 0; } gst_adapter_flush (spectrum->adapter, wanted); diff --git a/gst/spectrum/gstspectrum.h b/gst/spectrum/gstspectrum.h index 70ab25c9..a32d4ccf 100644 --- a/gst/spectrum/gstspectrum.h +++ b/gst/spectrum/gstspectrum.h @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -39,9 +40,11 @@ extern "C" { typedef struct _GstSpectrum GstSpectrum; typedef struct _GstSpectrumClass GstSpectrumClass; +typedef void (*GstSpectrumProcessFunc) (GstSpectrum *, const guint8 *); +typedef void (*GstSpectrumFFTFreeFunc) (void *); struct _GstSpectrum { - GstBaseTransform element; + GstAudioFilter element; GstPad *sinkpad,*srcpad; GstAdapter *adapter; @@ -49,24 +52,28 @@ struct _GstSpectrum { /* properties */ gboolean message; /* whether or not to post messages */ + gboolean message_magnitude; + gboolean message_phase; guint64 interval; /* how many seconds between emits */ guint bands; /* number of spectrum bands */ gint threshold; /* energy level treshold */ gint num_frames; /* frame count (1 sample per channel) * since last emit */ + gint num_fft; /* number of FFTs since last emit */ - gint rate; /* caps variables */ - gint channels; - /* */ - gint base, len; - gint16 *re, *im, *loud; - guchar *spect; + gfloat *spect_magnitude; + gfloat *spect_phase; + GstSpectrumProcessFunc process; + void *fft_ctx; + GstSpectrumFFTFreeFunc fft_free_func; + void *in; + void *freqdata; }; struct _GstSpectrumClass { - GstBaseTransformClass parent_class; + GstAudioFilterClass parent_class; }; GType gst_spectrum_get_type (void); -- cgit