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