Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst / spectrum / gstspectrum.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *               <2006,2011> Stefan Kost <ensonic@users.sf.net>
4  *               <2007-2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /**
22  * SECTION:element-spectrum
23  *
24  * The Spectrum element analyzes the frequency spectrum of an audio signal.
25  * If the #GstSpectrum:post-messages property is #TRUE, it sends analysis results
26  * as application messages named
27  * <classname>&quot;spectrum&quot;</classname> after each interval of time given
28  * by the #GstSpectrum:interval property.
29  *
30  * The message's structure contains some combination of these fields:
31  * <itemizedlist>
32  * <listitem>
33  *   <para>
34  *   #GstClockTime
35  *   <classname>&quot;timestamp&quot;</classname>:
36  *   the timestamp of the buffer that triggered the message.
37  *   </para>
38  * </listitem>
39  * <listitem>
40  *   <para>
41  *   #GstClockTime
42  *   <classname>&quot;stream-time&quot;</classname>:
43  *   the stream time of the buffer.
44  *   </para>
45  * </listitem>
46  * <listitem>
47  *   <para>
48  *   #GstClockTime
49  *   <classname>&quot;running-time&quot;</classname>:
50  *   the running_time of the buffer.
51  *   </para>
52  * </listitem>
53  * <listitem>
54  *   <para>
55  *   #GstClockTime
56  *   <classname>&quot;duration&quot;</classname>:
57  *   the duration of the buffer.
58  *   </para>
59  * </listitem>
60  * <listitem>
61  *   <para>
62  *   #GstClockTime
63  *   <classname>&quot;endtime&quot;</classname>:
64  *   the end time of the buffer that triggered the message as stream time (this
65  *   is deprecated, as it can be calculated from stream-time + duration)
66  *   </para>
67  * </listitem>
68  * <listitem>
69  *   <para>
70  *   #GstValueList of #gfloat
71  *   <classname>&quot;magnitude&quot;</classname>:
72  *   the level for each frequency band in dB. All values below the value of the
73  *   #GstSpectrum:threshold property will be set to the threshold. Only present
74  *   if the #GstSpectrum:message-magnitude property is %TRUE.
75  *   </para>
76  * </listitem>
77  * <listitem>
78  *   <para>
79  *   #GstValueList of #gfloat
80  *   <classname>&quot;phase&quot;</classname>:
81  *   The phase for each frequency band. The value is between -pi and pi. Only
82  *   present if the #GstSpectrum:message-phase property is %TRUE.
83  *   </para>
84  * </listitem>
85  * </itemizedlist>
86  *
87  * If #GstSpectrum:multi-channel property is set to true. magnitude and phase
88  * fields will be each a nested #GstValueArray. The first dimension are the
89  * channels and the second dimension are the values.
90  *
91  * <refsect2>
92  * <title>Example application</title>
93  * |[
94  * <xi:include xmlns:xi="http://www.w3.org/2003/XInclude" parse="text" href="../../../../tests/examples/spectrum/spectrum-example.c" />
95  * ]|
96  * </refsect2>
97  *
98  * Last reviewed on 2011-03-10 (0.10.29)
99  */
100
101 #ifdef HAVE_CONFIG_H
102 #include "config.h"
103 #endif
104
105 #include <string.h>
106 #include <math.h>
107 #include "gstspectrum.h"
108
109 GST_DEBUG_CATEGORY_STATIC (gst_spectrum_debug);
110 #define GST_CAT_DEFAULT gst_spectrum_debug
111
112 /* elementfactory information */
113 #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   g_mutex_init (&spectrum->lock);
260 }
261
262 static void
263 gst_spectrum_alloc_channel_data (GstSpectrum * spectrum)
264 {
265   gint i;
266   GstSpectrumChannel *cd;
267   guint bands = spectrum->bands;
268   guint nfft = 2 * bands - 2;
269
270   g_assert (spectrum->channel_data == NULL);
271
272   spectrum->num_channels = (spectrum->multi_channel) ?
273       GST_AUDIO_FILTER_CHANNELS (spectrum) : 1;
274
275   GST_DEBUG_OBJECT (spectrum, "allocating data for %d channels",
276       spectrum->num_channels);
277
278   spectrum->channel_data = g_new (GstSpectrumChannel, spectrum->num_channels);
279   for (i = 0; i < spectrum->num_channels; i++) {
280     cd = &spectrum->channel_data[i];
281     cd->fft_ctx = gst_fft_f32_new (nfft, FALSE);
282     cd->input = g_new0 (gfloat, nfft);
283     cd->input_tmp = g_new0 (gfloat, nfft);
284     cd->freqdata = g_new0 (GstFFTF32Complex, bands);
285     cd->spect_magnitude = g_new0 (gfloat, bands);
286     cd->spect_phase = g_new0 (gfloat, bands);
287   }
288 }
289
290 static void
291 gst_spectrum_free_channel_data (GstSpectrum * spectrum)
292 {
293   if (spectrum->channel_data) {
294     gint i;
295     GstSpectrumChannel *cd;
296
297     GST_DEBUG_OBJECT (spectrum, "freeing data for %d channels",
298         spectrum->num_channels);
299
300     for (i = 0; i < spectrum->num_channels; i++) {
301       cd = &spectrum->channel_data[i];
302       if (cd->fft_ctx)
303         gst_fft_f32_free (cd->fft_ctx);
304       g_free (cd->input);
305       g_free (cd->input_tmp);
306       g_free (cd->freqdata);
307       g_free (cd->spect_magnitude);
308       g_free (cd->spect_phase);
309     }
310     g_free (spectrum->channel_data);
311     spectrum->channel_data = NULL;
312   }
313 }
314
315 static void
316 gst_spectrum_flush (GstSpectrum * spectrum)
317 {
318   spectrum->num_frames = 0;
319   spectrum->num_fft = 0;
320
321   spectrum->accumulated_error = 0;
322 }
323
324 static void
325 gst_spectrum_reset_state (GstSpectrum * spectrum)
326 {
327   GST_DEBUG_OBJECT (spectrum, "resetting state");
328
329   gst_spectrum_free_channel_data (spectrum);
330   gst_spectrum_flush (spectrum);
331 }
332
333 static void
334 gst_spectrum_finalize (GObject * object)
335 {
336   GstSpectrum *spectrum = GST_SPECTRUM (object);
337
338   gst_spectrum_reset_state (spectrum);
339   g_mutex_clear (&spectrum->lock);
340
341   G_OBJECT_CLASS (parent_class)->finalize (object);
342 }
343
344 static void
345 gst_spectrum_set_property (GObject * object, guint prop_id,
346     const GValue * value, GParamSpec * pspec)
347 {
348   GstSpectrum *filter = GST_SPECTRUM (object);
349
350   switch (prop_id) {
351     case PROP_POST_MESSAGES:
352       filter->post_messages = g_value_get_boolean (value);
353       break;
354     case PROP_MESSAGE_MAGNITUDE:
355       filter->message_magnitude = g_value_get_boolean (value);
356       break;
357     case PROP_MESSAGE_PHASE:
358       filter->message_phase = g_value_get_boolean (value);
359       break;
360     case PROP_INTERVAL:{
361       guint64 interval = g_value_get_uint64 (value);
362       g_mutex_lock (&filter->lock);
363       if (filter->interval != interval) {
364         filter->interval = interval;
365         gst_spectrum_reset_state (filter);
366       }
367       g_mutex_unlock (&filter->lock);
368       break;
369     }
370     case PROP_BANDS:{
371       guint bands = g_value_get_uint (value);
372       g_mutex_lock (&filter->lock);
373       if (filter->bands != bands) {
374         filter->bands = bands;
375         gst_spectrum_reset_state (filter);
376       }
377       g_mutex_unlock (&filter->lock);
378       break;
379     }
380     case PROP_THRESHOLD:
381       filter->threshold = g_value_get_int (value);
382       break;
383     case PROP_MULTI_CHANNEL:{
384       gboolean multi_channel = g_value_get_boolean (value);
385       g_mutex_lock (&filter->lock);
386       if (filter->multi_channel != multi_channel) {
387         filter->multi_channel = multi_channel;
388         gst_spectrum_reset_state (filter);
389       }
390       g_mutex_unlock (&filter->lock);
391       break;
392     }
393     default:
394       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
395       break;
396   }
397 }
398
399 static void
400 gst_spectrum_get_property (GObject * object, guint prop_id,
401     GValue * value, GParamSpec * pspec)
402 {
403   GstSpectrum *filter = GST_SPECTRUM (object);
404
405   switch (prop_id) {
406     case PROP_POST_MESSAGES:
407       g_value_set_boolean (value, filter->post_messages);
408       break;
409     case PROP_MESSAGE_MAGNITUDE:
410       g_value_set_boolean (value, filter->message_magnitude);
411       break;
412     case PROP_MESSAGE_PHASE:
413       g_value_set_boolean (value, filter->message_phase);
414       break;
415     case PROP_INTERVAL:
416       g_value_set_uint64 (value, filter->interval);
417       break;
418     case PROP_BANDS:
419       g_value_set_uint (value, filter->bands);
420       break;
421     case PROP_THRESHOLD:
422       g_value_set_int (value, filter->threshold);
423       break;
424     case PROP_MULTI_CHANNEL:
425       g_value_set_boolean (value, filter->multi_channel);
426       break;
427     default:
428       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
429       break;
430   }
431 }
432
433 static gboolean
434 gst_spectrum_start (GstBaseTransform * trans)
435 {
436   GstSpectrum *spectrum = GST_SPECTRUM (trans);
437
438   gst_spectrum_reset_state (spectrum);
439
440   return TRUE;
441 }
442
443 static gboolean
444 gst_spectrum_stop (GstBaseTransform * trans)
445 {
446   GstSpectrum *spectrum = GST_SPECTRUM (trans);
447
448   gst_spectrum_reset_state (spectrum);
449
450   return TRUE;
451 }
452
453 /* mixing data readers */
454
455 static void
456 input_data_mixed_float (const guint8 * _in, gfloat * out, guint len,
457     guint channels, gfloat max_value, guint op, guint nfft)
458 {
459   guint i, j, ip = 0;
460   gfloat v;
461   gfloat *in = (gfloat *) _in;
462
463   for (j = 0; j < len; j++) {
464     v = in[ip++];
465     for (i = 1; i < channels; i++)
466       v += in[ip++];
467     out[op] = v / channels;
468     op = (op + 1) % nfft;
469   }
470 }
471
472 static void
473 input_data_mixed_double (const guint8 * _in, gfloat * out, guint len,
474     guint channels, gfloat max_value, guint op, guint nfft)
475 {
476   guint i, j, ip = 0;
477   gfloat v;
478   gdouble *in = (gdouble *) _in;
479
480   for (j = 0; j < len; j++) {
481     v = in[ip++];
482     for (i = 1; i < channels; i++)
483       v += in[ip++];
484     out[op] = v / channels;
485     op = (op + 1) % nfft;
486   }
487 }
488
489 static void
490 input_data_mixed_int32_max (const guint8 * _in, gfloat * out, guint len,
491     guint channels, gfloat max_value, guint op, guint nfft)
492 {
493   guint i, j, ip = 0;
494   gint32 *in = (gint32 *) _in;
495   gfloat v;
496
497   for (j = 0; j < len; j++) {
498     v = in[ip++] / max_value;
499     for (i = 1; i < channels; i++)
500       v += in[ip++] / max_value;
501     out[op] = v / channels;
502     op = (op + 1) % nfft;
503   }
504 }
505
506 static void
507 input_data_mixed_int24_max (const guint8 * _in, gfloat * out, guint len,
508     guint channels, gfloat max_value, guint op, guint nfft)
509 {
510   guint i, j;
511   gfloat v = 0.0;
512
513   for (j = 0; j < len; j++) {
514     for (i = 0; i < channels; i++) {
515 #if G_BYTE_ORDER == G_BIG_ENDIAN
516       gint32 value = GST_READ_UINT24_BE (_in);
517 #else
518       gint32 value = GST_READ_UINT24_LE (_in);
519 #endif
520       if (value & 0x00800000)
521         value |= 0xff000000;
522       v += value / max_value;
523       _in += 3;
524     }
525     out[op] = v / channels;
526     op = (op + 1) % nfft;
527   }
528 }
529
530 static void
531 input_data_mixed_int16_max (const guint8 * _in, gfloat * out, guint len,
532     guint channels, gfloat max_value, guint op, guint nfft)
533 {
534   guint i, j, ip = 0;
535   gint16 *in = (gint16 *) _in;
536   gfloat v;
537
538   for (j = 0; j < len; j++) {
539     v = in[ip++] / max_value;
540     for (i = 1; i < channels; i++)
541       v += in[ip++] / max_value;
542     out[op] = v / channels;
543     op = (op + 1) % nfft;
544   }
545 }
546
547 /* non mixing data readers */
548
549 static void
550 input_data_float (const guint8 * _in, gfloat * out, guint len, guint channels,
551     gfloat max_value, guint op, guint nfft)
552 {
553   guint j, ip;
554   gfloat *in = (gfloat *) _in;
555
556   for (j = 0, ip = 0; j < len; j++, ip += channels) {
557     out[op] = in[ip];
558     op = (op + 1) % nfft;
559   }
560 }
561
562 static void
563 input_data_double (const guint8 * _in, gfloat * out, guint len, guint channels,
564     gfloat max_value, guint op, guint nfft)
565 {
566   guint j, ip;
567   gdouble *in = (gdouble *) _in;
568
569   for (j = 0, ip = 0; j < len; j++, ip += channels) {
570     out[op] = in[ip];
571     op = (op + 1) % nfft;
572   }
573 }
574
575 static void
576 input_data_int32_max (const guint8 * _in, gfloat * out, guint len,
577     guint channels, gfloat max_value, guint op, guint nfft)
578 {
579   guint j, ip;
580   gint32 *in = (gint32 *) _in;
581
582   for (j = 0, ip = 0; j < len; j++, ip += channels) {
583     out[op] = in[ip] / max_value;
584     op = (op + 1) % nfft;
585   }
586 }
587
588 static void
589 input_data_int24_max (const guint8 * _in, gfloat * out, guint len,
590     guint channels, gfloat max_value, guint op, guint nfft)
591 {
592   guint j;
593
594   for (j = 0; j < len; j++) {
595 #if G_BYTE_ORDER == G_BIG_ENDIAN
596     gint32 v = GST_READ_UINT24_BE (_in);
597 #else
598     gint32 v = GST_READ_UINT24_LE (_in);
599 #endif
600     if (v & 0x00800000)
601       v |= 0xff000000;
602     _in += 3 * channels;
603     out[op] = v / max_value;
604     op = (op + 1) % nfft;
605   }
606 }
607
608 static void
609 input_data_int16_max (const guint8 * _in, gfloat * out, guint len,
610     guint channels, gfloat max_value, guint op, guint nfft)
611 {
612   guint j, ip;
613   gint16 *in = (gint16 *) _in;
614
615   for (j = 0, ip = 0; j < len; j++, ip += channels) {
616     out[op] = in[ip] / max_value;
617     op = (op + 1) % nfft;
618   }
619 }
620
621 static gboolean
622 gst_spectrum_setup (GstAudioFilter * base, const GstAudioInfo * info)
623 {
624   GstSpectrum *spectrum = GST_SPECTRUM (base);
625   gboolean multi_channel = spectrum->multi_channel;
626   GstSpectrumInputData input_data = NULL;
627
628   g_mutex_lock (&spectrum->lock);
629   switch (GST_AUDIO_INFO_FORMAT (info)) {
630     case GST_AUDIO_FORMAT_S16:
631       input_data =
632           multi_channel ? input_data_int16_max : input_data_mixed_int16_max;
633       break;
634     case GST_AUDIO_FORMAT_S24:
635       input_data =
636           multi_channel ? input_data_int24_max : input_data_mixed_int24_max;
637       break;
638     case GST_AUDIO_FORMAT_S32:
639       input_data =
640           multi_channel ? input_data_int32_max : input_data_mixed_int32_max;
641       break;
642     case GST_AUDIO_FORMAT_F32:
643       input_data = multi_channel ? input_data_float : input_data_mixed_float;
644       break;
645     case GST_AUDIO_FORMAT_F64:
646       input_data = multi_channel ? input_data_double : input_data_mixed_double;
647       break;
648     default:
649       g_assert_not_reached ();
650       break;
651   }
652   spectrum->input_data = input_data;
653
654   gst_spectrum_reset_state (spectrum);
655   g_mutex_unlock (&spectrum->lock);
656
657   return TRUE;
658 }
659
660 static GValue *
661 gst_spectrum_message_add_container (GstStructure * s, GType type,
662     const gchar * name)
663 {
664   GValue v = { 0, };
665
666   g_value_init (&v, type);
667   /* will copy-by-value */
668   gst_structure_set_value (s, name, &v);
669   g_value_unset (&v);
670   return (GValue *) gst_structure_get_value (s, name);
671 }
672
673 static void
674 gst_spectrum_message_add_list (GValue * cv, gfloat * data, guint num_values)
675 {
676   GValue v = { 0, };
677   guint i;
678
679   g_value_init (&v, G_TYPE_FLOAT);
680   for (i = 0; i < num_values; i++) {
681     g_value_set_float (&v, data[i]);
682     gst_value_list_append_value (cv, &v);       /* copies by value */
683   }
684   g_value_unset (&v);
685 }
686
687 static void
688 gst_spectrum_message_add_array (GValue * cv, gfloat * data, guint num_values)
689 {
690   GValue v = { 0, };
691   GValue a = { 0, };
692   guint i;
693
694   g_value_init (&a, GST_TYPE_ARRAY);
695
696   g_value_init (&v, G_TYPE_FLOAT);
697   for (i = 0; i < num_values; i++) {
698     g_value_set_float (&v, data[i]);
699     gst_value_array_append_value (&a, &v);      /* copies by value */
700   }
701   g_value_unset (&v);
702
703   gst_value_array_append_value (cv, &a);        /* copies by value */
704   g_value_unset (&a);
705 }
706
707 static GstMessage *
708 gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime timestamp,
709     GstClockTime duration)
710 {
711   GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (spectrum);
712   GstSpectrumChannel *cd;
713   GstStructure *s;
714   GValue *mcv = NULL, *pcv = NULL;
715   GstClockTime endtime, running_time, stream_time;
716
717   GST_DEBUG_OBJECT (spectrum, "preparing message, bands =%d ", spectrum->bands);
718
719   running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
720       timestamp);
721   stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
722       timestamp);
723   /* endtime is for backwards compatibility */
724   endtime = stream_time + duration;
725
726   s = gst_structure_new ("spectrum",
727       "endtime", GST_TYPE_CLOCK_TIME, endtime,
728       "timestamp", G_TYPE_UINT64, timestamp,
729       "stream-time", G_TYPE_UINT64, stream_time,
730       "running-time", G_TYPE_UINT64, running_time,
731       "duration", G_TYPE_UINT64, duration, NULL);
732
733   if (!spectrum->multi_channel) {
734     cd = &spectrum->channel_data[0];
735
736     if (spectrum->message_magnitude) {
737       /* FIXME 0.11: this should be an array, not a list */
738       mcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "magnitude");
739       gst_spectrum_message_add_list (mcv, cd->spect_magnitude, spectrum->bands);
740     }
741     if (spectrum->message_phase) {
742       /* FIXME 0.11: this should be an array, not a list */
743       pcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "phase");
744       gst_spectrum_message_add_list (pcv, cd->spect_phase, spectrum->bands);
745     }
746   } else {
747     guint c;
748     guint channels = GST_AUDIO_FILTER_CHANNELS (spectrum);
749
750     if (spectrum->message_magnitude) {
751       mcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "magnitude");
752     }
753     if (spectrum->message_phase) {
754       pcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "phase");
755     }
756
757     for (c = 0; c < channels; c++) {
758       cd = &spectrum->channel_data[c];
759
760       if (spectrum->message_magnitude) {
761         gst_spectrum_message_add_array (mcv, cd->spect_magnitude,
762             spectrum->bands);
763       }
764       if (spectrum->message_phase) {
765         gst_spectrum_message_add_array (pcv, cd->spect_magnitude,
766             spectrum->bands);
767       }
768     }
769   }
770   return gst_message_new_element (GST_OBJECT (spectrum), s);
771 }
772
773 static void
774 gst_spectrum_run_fft (GstSpectrum * spectrum, GstSpectrumChannel * cd,
775     guint input_pos)
776 {
777   guint i;
778   guint bands = spectrum->bands;
779   guint nfft = 2 * bands - 2;
780   gint threshold = spectrum->threshold;
781   gfloat *input = cd->input;
782   gfloat *input_tmp = cd->input_tmp;
783   gfloat *spect_magnitude = cd->spect_magnitude;
784   gfloat *spect_phase = cd->spect_phase;
785   GstFFTF32Complex *freqdata = cd->freqdata;
786   GstFFTF32 *fft_ctx = cd->fft_ctx;
787
788   for (i = 0; i < nfft; i++)
789     input_tmp[i] = input[(input_pos + i) % nfft];
790
791   gst_fft_f32_window (fft_ctx, input_tmp, GST_FFT_WINDOW_HAMMING);
792
793   gst_fft_f32_fft (fft_ctx, input_tmp, freqdata);
794
795   if (spectrum->message_magnitude) {
796     gdouble val;
797     /* Calculate magnitude in db */
798     for (i = 0; i < bands; i++) {
799       val = freqdata[i].r * freqdata[i].r;
800       val += freqdata[i].i * freqdata[i].i;
801       val /= nfft * nfft;
802       val = 10.0 * log10 (val);
803       if (val < threshold)
804         val = threshold;
805       spect_magnitude[i] += val;
806     }
807   }
808
809   if (spectrum->message_phase) {
810     /* Calculate phase */
811     for (i = 0; i < bands; i++)
812       spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r);
813   }
814 }
815
816 static void
817 gst_spectrum_prepare_message_data (GstSpectrum * spectrum,
818     GstSpectrumChannel * cd)
819 {
820   guint i;
821   guint bands = spectrum->bands;
822   guint num_fft = spectrum->num_fft;
823
824   /* Calculate average */
825   if (spectrum->message_magnitude) {
826     gfloat *spect_magnitude = cd->spect_magnitude;
827     for (i = 0; i < bands; i++)
828       spect_magnitude[i] /= num_fft;
829   }
830   if (spectrum->message_phase) {
831     gfloat *spect_phase = cd->spect_phase;
832     for (i = 0; i < bands; i++)
833       spect_phase[i] /= num_fft;
834   }
835 }
836
837 static void
838 gst_spectrum_reset_message_data (GstSpectrum * spectrum,
839     GstSpectrumChannel * cd)
840 {
841   guint bands = spectrum->bands;
842   gfloat *spect_magnitude = cd->spect_magnitude;
843   gfloat *spect_phase = cd->spect_phase;
844
845   /* reset spectrum accumulators */
846   memset (spect_magnitude, 0, bands * sizeof (gfloat));
847   memset (spect_phase, 0, bands * sizeof (gfloat));
848 }
849
850 static GstFlowReturn
851 gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
852 {
853   GstSpectrum *spectrum = GST_SPECTRUM (trans);
854   guint rate = GST_AUDIO_FILTER_RATE (spectrum);
855   guint channels = GST_AUDIO_FILTER_CHANNELS (spectrum);
856   guint bps = GST_AUDIO_FILTER_BPS (spectrum);
857   guint bpf = GST_AUDIO_FILTER_BPF (spectrum);
858   guint output_channels = spectrum->multi_channel ? channels : 1;
859   guint c;
860   gfloat max_value = (1UL << ((bps << 3) - 1)) - 1;
861   guint bands = spectrum->bands;
862   guint nfft = 2 * bands - 2;
863   guint input_pos;
864   gfloat *input;
865   GstMapInfo map;
866   const guint8 *data;
867   gsize size;
868   guint fft_todo, msg_todo, block_size;
869   gboolean have_full_interval;
870   GstSpectrumChannel *cd;
871   GstSpectrumInputData input_data;
872
873   g_mutex_lock (&spectrum->lock);
874   gst_buffer_map (buffer, &map, GST_MAP_READ);
875   data = map.data;
876   size = map.size;
877
878   GST_LOG_OBJECT (spectrum, "input size: %" G_GSIZE_FORMAT " bytes", size);
879
880   if (GST_BUFFER_IS_DISCONT (buffer)) {
881     GST_DEBUG_OBJECT (spectrum, "Discontinuity detected -- flushing");
882     gst_spectrum_flush (spectrum);
883   }
884
885   /* If we don't have a FFT context yet (or it was reset due to parameter
886    * changes) get one and allocate memory for everything
887    */
888   if (spectrum->channel_data == NULL) {
889     GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands);
890
891     gst_spectrum_alloc_channel_data (spectrum);
892
893     /* number of sample frames we process before posting a message
894      * interval is in ns */
895     spectrum->frames_per_interval =
896         gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND);
897     spectrum->frames_todo = spectrum->frames_per_interval;
898     /* rounding error for frames_per_interval in ns,
899      * aggregated it in accumulated_error */
900     spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
901     if (spectrum->frames_per_interval == 0)
902       spectrum->frames_per_interval = 1;
903
904     GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
905         G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
906         GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval,
907         GST_TIME_ARGS (spectrum->error_per_interval));
908
909     spectrum->input_pos = 0;
910
911     gst_spectrum_flush (spectrum);
912   }
913
914   if (spectrum->num_frames == 0)
915     spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);
916
917   input_pos = spectrum->input_pos;
918   input_data = spectrum->input_data;
919
920   while (size >= bpf) {
921     /* run input_data for a chunk of data */
922     fft_todo = nfft - (spectrum->num_frames % nfft);
923     msg_todo = spectrum->frames_todo - spectrum->num_frames;
924     GST_LOG_OBJECT (spectrum,
925         "message frames todo: %u, fft frames todo: %u, input frames %"
926         G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf));
927     block_size = msg_todo;
928     if (block_size > (size / bpf))
929       block_size = (size / bpf);
930     if (block_size > fft_todo)
931       block_size = fft_todo;
932
933     for (c = 0; c < output_channels; c++) {
934       cd = &spectrum->channel_data[c];
935       input = cd->input;
936       /* Move the current frames into our ringbuffers */
937       input_data (data + c * bps, input, block_size, channels, max_value,
938           input_pos, nfft);
939     }
940     data += block_size * bpf;
941     size -= block_size * bpf;
942     input_pos = (input_pos + block_size) % nfft;
943     spectrum->num_frames += block_size;
944
945     have_full_interval = (spectrum->num_frames == spectrum->frames_todo);
946
947     GST_LOG_OBJECT (spectrum,
948         "size: %" G_GSIZE_FORMAT ", do-fft = %d, do-message = %d", size,
949         (spectrum->num_frames % nfft == 0), have_full_interval);
950
951     /* If we have enough frames for an FFT or we have all frames required for
952      * the interval and we haven't run a FFT, then run an FFT */
953     if ((spectrum->num_frames % nfft == 0) ||
954         (have_full_interval && !spectrum->num_fft)) {
955       for (c = 0; c < output_channels; c++) {
956         cd = &spectrum->channel_data[c];
957         gst_spectrum_run_fft (spectrum, cd, input_pos);
958       }
959       spectrum->num_fft++;
960     }
961
962     /* Do we have the FFTs for one interval? */
963     if (have_full_interval) {
964       GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT
965           " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft,
966           spectrum->num_frames, spectrum->frames_per_interval,
967           GST_TIME_ARGS (spectrum->accumulated_error));
968
969       spectrum->frames_todo = spectrum->frames_per_interval;
970       if (spectrum->accumulated_error >= GST_SECOND) {
971         spectrum->accumulated_error -= GST_SECOND;
972         spectrum->frames_todo++;
973       }
974       spectrum->accumulated_error += spectrum->error_per_interval;
975
976       if (spectrum->post_messages) {
977         GstMessage *m;
978
979         for (c = 0; c < output_channels; c++) {
980           cd = &spectrum->channel_data[c];
981           gst_spectrum_prepare_message_data (spectrum, cd);
982         }
983
984         m = gst_spectrum_message_new (spectrum, spectrum->message_ts,
985             spectrum->interval);
986
987         gst_element_post_message (GST_ELEMENT (spectrum), m);
988       }
989
990       if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts))
991         spectrum->message_ts +=
992             gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);
993
994       for (c = 0; c < output_channels; c++) {
995         cd = &spectrum->channel_data[c];
996         gst_spectrum_reset_message_data (spectrum, cd);
997       }
998       spectrum->num_frames = 0;
999       spectrum->num_fft = 0;
1000     }
1001   }
1002
1003   spectrum->input_pos = input_pos;
1004
1005   gst_buffer_unmap (buffer, &map);
1006   g_mutex_unlock (&spectrum->lock);
1007
1008   g_assert (size == 0);
1009
1010   return GST_FLOW_OK;
1011 }
1012
1013 static gboolean
1014 plugin_init (GstPlugin * plugin)
1015 {
1016   return gst_element_register (plugin, "spectrum", GST_RANK_NONE,
1017       GST_TYPE_SPECTRUM);
1018 }
1019
1020 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1021     GST_VERSION_MINOR,
1022     "spectrum",
1023     "Run an FFT on the audio signal, output spectrum data",
1024     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)