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