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