Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / gst / spectrum / gstspectrum.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *               <2006,2011> Stefan Kost <ensonic@users.sf.net>
4  *               <2007-2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
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  * The Spectrum element analyzes the frequency spectrum of an audio signal.
25  * If the #GstSpectrum:post-messages property is #TRUE, it sends analysis results
26  * as application messages named
27  * <classname>&quot;spectrum&quot;</classname> after each interval of time given
28  * by the #GstSpectrum:interval property.
29  *
30  * The message's structure contains some combination of these fields:
31  * <itemizedlist>
32  * <listitem>
33  *   <para>
34  *   #GstClockTime
35  *   <classname>&quot;timestamp&quot;</classname>:
36  *   the timestamp of the buffer that triggered the message.
37  *   </para>
38  * </listitem>
39  * <listitem>
40  *   <para>
41  *   #GstClockTime
42  *   <classname>&quot;stream-time&quot;</classname>:
43  *   the stream time of the buffer.
44  *   </para>
45  * </listitem>
46  * <listitem>
47  *   <para>
48  *   #GstClockTime
49  *   <classname>&quot;running-time&quot;</classname>:
50  *   the running_time of the buffer.
51  *   </para>
52  * </listitem>
53  * <listitem>
54  *   <para>
55  *   #GstClockTime
56  *   <classname>&quot;duration&quot;</classname>:
57  *   the duration of the buffer.
58  *   </para>
59  * </listitem>
60  * <listitem>
61  *   <para>
62  *   #GstClockTime
63  *   <classname>&quot;endtime&quot;</classname>:
64  *   the end time of the buffer that triggered the message as stream time (this
65  *   is deprecated, as it can be calculated from stream-time + duration)
66  *   </para>
67  * </listitem>
68  * <listitem>
69  *   <para>
70  *   #GstValueList of #gfloat
71  *   <classname>&quot;magnitude&quot;</classname>:
72  *   the level for each frequency band in dB. All values below the value of the
73  *   #GstSpectrum:threshold property will be set to the threshold. Only present
74  *   if the #GstSpectrum:message-magnitude property is %TRUE.
75  *   </para>
76  * </listitem>
77  * <listitem>
78  *   <para>
79  *   #GstValueList of #gfloat
80  *   <classname>&quot;phase&quot;</classname>:
81  *   The phase for each frequency band. The value is between -pi and pi. Only
82  *   present if the #GstSpectrum:message-phase property is %TRUE.
83  *   </para>
84  * </listitem>
85  * </itemizedlist>
86  *
87  * If #GstSpectrum:multi-channel property is set to true. magnitude and phase
88  * fields will be each a nested #GstValueArray. The first dimension are the
89  * channels and the second dimension are the values.
90  *
91  * <refsect2>
92  * <title>Example application</title>
93  * |[
94  * <xi:include xmlns:xi="http://www.w3.org/2003/XInclude" parse="text" href="../../../../tests/examples/spectrum/spectrum-example.c" />
95  * ]|
96  * </refsect2>
97  *
98  * Last reviewed on 2011-03-10 (0.10.29)
99  */
100
101 #ifdef HAVE_CONFIG_H
102 #include "config.h"
103 #endif
104
105 #include <string.h>
106 #include <math.h>
107 #include "gstspectrum.h"
108
109 GST_DEBUG_CATEGORY_STATIC (gst_spectrum_debug);
110 #define GST_CAT_DEFAULT gst_spectrum_debug
111
112 /* elementfactory information */
113
114 #define ALLOWED_CAPS \
115     "audio/x-raw-int, "                                               \
116     " width = (int) 16, "                                             \
117     " depth = (int) [ 1, 16 ], "                                      \
118     " signed = (boolean) true, "                                      \
119     " endianness = (int) BYTE_ORDER, "                                \
120     " rate = (int) [ 1, MAX ], "                                      \
121     " channels = (int) [ 1, MAX ]; "                                  \
122     "audio/x-raw-int, "                                               \
123     " width = (int) 24, "                                             \
124     " depth = (int) [ 1, 24 ], "                                      \
125     " signed = (boolean) true, "                                      \
126     " endianness = (int) BYTE_ORDER, "                                \
127     " rate = (int) [ 1, MAX ], "                                      \
128     " channels = (int) [ 1, MAX ]; "                                  \
129     "audio/x-raw-int, "                                               \
130     " width = (int) 32, "                                             \
131     " depth = (int) [ 1, 32 ], "                                      \
132     " signed = (boolean) true, "                                      \
133     " endianness = (int) BYTE_ORDER, "                                \
134     " rate = (int) [ 1, MAX ], "                                      \
135     " channels = (int) [ 1, MAX ]; "                                  \
136     "audio/x-raw-float, "                                             \
137     " width = (int) { 32, 64 }, "                                     \
138     " endianness = (int) BYTE_ORDER, "                                \
139     " rate = (int) [ 1, MAX ], "                                      \
140     " channels = (int) [ 1, MAX ]"
141
142 /* Spectrum properties */
143 #define DEFAULT_MESSAGE                 TRUE
144 #define DEFAULT_POST_MESSAGES                   TRUE
145 #define DEFAULT_MESSAGE_MAGNITUDE       TRUE
146 #define DEFAULT_MESSAGE_PHASE           FALSE
147 #define DEFAULT_INTERVAL                (GST_SECOND / 10)
148 #define DEFAULT_BANDS                   128
149 #define DEFAULT_THRESHOLD               -60
150 #define DEFAULT_MULTI_CHANNEL           FALSE
151
152 enum
153 {
154   PROP_0,
155   PROP_MESSAGE,
156   PROP_POST_MESSAGES,
157   PROP_MESSAGE_MAGNITUDE,
158   PROP_MESSAGE_PHASE,
159   PROP_INTERVAL,
160   PROP_BANDS,
161   PROP_THRESHOLD,
162   PROP_MULTI_CHANNEL
163 };
164
165 GST_BOILERPLATE (GstSpectrum, gst_spectrum, GstAudioFilter,
166     GST_TYPE_AUDIO_FILTER);
167
168 static void gst_spectrum_finalize (GObject * object);
169 static void gst_spectrum_set_property (GObject * object, guint prop_id,
170     const GValue * value, GParamSpec * pspec);
171 static void gst_spectrum_get_property (GObject * object, guint prop_id,
172     GValue * value, GParamSpec * pspec);
173 static gboolean gst_spectrum_start (GstBaseTransform * trans);
174 static gboolean gst_spectrum_stop (GstBaseTransform * trans);
175 static GstFlowReturn gst_spectrum_transform_ip (GstBaseTransform * trans,
176     GstBuffer * in);
177 static gboolean gst_spectrum_setup (GstAudioFilter * base,
178     GstRingBufferSpec * format);
179
180 static void
181 gst_spectrum_base_init (gpointer g_class)
182 {
183   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
184   GstCaps *caps;
185
186   gst_element_class_set_details_simple (element_class, "Spectrum analyzer",
187       "Filter/Analyzer/Audio",
188       "Run an FFT on the audio signal, output spectrum data",
189       "Erik Walthinsen <omega@cse.ogi.edu>, "
190       "Stefan Kost <ensonic@users.sf.net>, "
191       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
192
193   caps = gst_caps_from_string (ALLOWED_CAPS);
194   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (g_class),
195       caps);
196   gst_caps_unref (caps);
197 }
198
199 static void
200 gst_spectrum_class_init (GstSpectrumClass * klass)
201 {
202   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
203   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
204   GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (klass);
205
206   gobject_class->set_property = gst_spectrum_set_property;
207   gobject_class->get_property = gst_spectrum_get_property;
208   gobject_class->finalize = gst_spectrum_finalize;
209
210   trans_class->start = GST_DEBUG_FUNCPTR (gst_spectrum_start);
211   trans_class->stop = GST_DEBUG_FUNCPTR (gst_spectrum_stop);
212   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_spectrum_transform_ip);
213   trans_class->passthrough_on_same_caps = TRUE;
214
215   filter_class->setup = GST_DEBUG_FUNCPTR (gst_spectrum_setup);
216
217   /* FIXME 0.11, remove in favour of post-messages */
218   g_object_class_install_property (gobject_class, PROP_MESSAGE,
219       g_param_spec_boolean ("message", "Message",
220           "Whether to post a 'spectrum' element message on the bus for each "
221           "passed interval (deprecated, use post-messages)", DEFAULT_MESSAGE,
222           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
223   /**
224    * GstSpectrum:post-messages
225    *
226    * Post messages on the bus with spectrum information.
227    *
228    * Since: 0.10.17
229    */
230   g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
231       g_param_spec_boolean ("post-messages", "Post Messages",
232           "Whether to post a 'spectrum' element message on the bus for each "
233           "passed interval", DEFAULT_POST_MESSAGES,
234           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
235
236   g_object_class_install_property (gobject_class, PROP_MESSAGE_MAGNITUDE,
237       g_param_spec_boolean ("message-magnitude", "Magnitude",
238           "Whether to add a 'magnitude' field to the structure of any "
239           "'spectrum' element messages posted on the bus",
240           DEFAULT_MESSAGE_MAGNITUDE,
241           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
242
243   g_object_class_install_property (gobject_class, PROP_MESSAGE_PHASE,
244       g_param_spec_boolean ("message-phase", "Phase",
245           "Whether to add a 'phase' field to the structure of any "
246           "'spectrum' element messages posted on the bus",
247           DEFAULT_MESSAGE_PHASE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
248
249   g_object_class_install_property (gobject_class, PROP_INTERVAL,
250       g_param_spec_uint64 ("interval", "Interval",
251           "Interval of time between message posts (in nanoseconds)",
252           1, G_MAXUINT64, DEFAULT_INTERVAL,
253           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
254
255   g_object_class_install_property (gobject_class, PROP_BANDS,
256       g_param_spec_uint ("bands", "Bands", "Number of frequency bands",
257           0, G_MAXUINT, DEFAULT_BANDS,
258           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
259
260   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
261       g_param_spec_int ("threshold", "Threshold",
262           "dB threshold for result. All lower values will be set to this",
263           G_MININT, 0, DEFAULT_THRESHOLD,
264           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
265
266   /**
267    * GstSpectrum:multi-channel
268    *
269    * Send separate results for each channel
270    *
271    * Since: 0.10.29
272    */
273   g_object_class_install_property (gobject_class, PROP_MULTI_CHANNEL,
274       g_param_spec_boolean ("multi-channel", "Multichannel results",
275           "Send separate results for each channel",
276           DEFAULT_MULTI_CHANNEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
277
278   GST_DEBUG_CATEGORY_INIT (gst_spectrum_debug, "spectrum", 0,
279       "audio spectrum analyser element");
280 }
281
282 static void
283 gst_spectrum_init (GstSpectrum * spectrum, GstSpectrumClass * g_class)
284 {
285   spectrum->post_messages = DEFAULT_POST_MESSAGES;
286   spectrum->message_magnitude = DEFAULT_MESSAGE_MAGNITUDE;
287   spectrum->message_phase = DEFAULT_MESSAGE_PHASE;
288   spectrum->interval = DEFAULT_INTERVAL;
289   spectrum->bands = DEFAULT_BANDS;
290   spectrum->threshold = DEFAULT_THRESHOLD;
291 }
292
293 static void
294 gst_spectrum_alloc_channel_data (GstSpectrum * spectrum)
295 {
296   gint i;
297   GstSpectrumChannel *cd;
298   guint bands = spectrum->bands;
299   guint nfft = 2 * bands - 2;
300
301   g_assert (spectrum->channel_data == NULL);
302
303   spectrum->num_channels = (spectrum->multi_channel) ?
304       GST_AUDIO_FILTER (spectrum)->format.channels : 1;
305
306   GST_DEBUG_OBJECT (spectrum, "allocating data for %d channels",
307       spectrum->num_channels);
308
309   spectrum->channel_data = g_new (GstSpectrumChannel, spectrum->num_channels);
310   for (i = 0; i < spectrum->num_channels; i++) {
311     cd = &spectrum->channel_data[i];
312     cd->fft_ctx = gst_fft_f32_new (nfft, FALSE);
313     cd->input = g_new0 (gfloat, nfft);
314     cd->input_tmp = g_new0 (gfloat, nfft);
315     cd->freqdata = g_new0 (GstFFTF32Complex, bands);
316     cd->spect_magnitude = g_new0 (gfloat, bands);
317     cd->spect_phase = g_new0 (gfloat, bands);
318   }
319 }
320
321 static void
322 gst_spectrum_free_channel_data (GstSpectrum * spectrum)
323 {
324   if (spectrum->channel_data) {
325     gint i;
326     GstSpectrumChannel *cd;
327
328     GST_DEBUG_OBJECT (spectrum, "freeing data for %d channels",
329         spectrum->num_channels);
330
331     for (i = 0; i < spectrum->num_channels; i++) {
332       cd = &spectrum->channel_data[i];
333       if (cd->fft_ctx)
334         gst_fft_f32_free (cd->fft_ctx);
335       g_free (cd->input);
336       g_free (cd->input_tmp);
337       g_free (cd->freqdata);
338       g_free (cd->spect_magnitude);
339       g_free (cd->spect_phase);
340     }
341     g_free (spectrum->channel_data);
342     spectrum->channel_data = NULL;
343   }
344 }
345
346 static void
347 gst_spectrum_flush (GstSpectrum * spectrum)
348 {
349   spectrum->num_frames = 0;
350   spectrum->num_fft = 0;
351
352   spectrum->accumulated_error = 0;
353 }
354
355 static void
356 gst_spectrum_reset_state (GstSpectrum * spectrum)
357 {
358   GST_DEBUG_OBJECT (spectrum, "resetting state");
359
360   gst_spectrum_free_channel_data (spectrum);
361   gst_spectrum_flush (spectrum);
362 }
363
364 static void
365 gst_spectrum_finalize (GObject * object)
366 {
367   GstSpectrum *spectrum = GST_SPECTRUM (object);
368
369   gst_spectrum_reset_state (spectrum);
370
371   G_OBJECT_CLASS (parent_class)->finalize (object);
372 }
373
374 static void
375 gst_spectrum_set_property (GObject * object, guint prop_id,
376     const GValue * value, GParamSpec * pspec)
377 {
378   GstSpectrum *filter = GST_SPECTRUM (object);
379
380   switch (prop_id) {
381     case PROP_MESSAGE:
382     case PROP_POST_MESSAGES:
383       filter->post_messages = g_value_get_boolean (value);
384       break;
385     case PROP_MESSAGE_MAGNITUDE:
386       filter->message_magnitude = g_value_get_boolean (value);
387       break;
388     case PROP_MESSAGE_PHASE:
389       filter->message_phase = g_value_get_boolean (value);
390       break;
391     case PROP_INTERVAL:{
392       guint64 interval = g_value_get_uint64 (value);
393       if (filter->interval != interval) {
394         GST_BASE_TRANSFORM_LOCK (filter);
395         filter->interval = interval;
396         gst_spectrum_reset_state (filter);
397         GST_BASE_TRANSFORM_UNLOCK (filter);
398       }
399     }
400       break;
401     case PROP_BANDS:{
402       guint bands = g_value_get_uint (value);
403       if (filter->bands != bands) {
404         GST_BASE_TRANSFORM_LOCK (filter);
405         filter->bands = bands;
406         gst_spectrum_reset_state (filter);
407         GST_BASE_TRANSFORM_UNLOCK (filter);
408       }
409     }
410       break;
411     case PROP_THRESHOLD:
412       filter->threshold = g_value_get_int (value);
413       break;
414     case PROP_MULTI_CHANNEL:{
415       gboolean multi_channel = g_value_get_boolean (value);
416       if (filter->multi_channel != multi_channel) {
417         GST_BASE_TRANSFORM_LOCK (filter);
418         filter->multi_channel = multi_channel;
419         gst_spectrum_reset_state (filter);
420         GST_BASE_TRANSFORM_UNLOCK (filter);
421       }
422     }
423       break;
424     default:
425       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
426       break;
427   }
428 }
429
430 static void
431 gst_spectrum_get_property (GObject * object, guint prop_id,
432     GValue * value, GParamSpec * pspec)
433 {
434   GstSpectrum *filter = GST_SPECTRUM (object);
435
436   switch (prop_id) {
437     case PROP_MESSAGE:
438     case PROP_POST_MESSAGES:
439       g_value_set_boolean (value, filter->post_messages);
440       break;
441     case PROP_MESSAGE_MAGNITUDE:
442       g_value_set_boolean (value, filter->message_magnitude);
443       break;
444     case PROP_MESSAGE_PHASE:
445       g_value_set_boolean (value, filter->message_phase);
446       break;
447     case PROP_INTERVAL:
448       g_value_set_uint64 (value, filter->interval);
449       break;
450     case PROP_BANDS:
451       g_value_set_uint (value, filter->bands);
452       break;
453     case PROP_THRESHOLD:
454       g_value_set_int (value, filter->threshold);
455       break;
456     case PROP_MULTI_CHANNEL:
457       g_value_set_boolean (value, filter->multi_channel);
458       break;
459     default:
460       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
461       break;
462   }
463 }
464
465 static gboolean
466 gst_spectrum_start (GstBaseTransform * trans)
467 {
468   GstSpectrum *spectrum = GST_SPECTRUM (trans);
469
470   gst_spectrum_reset_state (spectrum);
471
472   return TRUE;
473 }
474
475 static gboolean
476 gst_spectrum_stop (GstBaseTransform * trans)
477 {
478   GstSpectrum *spectrum = GST_SPECTRUM (trans);
479
480   gst_spectrum_reset_state (spectrum);
481
482   return TRUE;
483 }
484
485 /* mixing data readers */
486
487 static void
488 input_data_mixed_float (const guint8 * _in, gfloat * out, guint len,
489     guint channels, gfloat max_value, guint op, guint nfft)
490 {
491   guint i, j, ip = 0;
492   gfloat v;
493   gfloat *in = (gfloat *) _in;
494
495   for (j = 0; j < len; j++) {
496     v = in[ip++];
497     for (i = 1; i < channels; i++)
498       v += in[ip++];
499     out[op] = v / channels;
500     op = (op + 1) % nfft;
501   }
502 }
503
504 static void
505 input_data_mixed_double (const guint8 * _in, gfloat * out, guint len,
506     guint channels, gfloat max_value, guint op, guint nfft)
507 {
508   guint i, j, ip = 0;
509   gfloat v;
510   gdouble *in = (gdouble *) _in;
511
512   for (j = 0; j < len; j++) {
513     v = in[ip++];
514     for (i = 1; i < channels; i++)
515       v += in[ip++];
516     out[op] = v / channels;
517     op = (op + 1) % nfft;
518   }
519 }
520
521 static void
522 input_data_mixed_int32 (const guint8 * _in, gfloat * out, guint len,
523     guint channels, gfloat max_value, guint op, guint nfft)
524 {
525   guint i, j, ip = 0;
526   gint32 *in = (gint32 *) _in;
527   gfloat v;
528
529   for (j = 0; j < len; j++) {
530     v = in[ip++] * 2 + 1;
531     for (i = 1; i < channels; i++)
532       v += in[ip++] * 2 + 1;
533     out[op] = v / channels;
534     op = (op + 1) % nfft;
535   }
536 }
537
538 static void
539 input_data_mixed_int32_max (const guint8 * _in, gfloat * out, guint len,
540     guint channels, gfloat max_value, guint op, guint nfft)
541 {
542   guint i, j, ip = 0;
543   gint32 *in = (gint32 *) _in;
544   gfloat v;
545
546   for (j = 0; j < len; j++) {
547     v = in[ip++] / max_value;
548     for (i = 1; i < channels; i++)
549       v += in[ip++] / max_value;
550     out[op] = v / channels;
551     op = (op + 1) % nfft;
552   }
553 }
554
555 static void
556 input_data_mixed_int24 (const guint8 * _in, gfloat * out, guint len,
557     guint channels, gfloat max_value, guint op, guint nfft)
558 {
559   guint i, j;
560   gfloat v = 0.0;
561
562   for (j = 0; j < len; j++) {
563     for (i = 0; i < channels; i++) {
564 #if G_BYTE_ORDER == G_BIG_ENDIAN
565       gint32 value = GST_READ_UINT24_BE (_in);
566 #else
567       gint32 value = GST_READ_UINT24_LE (_in);
568 #endif
569       if (value & 0x00800000)
570         value |= 0xff000000;
571       v += value * 2 + 1;
572       _in += 3;
573     }
574     out[op] = v / channels;
575     op = (op + 1) % nfft;
576   }
577 }
578
579 static void
580 input_data_mixed_int24_max (const guint8 * _in, gfloat * out, guint len,
581     guint channels, gfloat max_value, guint op, guint nfft)
582 {
583   guint i, j;
584   gfloat v = 0.0;
585
586   for (j = 0; j < len; j++) {
587     for (i = 0; i < channels; i++) {
588 #if G_BYTE_ORDER == G_BIG_ENDIAN
589       gint32 value = GST_READ_UINT24_BE (_in);
590 #else
591       gint32 value = GST_READ_UINT24_LE (_in);
592 #endif
593       if (value & 0x00800000)
594         value |= 0xff000000;
595       v += value / max_value;
596       _in += 3;
597     }
598     out[op] = v / channels;
599     op = (op + 1) % nfft;
600   }
601 }
602
603 static void
604 input_data_mixed_int16 (const guint8 * _in, gfloat * out, guint len,
605     guint channels, gfloat max_value, guint op, guint nfft)
606 {
607   guint i, j, ip = 0;
608   gint16 *in = (gint16 *) _in;
609   gfloat v;
610
611   for (j = 0; j < len; j++) {
612     v = in[ip++] * 2 + 1;
613     for (i = 1; i < channels; i++)
614       v += in[ip++] * 2 + 1;
615     out[op] = v / channels;
616     op = (op + 1) % nfft;
617   }
618 }
619
620 static void
621 input_data_mixed_int16_max (const guint8 * _in, gfloat * out, guint len,
622     guint channels, gfloat max_value, guint op, guint nfft)
623 {
624   guint i, j, ip = 0;
625   gint16 *in = (gint16 *) _in;
626   gfloat v;
627
628   for (j = 0; j < len; j++) {
629     v = in[ip++] / max_value;
630     for (i = 1; i < channels; i++)
631       v += in[ip++] / max_value;
632     out[op] = v / channels;
633     op = (op + 1) % nfft;
634   }
635 }
636
637 /* non mixing data readers */
638
639 static void
640 input_data_float (const guint8 * _in, gfloat * out, guint len, guint channels,
641     gfloat max_value, guint op, guint nfft)
642 {
643   guint j, ip;
644   gfloat *in = (gfloat *) _in;
645
646   for (j = 0, ip = 0; j < len; j++, ip += channels) {
647     out[op] = in[ip];
648     op = (op + 1) % nfft;
649   }
650 }
651
652 static void
653 input_data_double (const guint8 * _in, gfloat * out, guint len, guint channels,
654     gfloat max_value, guint op, guint nfft)
655 {
656   guint j, ip;
657   gdouble *in = (gdouble *) _in;
658
659   for (j = 0, ip = 0; j < len; j++, ip += channels) {
660     out[op] = in[ip];
661     op = (op + 1) % nfft;
662   }
663 }
664
665 static void
666 input_data_int32 (const guint8 * _in, gfloat * out, guint len, guint channels,
667     gfloat max_value, guint op, guint nfft)
668 {
669   guint j, ip;
670   gint32 *in = (gint32 *) _in;
671
672   for (j = 0, ip = 0; j < len; j++, ip += channels) {
673     out[op] = in[ip] * 2 + 1;
674     op = (op + 1) % nfft;
675   }
676 }
677
678 static void
679 input_data_int32_max (const guint8 * _in, gfloat * out, guint len,
680     guint channels, gfloat max_value, guint op, guint nfft)
681 {
682   guint j, ip;
683   gint32 *in = (gint32 *) _in;
684
685   for (j = 0, ip = 0; j < len; j++, ip += channels) {
686     out[op] = in[ip] / max_value;
687     op = (op + 1) % nfft;
688   }
689 }
690
691 static void
692 input_data_int24 (const guint8 * _in, gfloat * out, guint len, guint channels,
693     gfloat max_value, guint op, guint nfft)
694 {
695   guint j;
696
697   for (j = 0; j < len; j++) {
698 #if G_BYTE_ORDER == G_BIG_ENDIAN
699     gint32 v = GST_READ_UINT24_BE (_in);
700 #else
701     gint32 v = GST_READ_UINT24_LE (_in);
702 #endif
703     if (v & 0x00800000)
704       v |= 0xff000000;
705     _in += 3 * channels;
706     out[op] = v * 2 + 1;
707     op = (op + 1) % nfft;
708   }
709 }
710
711 static void
712 input_data_int24_max (const guint8 * _in, gfloat * out, guint len,
713     guint channels, gfloat max_value, guint op, guint nfft)
714 {
715   guint j;
716
717   for (j = 0; j < len; j++) {
718 #if G_BYTE_ORDER == G_BIG_ENDIAN
719     gint32 v = GST_READ_UINT24_BE (_in);
720 #else
721     gint32 v = GST_READ_UINT24_LE (_in);
722 #endif
723     if (v & 0x00800000)
724       v |= 0xff000000;
725     _in += 3 * channels;
726     out[op] = v / max_value;
727     op = (op + 1) % nfft;
728   }
729 }
730
731 static void
732 input_data_int16 (const guint8 * _in, gfloat * out, guint len, guint channels,
733     gfloat max_value, guint op, guint nfft)
734 {
735   guint j, ip;
736   gint16 *in = (gint16 *) _in;
737
738   for (j = 0, ip = 0; j < len; j++, ip += channels) {
739     out[op] = in[ip] * 2 + 1;
740     op = (op + 1) % nfft;
741   }
742 }
743
744 static void
745 input_data_int16_max (const guint8 * _in, gfloat * out, guint len,
746     guint channels, gfloat max_value, guint op, guint nfft)
747 {
748   guint j, ip;
749   gint16 *in = (gint16 *) _in;
750
751   for (j = 0, ip = 0; j < len; j++, ip += channels) {
752     out[op] = in[ip] / max_value;
753     op = (op + 1) % nfft;
754   }
755 }
756
757 static gboolean
758 gst_spectrum_setup (GstAudioFilter * base, GstRingBufferSpec * format)
759 {
760   GstSpectrum *spectrum = GST_SPECTRUM (base);
761   guint width = format->width / 8;
762   gboolean is_float = (format->type == GST_BUFTYPE_FLOAT);
763   /* max_value will be 0 when depth is 1,
764    * interpret -1 and 0 as -1 and +1 if that's the case. */
765   guint max_value = (1UL << (format->depth - 1)) - 1;
766   gboolean multi_channel = spectrum->multi_channel;
767   GstSpectrumInputData input_data = NULL;
768
769   if (is_float) {
770     if (width == 4) {
771       input_data = multi_channel ? input_data_float : input_data_mixed_float;
772     } else if (width == 8) {
773       input_data = multi_channel ? input_data_double : input_data_mixed_double;
774     } else {
775       g_assert_not_reached ();
776     }
777   } else {
778     if (width == 4) {
779       if (max_value) {
780         input_data =
781             multi_channel ? input_data_int32_max : input_data_mixed_int32_max;
782       } else {
783         input_data = multi_channel ? input_data_int32 : input_data_mixed_int32;
784       }
785     } else if (width == 3) {
786       if (max_value) {
787         input_data =
788             multi_channel ? input_data_int24_max : input_data_mixed_int24_max;
789       } else {
790         input_data = multi_channel ? input_data_int24 : input_data_mixed_int24;
791       }
792     } else if (width == 2) {
793       if (max_value) {
794         input_data =
795             multi_channel ? input_data_int16_max : input_data_mixed_int16_max;
796       } else {
797         input_data = multi_channel ? input_data_int16 : input_data_mixed_int16;
798       }
799     } else {
800       g_assert_not_reached ();
801     }
802   }
803
804   spectrum->input_data = input_data;
805   gst_spectrum_reset_state (spectrum);
806   return TRUE;
807 }
808
809 static GValue *
810 gst_spectrum_message_add_container (GstStructure * s, GType type,
811     const gchar * name)
812 {
813   GValue v = { 0, };
814
815   g_value_init (&v, type);
816   /* will copy-by-value */
817   gst_structure_set_value (s, name, &v);
818   g_value_unset (&v);
819   return (GValue *) gst_structure_get_value (s, name);
820 }
821
822 static void
823 gst_spectrum_message_add_list (GValue * cv, gfloat * data, guint num_values)
824 {
825   GValue v = { 0, };
826   guint i;
827
828   g_value_init (&v, G_TYPE_FLOAT);
829   for (i = 0; i < num_values; i++) {
830     g_value_set_float (&v, data[i]);
831     gst_value_list_append_value (cv, &v);       /* copies by value */
832   }
833   g_value_unset (&v);
834 }
835
836 static void
837 gst_spectrum_message_add_array (GValue * cv, gfloat * data, guint num_values)
838 {
839   GValue v = { 0, };
840   GValue a = { 0, };
841   guint i;
842
843   g_value_init (&a, GST_TYPE_ARRAY);
844
845   g_value_init (&v, G_TYPE_FLOAT);
846   for (i = 0; i < num_values; i++) {
847     g_value_set_float (&v, data[i]);
848     gst_value_array_append_value (&a, &v);      /* copies by value */
849   }
850   g_value_unset (&v);
851
852   gst_value_array_append_value (cv, &a);        /* copies by value */
853   g_value_unset (&a);
854 }
855
856 static GstMessage *
857 gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime timestamp,
858     GstClockTime duration)
859 {
860   GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (spectrum);
861   GstSpectrumChannel *cd;
862   GstStructure *s;
863   GValue *mcv = NULL, *pcv = NULL;
864   GstClockTime endtime, running_time, stream_time;
865
866   GST_DEBUG_OBJECT (spectrum, "preparing message, bands =%d ", spectrum->bands);
867
868   running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
869       timestamp);
870   stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
871       timestamp);
872   /* endtime is for backwards compatibility */
873   endtime = stream_time + duration;
874
875   s = gst_structure_new ("spectrum",
876       "endtime", GST_TYPE_CLOCK_TIME, endtime,
877       "timestamp", G_TYPE_UINT64, timestamp,
878       "stream-time", G_TYPE_UINT64, stream_time,
879       "running-time", G_TYPE_UINT64, running_time,
880       "duration", G_TYPE_UINT64, duration, NULL);
881
882   if (!spectrum->multi_channel) {
883     cd = &spectrum->channel_data[0];
884
885     if (spectrum->message_magnitude) {
886       /* FIXME 0.11: this should be an array, not a list */
887       mcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "magnitude");
888       gst_spectrum_message_add_list (mcv, cd->spect_magnitude, spectrum->bands);
889     }
890     if (spectrum->message_phase) {
891       /* FIXME 0.11: this should be an array, not a list */
892       pcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "phase");
893       gst_spectrum_message_add_list (pcv, cd->spect_phase, spectrum->bands);
894     }
895   } else {
896     guint c;
897     guint channels = GST_AUDIO_FILTER (spectrum)->format.channels;
898
899     if (spectrum->message_magnitude) {
900       mcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "magnitude");
901     }
902     if (spectrum->message_phase) {
903       pcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "phase");
904     }
905
906     for (c = 0; c < channels; c++) {
907       cd = &spectrum->channel_data[c];
908
909       if (spectrum->message_magnitude) {
910         gst_spectrum_message_add_array (mcv, cd->spect_magnitude,
911             spectrum->bands);
912       }
913       if (spectrum->message_phase) {
914         gst_spectrum_message_add_array (pcv, cd->spect_magnitude,
915             spectrum->bands);
916       }
917     }
918   }
919   return gst_message_new_element (GST_OBJECT (spectrum), s);
920 }
921
922 static void
923 gst_spectrum_run_fft (GstSpectrum * spectrum, GstSpectrumChannel * cd,
924     guint input_pos)
925 {
926   guint i;
927   guint bands = spectrum->bands;
928   guint nfft = 2 * bands - 2;
929   gint threshold = spectrum->threshold;
930   gfloat *input = cd->input;
931   gfloat *input_tmp = cd->input_tmp;
932   gfloat *spect_magnitude = cd->spect_magnitude;
933   gfloat *spect_phase = cd->spect_phase;
934   GstFFTF32Complex *freqdata = cd->freqdata;
935   GstFFTF32 *fft_ctx = cd->fft_ctx;
936
937   for (i = 0; i < nfft; i++)
938     input_tmp[i] = input[(input_pos + i) % nfft];
939
940   gst_fft_f32_window (fft_ctx, input_tmp, GST_FFT_WINDOW_HAMMING);
941
942   gst_fft_f32_fft (fft_ctx, input_tmp, freqdata);
943
944   if (spectrum->message_magnitude) {
945     gdouble val;
946     /* Calculate magnitude in db */
947     for (i = 0; i < bands; i++) {
948       val = freqdata[i].r * freqdata[i].r;
949       val += freqdata[i].i * freqdata[i].i;
950       val /= nfft * nfft;
951       val = 10.0 * log10 (val);
952       if (val < threshold)
953         val = threshold;
954       spect_magnitude[i] += val;
955     }
956   }
957
958   if (spectrum->message_phase) {
959     /* Calculate phase */
960     for (i = 0; i < bands; i++)
961       spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r);
962   }
963 }
964
965 static void
966 gst_spectrum_prepare_message_data (GstSpectrum * spectrum,
967     GstSpectrumChannel * cd)
968 {
969   guint i;
970   guint bands = spectrum->bands;
971   guint num_fft = spectrum->num_fft;
972
973   /* Calculate average */
974   if (spectrum->message_magnitude) {
975     gfloat *spect_magnitude = cd->spect_magnitude;
976     for (i = 0; i < bands; i++)
977       spect_magnitude[i] /= num_fft;
978   }
979   if (spectrum->message_phase) {
980     gfloat *spect_phase = cd->spect_phase;
981     for (i = 0; i < bands; i++)
982       spect_phase[i] /= num_fft;
983   }
984 }
985
986 static void
987 gst_spectrum_reset_message_data (GstSpectrum * spectrum,
988     GstSpectrumChannel * cd)
989 {
990   guint bands = spectrum->bands;
991   gfloat *spect_magnitude = cd->spect_magnitude;
992   gfloat *spect_phase = cd->spect_phase;
993
994   /* reset spectrum accumulators */
995   memset (spect_magnitude, 0, bands * sizeof (gfloat));
996   memset (spect_phase, 0, bands * sizeof (gfloat));
997 }
998
999 static GstFlowReturn
1000 gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
1001 {
1002   GstSpectrum *spectrum = GST_SPECTRUM (trans);
1003   GstRingBufferSpec *format = &GST_AUDIO_FILTER (spectrum)->format;
1004   guint rate = format->rate;
1005   guint channels = format->channels;
1006   guint output_channels = spectrum->multi_channel ? channels : 1;
1007   guint c;
1008   guint width = format->width / 8;
1009   gfloat max_value = (1UL << (format->depth - 1)) - 1;
1010   guint bands = spectrum->bands;
1011   guint nfft = 2 * bands - 2;
1012   guint input_pos;
1013   gfloat *input;
1014   const guint8 *data = GST_BUFFER_DATA (buffer);
1015   guint size = GST_BUFFER_SIZE (buffer);
1016   guint frame_size = width * channels;
1017   guint fft_todo, msg_todo, block_size;
1018   gboolean have_full_interval;
1019   GstSpectrumChannel *cd;
1020   GstSpectrumInputData input_data;
1021
1022   GST_LOG_OBJECT (spectrum, "input size: %d bytes", GST_BUFFER_SIZE (buffer));
1023
1024   if (GST_BUFFER_IS_DISCONT (buffer)) {
1025     GST_DEBUG_OBJECT (spectrum, "Discontinuity detected -- flushing");
1026     gst_spectrum_flush (spectrum);
1027   }
1028
1029   /* If we don't have a FFT context yet (or it was reset due to parameter
1030    * changes) get one and allocate memory for everything
1031    */
1032   if (spectrum->channel_data == NULL) {
1033     GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands);
1034
1035     gst_spectrum_alloc_channel_data (spectrum);
1036
1037     /* number of sample frames we process before posting a message
1038      * interval is in ns */
1039     spectrum->frames_per_interval =
1040         gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND);
1041     spectrum->frames_todo = spectrum->frames_per_interval;
1042     /* rounding error for frames_per_interval in ns,
1043      * aggregated it in accumulated_error */
1044     spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
1045     if (spectrum->frames_per_interval == 0)
1046       spectrum->frames_per_interval = 1;
1047
1048     GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
1049         G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
1050         GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval,
1051         GST_TIME_ARGS (spectrum->error_per_interval));
1052
1053     spectrum->input_pos = 0;
1054
1055     gst_spectrum_flush (spectrum);
1056   }
1057
1058   if (spectrum->num_frames == 0)
1059     spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);
1060
1061   input_pos = spectrum->input_pos;
1062   input_data = spectrum->input_data;
1063
1064   while (size >= frame_size) {
1065     /* run input_data for a chunk of data */
1066     fft_todo = nfft - (spectrum->num_frames % nfft);
1067     msg_todo = spectrum->frames_todo - spectrum->num_frames;
1068     GST_LOG_OBJECT (spectrum,
1069         "message frames todo: %u, fft frames todo: %u, input frames %u",
1070         msg_todo, fft_todo, (size / frame_size));
1071     block_size = msg_todo;
1072     if (block_size > (size / frame_size))
1073       block_size = (size / frame_size);
1074     if (block_size > fft_todo)
1075       block_size = fft_todo;
1076
1077     for (c = 0; c < output_channels; c++) {
1078       cd = &spectrum->channel_data[c];
1079       input = cd->input;
1080       /* Move the current frames into our ringbuffers */
1081       input_data (data + c * width, input, block_size, channels, max_value,
1082           input_pos, nfft);
1083     }
1084     data += block_size * frame_size;
1085     size -= block_size * frame_size;
1086     input_pos = (input_pos + block_size) % nfft;
1087     spectrum->num_frames += block_size;
1088
1089     have_full_interval = (spectrum->num_frames == spectrum->frames_todo);
1090
1091     GST_LOG_OBJECT (spectrum, "size: %u, do-fft = %d, do-message = %d", size,
1092         (spectrum->num_frames % nfft == 0), have_full_interval);
1093
1094     /* If we have enough frames for an FFT or we have all frames required for
1095      * the interval and we haven't run a FFT, then run an FFT */
1096     if ((spectrum->num_frames % nfft == 0) ||
1097         (have_full_interval && !spectrum->num_fft)) {
1098       for (c = 0; c < output_channels; c++) {
1099         cd = &spectrum->channel_data[c];
1100         gst_spectrum_run_fft (spectrum, cd, input_pos);
1101       }
1102       spectrum->num_fft++;
1103     }
1104
1105     /* Do we have the FFTs for one interval? */
1106     if (have_full_interval) {
1107       GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT
1108           " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft,
1109           spectrum->num_frames, spectrum->frames_per_interval,
1110           GST_TIME_ARGS (spectrum->accumulated_error));
1111
1112       spectrum->frames_todo = spectrum->frames_per_interval;
1113       if (spectrum->accumulated_error >= GST_SECOND) {
1114         spectrum->accumulated_error -= GST_SECOND;
1115         spectrum->frames_todo++;
1116       }
1117       spectrum->accumulated_error += spectrum->error_per_interval;
1118
1119       if (spectrum->post_messages) {
1120         GstMessage *m;
1121
1122         for (c = 0; c < output_channels; c++) {
1123           cd = &spectrum->channel_data[c];
1124           gst_spectrum_prepare_message_data (spectrum, cd);
1125         }
1126
1127         m = gst_spectrum_message_new (spectrum, spectrum->message_ts,
1128             spectrum->interval);
1129
1130         gst_element_post_message (GST_ELEMENT (spectrum), m);
1131       }
1132
1133       if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts))
1134         spectrum->message_ts +=
1135             gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);
1136
1137       for (c = 0; c < output_channels; c++) {
1138         cd = &spectrum->channel_data[c];
1139         gst_spectrum_reset_message_data (spectrum, cd);
1140       }
1141       spectrum->num_frames = 0;
1142       spectrum->num_fft = 0;
1143     }
1144   }
1145
1146   spectrum->input_pos = input_pos;
1147
1148   g_assert (size == 0);
1149
1150   return GST_FLOW_OK;
1151 }
1152
1153 static gboolean
1154 plugin_init (GstPlugin * plugin)
1155 {
1156   return gst_element_register (plugin, "spectrum", GST_RANK_NONE,
1157       GST_TYPE_SPECTRUM);
1158 }
1159
1160 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1161     GST_VERSION_MINOR,
1162     "spectrum",
1163     "Run an FFT on the audio signal, output spectrum data",
1164     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)