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