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