d9899b497456c0599f61f85a3b110d06a6cc7d4f
[platform/upstream/gstreamer.git] / gst / spectrum / gstspectrum.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *               <2006> Stefan Kost <ensonic@users.sf.net>
4  *               <2007> Sebastian Dröge <slomo@circular-chaos.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /**
22  * SECTION:element-spectrum
23  *
24  * <refsect2>
25  * The Spectrum element analyzes the frequency spectrum of an audio signal.
26  * If <link linkend="GstSpectrum--message">message property</link> is #TRUE it
27  * sends analysis results as application message named
28  * <classname>&quot;spectrum&quot;</classname> after each interval of time given
29  * by the <link linkend="GstSpectrum--interval">interval property</link>.
30  * The message's structure contains two fields:
31  * <itemizedlist>
32  * <listitem>
33  *   <para>
34  *   #GstClockTime
35  *   <classname>&quot;endtime&quot;</classname>:
36  *   the end time of the buffer that triggered the message
37  *   </para>
38  * </listitem>
39  * <listitem>
40  *   <para>
41  *   #GstValueList of #gfloat
42  *   <classname>&quot;magnitude&quot;</classname>:
43  *   the level for each frequency band. A value of 0 maps to the
44  *   db value given by the
45  *   <link linkend="GstSpectrum--threshold">threshold property.</link>.
46  *   </para>
47  * </listitem>
48  * <listitem>
49  *   <para>
50  *   #GstValueList of #gfloat
51  *   <classname>&quot;phase&quot;</classname>:
52  *   the phase for each frequency band. The value is between -pi and pi.
53  *   </para>
54  * </listitem>
55
56  *
57  * This element cannot be used with the gst-launch command in a sensible way.
58  * The included demo shows how to use it in an application.
59  *
60  * Last reviewed on 2007-08-18 (0.10.5)
61  * </refsect2>
62  */
63
64 #ifdef HAVE_CONFIG_H
65 #include "config.h"
66 #endif
67 #include <string.h>
68 #include <stdlib.h>
69 #include <gst/audio/audio.h>
70 #include <gst/audio/gstaudiofilter.h>
71 #include <math.h>
72 #include "gstspectrum.h"
73
74 #include <gst/fft/gstfft.h>
75 #include <gst/fft/gstffts16.h>
76 #include <gst/fft/gstffts32.h>
77 #include <gst/fft/gstfftf32.h>
78 #include <gst/fft/gstfftf64.h>
79
80 GST_DEBUG_CATEGORY_STATIC (gst_spectrum_debug);
81 #define GST_CAT_DEFAULT gst_spectrum_debug
82
83 /* elementfactory information */
84 static const GstElementDetails gst_spectrum_details =
85 GST_ELEMENT_DETAILS ("Spectrum analyzer",
86     "Filter/Analyzer/Audio",
87     "Run an FFT on the audio signal, output spectrum data",
88     "Erik Walthinsen <omega@cse.ogi.edu>, "
89     "Stefan Kost <ensonic@users.sf.net>, "
90     "Sebastian Dröge <slomo@circular-chaos.org>");
91
92 #define ALLOWED_CAPS \
93     "audio/x-raw-int, "                                               \
94     " width = (int) 16, "                                             \
95     " depth = (int) 16, "                                             \
96     " signed = (boolean) true, "                                      \
97     " endianness = (int) BYTE_ORDER, "                                \
98     " rate = (int) [ 1, MAX ], "                                      \
99     " channels = (int) [ 1, MAX ]; "                                  \
100     "audio/x-raw-int, "                                               \
101     " width = (int) 32, "                                             \
102     " depth = (int) 32, "                                             \
103     " signed = (boolean) true, "                                      \
104     " endianness = (int) BYTE_ORDER, "                                \
105     " rate = (int) [ 1, MAX ], "                                      \
106     " channels = (int) [ 1, MAX ]; "                                  \
107     "audio/x-raw-float, "                                             \
108     " width = (int) { 32, 64 }, "                                     \
109     " endianness = (int) BYTE_ORDER, "                                \
110     " rate = (int) [ 1, MAX ], "                                      \
111     " channels = (int) [ 1, MAX ]"
112
113 /* Spectrum properties */
114 #define DEFAULT_SIGNAL_SPECTRUM         TRUE
115 #define DEFAULT_SIGNAL_MAGNITUDE        TRUE
116 #define DEFAULT_SIGNAL_PHASE            FALSE
117 #define DEFAULT_SIGNAL_INTERVAL         (GST_SECOND / 10)
118 #define DEFAULT_BANDS                   128
119 #define DEFAULT_THRESHOLD               -60
120
121 #define SPECTRUM_WINDOW_BASE 9
122 #define SPECTRUM_WINDOW_LEN (1 << (SPECTRUM_WINDOW_BASE+1))
123
124 enum
125 {
126   PROP_0,
127   PROP_SIGNAL_SPECTRUM,
128   PROP_SIGNAL_MAGNITUDE,
129   PROP_SIGNAL_PHASE,
130   PROP_SIGNAL_INTERVAL,
131   PROP_BANDS,
132   PROP_THRESHOLD
133 };
134
135 GST_BOILERPLATE (GstSpectrum, gst_spectrum, GstAudioFilter,
136     GST_TYPE_AUDIO_FILTER);
137
138 static void gst_spectrum_dispose (GObject * object);
139 static void gst_spectrum_set_property (GObject * object, guint prop_id,
140     const GValue * value, GParamSpec * pspec);
141 static void gst_spectrum_get_property (GObject * object, guint prop_id,
142     GValue * value, GParamSpec * pspec);
143 static gboolean gst_spectrum_start (GstBaseTransform * trans);
144 static gboolean gst_spectrum_stop (GstBaseTransform * trans);
145 static gboolean gst_spectrum_event (GstBaseTransform * trans, GstEvent * event);
146 static GstFlowReturn gst_spectrum_transform_ip (GstBaseTransform * trans,
147     GstBuffer * in);
148 static gboolean gst_spectrum_setup (GstAudioFilter * base,
149     GstRingBufferSpec * format);
150
151 static void process_s16 (GstSpectrum * spectrum, const gint16 * samples);
152 static void process_s32 (GstSpectrum * spectrum, const gint32 * samples);
153 static void process_f32 (GstSpectrum * spectrum, const gfloat * samples);
154 static void process_f64 (GstSpectrum * spectrum, const gdouble * samples);
155
156 static void
157 gst_spectrum_base_init (gpointer g_class)
158 {
159   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
160   GstCaps *caps;
161
162   gst_element_class_set_details (element_class, &gst_spectrum_details);
163
164   caps = gst_caps_from_string (ALLOWED_CAPS);
165   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (g_class),
166       caps);
167   gst_caps_unref (caps);
168 }
169
170 static void
171 gst_spectrum_class_init (GstSpectrumClass * klass)
172 {
173   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
174   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
175   GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (klass);
176
177   gobject_class->set_property = gst_spectrum_set_property;
178   gobject_class->get_property = gst_spectrum_get_property;
179   gobject_class->dispose = gst_spectrum_dispose;
180
181   trans_class->start = GST_DEBUG_FUNCPTR (gst_spectrum_start);
182   trans_class->stop = GST_DEBUG_FUNCPTR (gst_spectrum_stop);
183   trans_class->event = GST_DEBUG_FUNCPTR (gst_spectrum_event);
184   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_spectrum_transform_ip);
185   trans_class->passthrough_on_same_caps = TRUE;
186
187   filter_class->setup = GST_DEBUG_FUNCPTR (gst_spectrum_setup);
188
189   g_object_class_install_property (gobject_class, PROP_SIGNAL_SPECTRUM,
190       g_param_spec_boolean ("message", "Message",
191           "Post a level message for each passed interval",
192           DEFAULT_SIGNAL_SPECTRUM, G_PARAM_READWRITE));
193
194   g_object_class_install_property (gobject_class, PROP_SIGNAL_MAGNITUDE,
195       g_param_spec_boolean ("message-magnitude", "Magnitude",
196           "Post the magnitude of the spectrum",
197           DEFAULT_SIGNAL_MAGNITUDE, G_PARAM_READWRITE));
198
199   g_object_class_install_property (gobject_class, PROP_SIGNAL_PHASE,
200       g_param_spec_boolean ("message-phase", "Phase",
201           "Post the phase of the spectrum",
202           DEFAULT_SIGNAL_PHASE, G_PARAM_READWRITE));
203
204   g_object_class_install_property (gobject_class, PROP_SIGNAL_INTERVAL,
205       g_param_spec_uint64 ("interval", "Interval",
206           "Interval of time between message posts (in nanoseconds)",
207           1, G_MAXUINT64, DEFAULT_SIGNAL_INTERVAL, G_PARAM_READWRITE));
208
209   g_object_class_install_property (gobject_class, PROP_BANDS,
210       g_param_spec_uint ("bands", "Bands", "number of frequency bands",
211           0, G_MAXUINT, DEFAULT_BANDS, G_PARAM_READWRITE));
212
213   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
214       g_param_spec_int ("threshold", "Threshold",
215           "db threshold for result, maps to 0", G_MININT, 0, DEFAULT_THRESHOLD,
216           G_PARAM_READWRITE));
217
218   GST_DEBUG_CATEGORY_INIT (gst_spectrum_debug, "spectrum", 0,
219       "audio spectrum analyser element");
220 }
221
222 static void
223 gst_spectrum_init (GstSpectrum * spectrum, GstSpectrumClass * g_class)
224 {
225   spectrum->adapter = gst_adapter_new ();
226
227   spectrum->message = DEFAULT_SIGNAL_SPECTRUM;
228   spectrum->message_magnitude = DEFAULT_SIGNAL_MAGNITUDE;
229   spectrum->message_phase = DEFAULT_SIGNAL_PHASE;
230   spectrum->interval = DEFAULT_SIGNAL_INTERVAL;
231   spectrum->bands = DEFAULT_BANDS;
232   spectrum->threshold = DEFAULT_THRESHOLD;
233
234   spectrum->spect_magnitude = g_new0 (gfloat, spectrum->bands);
235   spectrum->spect_phase = g_new0 (gfloat, spectrum->bands);
236 }
237
238 static void
239 gst_spectrum_dispose (GObject * object)
240 {
241   GstSpectrum *spectrum = GST_SPECTRUM (object);
242
243   if (spectrum->adapter) {
244     g_object_unref (spectrum->adapter);
245     spectrum->adapter = NULL;
246   }
247
248   g_free (spectrum->in);
249   if (spectrum->fft_free_func) {
250     spectrum->fft_free_func (spectrum->fft_ctx);
251     spectrum->fft_ctx = NULL;
252     spectrum->fft_free_func = NULL;
253   }
254   g_free (spectrum->freqdata);
255   g_free (spectrum->spect_magnitude);
256   g_free (spectrum->spect_phase);
257
258   spectrum->in = NULL;
259   spectrum->spect_magnitude = NULL;
260   spectrum->spect_phase = NULL;
261   spectrum->freqdata = NULL;
262
263   G_OBJECT_CLASS (parent_class)->dispose (object);
264 }
265
266 static void
267 gst_spectrum_set_property (GObject * object, guint prop_id,
268     const GValue * value, GParamSpec * pspec)
269 {
270   GstSpectrum *filter = GST_SPECTRUM (object);
271
272   switch (prop_id) {
273     case PROP_SIGNAL_SPECTRUM:
274       filter->message = g_value_get_boolean (value);
275       break;
276     case PROP_SIGNAL_MAGNITUDE:
277       filter->message_magnitude = g_value_get_boolean (value);
278       break;
279     case PROP_SIGNAL_PHASE:
280       filter->message_phase = g_value_get_boolean (value);
281       break;
282     case PROP_SIGNAL_INTERVAL:
283       filter->interval = g_value_get_uint64 (value);
284       break;
285     case PROP_BANDS:
286       GST_BASE_TRANSFORM_LOCK (filter);
287
288       filter->bands = g_value_get_uint (value);
289       g_free (filter->spect_magnitude);
290       g_free (filter->spect_phase);
291       g_free (filter->in);
292       g_free (filter->freqdata);
293
294       if (filter->fft_free_func) {
295         filter->fft_free_func (filter->fft_ctx);
296         filter->fft_ctx = NULL;
297         filter->fft_free_func = NULL;
298       }
299
300       filter->in = NULL;
301       filter->freqdata = NULL;
302       filter->spect_magnitude = g_new0 (gfloat, filter->bands);
303       filter->spect_phase = g_new0 (gfloat, filter->bands);
304       filter->num_frames = 0;
305       filter->num_fft = 0;
306       GST_BASE_TRANSFORM_UNLOCK (filter);
307       GST_DEBUG_OBJECT (filter, "reallocation, spect = %p, bands =%d ",
308           filter->spect_magnitude, filter->bands);
309       break;
310     case PROP_THRESHOLD:
311       filter->threshold = g_value_get_int (value);
312       break;
313     default:
314       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
315       break;
316   }
317 }
318
319 static void
320 gst_spectrum_get_property (GObject * object, guint prop_id,
321     GValue * value, GParamSpec * pspec)
322 {
323   GstSpectrum *filter = GST_SPECTRUM (object);
324
325   switch (prop_id) {
326     case PROP_SIGNAL_SPECTRUM:
327       g_value_set_boolean (value, filter->message);
328       break;
329     case PROP_SIGNAL_MAGNITUDE:
330       g_value_set_boolean (value, filter->message_magnitude);
331       break;
332     case PROP_SIGNAL_PHASE:
333       g_value_set_boolean (value, filter->message_phase);
334       break;
335     case PROP_SIGNAL_INTERVAL:
336       g_value_set_uint64 (value, filter->interval);
337       break;
338     case PROP_BANDS:
339       g_value_set_uint (value, filter->bands);
340       break;
341     case PROP_THRESHOLD:
342       g_value_set_int (value, filter->threshold);
343       break;
344     default:
345       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346       break;
347   }
348 }
349
350 static gboolean
351 gst_spectrum_start (GstBaseTransform * trans)
352 {
353   GstSpectrum *filter = GST_SPECTRUM (trans);
354
355   filter->num_frames = 0;
356   filter->num_fft = 0;
357   if (filter->spect_magnitude)
358     memset (filter->spect_magnitude, 0, filter->bands * sizeof (gfloat));
359   if (filter->spect_phase)
360     memset (filter->spect_phase, 0, filter->bands * sizeof (gfloat));
361
362   return TRUE;
363 }
364
365 static gboolean
366 gst_spectrum_stop (GstBaseTransform * trans)
367 {
368   GstSpectrum *filter = GST_SPECTRUM (trans);
369
370   gst_adapter_clear (filter->adapter);
371
372   return TRUE;
373 }
374
375 static gboolean
376 gst_spectrum_event (GstBaseTransform * trans, GstEvent * event)
377 {
378   GstSpectrum *filter = GST_SPECTRUM (trans);
379
380   switch (GST_EVENT_TYPE (event)) {
381     case GST_EVENT_FLUSH_STOP:
382     case GST_EVENT_EOS:
383       gst_adapter_clear (filter->adapter);
384       break;
385     default:
386       break;
387   }
388
389   return TRUE;
390 }
391
392 static gboolean
393 gst_spectrum_setup (GstAudioFilter * base, GstRingBufferSpec * format)
394 {
395   GstSpectrum *filter = GST_SPECTRUM (base);
396
397   if (filter->in) {
398     g_free (filter->in);
399     filter->in = NULL;
400   }
401
402   if (filter->fft_free_func) {
403     filter->fft_free_func (filter->fft_ctx);
404     filter->fft_ctx = NULL;
405     filter->fft_free_func = NULL;
406   }
407
408   if (filter->freqdata) {
409     g_free (filter->freqdata);
410     filter->freqdata = NULL;
411   }
412
413   if (format->type == GST_BUFTYPE_LINEAR && format->width == 32)
414     filter->process = (GstSpectrumProcessFunc) process_s32;
415   else if (format->type == GST_BUFTYPE_LINEAR && format->width == 16)
416     filter->process = (GstSpectrumProcessFunc) process_s16;
417   else if (format->type == GST_BUFTYPE_FLOAT && format->width == 64)
418     filter->process = (GstSpectrumProcessFunc) process_f64;
419   else if (format->type == GST_BUFTYPE_FLOAT && format->width == 32)
420     filter->process = (GstSpectrumProcessFunc) process_f32;
421   else
422     g_assert_not_reached ();
423
424   return TRUE;
425 }
426
427 static GstMessage *
428 gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime endtime)
429 {
430   GstStructure *s;
431   GValue v = { 0, };
432   GValue *l;
433   guint i;
434   gfloat *spect_magnitude = spectrum->spect_magnitude;
435   gfloat *spect_phase = spectrum->spect_phase;
436
437   GST_DEBUG_OBJECT (spectrum, "preparing message, spect = %p, bands =%d ",
438       spect_magnitude, spectrum->bands);
439
440   s = gst_structure_new ("spectrum", "endtime", GST_TYPE_CLOCK_TIME,
441       endtime, NULL);
442
443   if (spectrum->message_magnitude) {
444     g_value_init (&v, GST_TYPE_LIST);
445     /* will copy-by-value */
446     gst_structure_set_value (s, "magnitude", &v);
447     g_value_unset (&v);
448
449     g_value_init (&v, G_TYPE_FLOAT);
450     l = (GValue *) gst_structure_get_value (s, "magnitude");
451     for (i = 0; i < spectrum->bands; i++) {
452       g_value_set_float (&v, spect_magnitude[i]);
453       gst_value_list_append_value (l, &v);      /* copies by value */
454     }
455     g_value_unset (&v);
456   }
457
458   if (spectrum->message_phase) {
459     g_value_init (&v, GST_TYPE_LIST);
460     /* will copy-by-value */
461     gst_structure_set_value (s, "phase", &v);
462     g_value_unset (&v);
463
464     g_value_init (&v, G_TYPE_FLOAT);
465     l = (GValue *) gst_structure_get_value (s, "phase");
466     for (i = 0; i < spectrum->bands; i++) {
467       g_value_set_float (&v, spect_phase[i]);
468       gst_value_list_append_value (l, &v);      /* copies by value */
469     }
470     g_value_unset (&v);
471   }
472
473   return gst_message_new_element (GST_OBJECT (spectrum), s);
474 }
475
476 #define DEFINE_PROCESS_FUNC_INT(width,next_width,max) \
477 static void \
478 process_s##width (GstSpectrum *spectrum, const gint##width *samples) \
479 { \
480   gfloat *spect_magnitude = spectrum->spect_magnitude; \
481   gfloat *spect_phase = spectrum->spect_phase; \
482   gint channels = GST_AUDIO_FILTER (spectrum)->format.channels; \
483   gint i, j, k; \
484   gint##next_width acc; \
485   GstFFTS##width##Complex *freqdata; \
486   GstFFTS##width *ctx; \
487   gint##width *in; \
488   gint nfft = 2 * spectrum->bands - 2; \
489   \
490   if (!spectrum->in) \
491     spectrum->in = (guint8 *) g_new (gint##width, nfft); \
492   \
493   in = (gint##width *) spectrum->in; \
494   \
495   for (i = 0, j = 0; i < nfft; i++) { \
496     /* convert to mono */ \
497     for (k = 0, acc = 0; k < channels; k++) \
498       acc += samples[j++]; \
499     in[i] = (gint##width) (acc / channels); \
500   } \
501   \
502   if (!spectrum->fft_ctx) { \
503     spectrum->fft_ctx = gst_fft_s##width##_new (nfft, FALSE); \
504     spectrum->fft_free_func = (GstSpectrumFFTFreeFunc) gst_fft_s##width##_free; \
505   } \
506   ctx = spectrum->fft_ctx; \
507   \
508   gst_fft_s##width##_window (ctx, in, GST_FFT_WINDOW_HAMMING); \
509   \
510   if (!spectrum->freqdata) \
511     spectrum->freqdata = g_new (GstFFTS##width##Complex, spectrum->bands); \
512   \
513   freqdata = (GstFFTS##width##Complex *) spectrum->freqdata; \
514   \
515   gst_fft_s##width##_fft (ctx, in, freqdata); \
516   spectrum->num_fft++; \
517   \
518   /* Calculate magnitude in db */ \
519   for (i = 0; i < spectrum->bands; i++) { \
520     gdouble val = (gdouble) freqdata[i].r * (gdouble) freqdata[i].r \
521         + (gdouble) freqdata[i].i * (gdouble) freqdata[i].i; \
522     val = sqrt (val); \
523     val = 20.0 * log10 (val / max); \
524     if (val > spectrum->threshold) \
525       val -= spectrum->threshold; \
526     else \
527       val = 0.0; \
528     spect_magnitude[i] += val; \
529   } \
530    \
531   /* Calculate phase */ \
532   for (i = 0; i < spectrum->bands; i++) \
533     spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r); \
534    \
535 }
536
537 DEFINE_PROCESS_FUNC_INT (16, 32, 32767.0);
538 DEFINE_PROCESS_FUNC_INT (32, 64, 2147483647.0);
539
540 #define DEFINE_PROCESS_FUNC_FLOAT(width,type) \
541 static void \
542 process_f##width (GstSpectrum *spectrum, const g##type *samples) \
543 { \
544   gfloat *spect_magnitude = spectrum->spect_magnitude; \
545   gfloat *spect_phase = spectrum->spect_phase; \
546   gint channels = GST_AUDIO_FILTER (spectrum)->format.channels; \
547   gint i, j, k; \
548   g##type acc; \
549   GstFFTF##width##Complex *freqdata; \
550   GstFFTF##width *ctx; \
551   g##type *in; \
552   gint nfft = 2 * spectrum->bands - 2; \
553   \
554   if (!spectrum->in) \
555     spectrum->in = (guint8 *) g_new (g##type, nfft); \
556   \
557   in = (g##type *) spectrum->in; \
558   \
559   for (i = 0, j = 0; i < nfft; i++) { \
560     /* convert to mono */ \
561     for (k = 0, acc = 0; k < channels; k++) \
562       acc += samples[j++]; \
563     in[i] = (g##type) (acc / channels); \
564     if (abs (in[i]) > 1.0) \
565       g_assert_not_reached(); \
566   } \
567   \
568   if (!spectrum->fft_ctx) { \
569     spectrum->fft_ctx = gst_fft_f##width##_new (nfft, FALSE); \
570     spectrum->fft_free_func = (GstSpectrumFFTFreeFunc) gst_fft_f##width##_free; \
571   } \
572   ctx = spectrum->fft_ctx; \
573   \
574   gst_fft_f##width##_window (ctx, in, GST_FFT_WINDOW_HAMMING); \
575   \
576   if (!spectrum->freqdata) \
577     spectrum->freqdata = g_new (GstFFTF##width##Complex, spectrum->bands); \
578   \
579   freqdata = (GstFFTF##width##Complex *) spectrum->freqdata; \
580   \
581   gst_fft_f##width##_fft (ctx, in, freqdata); \
582   spectrum->num_fft++; \
583   \
584   /* Calculate magnitude in db */ \
585   for (i = 0; i < spectrum->bands; i++) { \
586     gdouble val = freqdata[i].r * freqdata[i].r + freqdata[i].i * freqdata[i].i; \
587     val = sqrt (val); \
588     val = 20.0 * log10 (val / nfft); \
589     if (val > spectrum->threshold) \
590       val -= spectrum->threshold; \
591     else \
592       val = 0.0; \
593     spect_magnitude[i] += val; \
594   } \
595    \
596   /* Calculate phase */ \
597   for (i = 0; i < spectrum->bands; i++) \
598     spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r); \
599    \
600 }
601
602 DEFINE_PROCESS_FUNC_FLOAT (32, float);
603 DEFINE_PROCESS_FUNC_FLOAT (64, double);
604
605 static GstFlowReturn
606 gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * in)
607 {
608   GstSpectrum *spectrum = GST_SPECTRUM (trans);
609   gint wanted;
610   gint i;
611   gfloat *spect_magnitude = spectrum->spect_magnitude;
612   gfloat *spect_phase = spectrum->spect_phase;
613   gint rate = GST_AUDIO_FILTER (spectrum)->format.rate;
614   gint channels = GST_AUDIO_FILTER (spectrum)->format.channels;
615   gint width = GST_AUDIO_FILTER (spectrum)->format.width / 8;
616   gint nfft = 2 * spectrum->bands - 2;
617
618   GstClockTime endtime =
619       gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
620       GST_BUFFER_TIMESTAMP (in));
621   GstClockTime blktime = GST_FRAMES_TO_CLOCK_TIME (nfft, rate);
622
623   GST_LOG_OBJECT (spectrum, "input size: %d bytes", GST_BUFFER_SIZE (in));
624
625   /* can we do this nicer? */
626   gst_adapter_push (spectrum->adapter, gst_buffer_copy (in));
627   /* required number of bytes */
628   wanted = channels * nfft * width;
629
630   while (gst_adapter_available (spectrum->adapter) >= wanted) {
631     const guint8 *samples;
632
633     samples = gst_adapter_peek (spectrum->adapter, wanted);
634
635     spectrum->process (spectrum, samples);
636
637     spectrum->num_frames += nfft;
638     endtime += blktime;
639     /* do we need to message ? */
640     if (spectrum->num_frames >=
641         GST_CLOCK_TIME_TO_FRAMES (spectrum->interval, rate)) {
642       if (spectrum->message) {
643         GstMessage *m;
644
645         /* Calculate average */
646         for (i = 0; i < spectrum->bands; i++) {
647           spect_magnitude[i] /= spectrum->num_fft;
648           spect_phase[i] /= spectrum->num_fft;
649         }
650
651         m = gst_spectrum_message_new (spectrum, endtime);
652
653         gst_element_post_message (GST_ELEMENT (spectrum), m);
654       }
655       memset (spect_magnitude, 0, spectrum->bands * sizeof (gfloat));
656       memset (spect_phase, 0, spectrum->bands * sizeof (gfloat));
657       spectrum->num_frames = 0;
658       spectrum->num_fft = 0;
659     }
660
661     gst_adapter_flush (spectrum->adapter, wanted);
662   }
663
664   return GST_FLOW_OK;
665 }
666
667 static gboolean
668 plugin_init (GstPlugin * plugin)
669 {
670   return gst_element_register (plugin, "spectrum", GST_RANK_NONE,
671       GST_TYPE_SPECTRUM);
672 }
673
674 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
675     GST_VERSION_MINOR,
676     "spectrum",
677     "Run an FFT on the audio signal, output spectrum data",
678     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)