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
gst_spectrum_class_init(GstSpectrumClass * klass)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
gst_spectrum_init(GstSpectrum * spectrum)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
gst_spectrum_alloc_channel_data(GstSpectrum * spectrum)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
gst_spectrum_free_channel_data(GstSpectrum * spectrum)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
gst_spectrum_flush(GstSpectrum * spectrum)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
gst_spectrum_reset_state(GstSpectrum * spectrum)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
gst_spectrum_finalize(GObject * object)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
gst_spectrum_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gst_spectrum_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_spectrum_start(GstBaseTransform * trans)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
gst_spectrum_stop(GstBaseTransform * trans)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
input_data_mixed_float(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
input_data_mixed_double(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
input_data_mixed_int32_max(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
input_data_mixed_int24_max(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
input_data_mixed_int16_max(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
input_data_float(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
input_data_double(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
input_data_int32_max(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
input_data_int24_max(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
input_data_int16_max(const guint8 * _in,gfloat * out,guint len,guint channels,gfloat max_value,guint op,guint nfft)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
gst_spectrum_setup(GstAudioFilter * base,const GstAudioInfo * info)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 *
gst_spectrum_message_add_container(GstStructure * s,GType type,const gchar * name)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
gst_spectrum_message_add_list(GValue * cv,gfloat * data,guint num_values)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
gst_spectrum_message_add_array(GValue * cv,gfloat * data,guint num_values)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 *
gst_spectrum_message_new(GstSpectrum * spectrum,GstClockTime timestamp,GstClockTime duration)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_phase, spectrum->bands);
750       }
751     }
752   }
753   return gst_message_new_element (GST_OBJECT (spectrum), s);
754 }
755 
756 static void
gst_spectrum_run_fft(GstSpectrum * spectrum,GstSpectrumChannel * cd,guint input_pos)757 gst_spectrum_run_fft (GstSpectrum * spectrum, GstSpectrumChannel * cd,
758     guint input_pos)
759 {
760   guint i;
761   guint bands = spectrum->bands;
762   guint nfft = 2 * bands - 2;
763   gint threshold = spectrum->threshold;
764   gfloat *input = cd->input;
765   gfloat *input_tmp = cd->input_tmp;
766   gfloat *spect_magnitude = cd->spect_magnitude;
767   gfloat *spect_phase = cd->spect_phase;
768   GstFFTF32Complex *freqdata = cd->freqdata;
769   GstFFTF32 *fft_ctx = cd->fft_ctx;
770 
771   for (i = 0; i < nfft; i++)
772     input_tmp[i] = input[(input_pos + i) % nfft];
773 
774   gst_fft_f32_window (fft_ctx, input_tmp, GST_FFT_WINDOW_HAMMING);
775 
776   gst_fft_f32_fft (fft_ctx, input_tmp, freqdata);
777 
778   if (spectrum->message_magnitude) {
779     gdouble val;
780     /* Calculate magnitude in db */
781     for (i = 0; i < bands; i++) {
782       val = freqdata[i].r * freqdata[i].r;
783       val += freqdata[i].i * freqdata[i].i;
784       val /= nfft * nfft;
785       val = 10.0 * log10 (val);
786       if (val < threshold)
787         val = threshold;
788       spect_magnitude[i] += val;
789     }
790   }
791 
792   if (spectrum->message_phase) {
793     /* Calculate phase */
794     for (i = 0; i < bands; i++)
795       spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r);
796   }
797 }
798 
799 static void
gst_spectrum_prepare_message_data(GstSpectrum * spectrum,GstSpectrumChannel * cd)800 gst_spectrum_prepare_message_data (GstSpectrum * spectrum,
801     GstSpectrumChannel * cd)
802 {
803   guint i;
804   guint bands = spectrum->bands;
805   guint num_fft = spectrum->num_fft;
806 
807   /* Calculate average */
808   if (spectrum->message_magnitude) {
809     gfloat *spect_magnitude = cd->spect_magnitude;
810     for (i = 0; i < bands; i++)
811       spect_magnitude[i] /= num_fft;
812   }
813   if (spectrum->message_phase) {
814     gfloat *spect_phase = cd->spect_phase;
815     for (i = 0; i < bands; i++)
816       spect_phase[i] /= num_fft;
817   }
818 }
819 
820 static void
gst_spectrum_reset_message_data(GstSpectrum * spectrum,GstSpectrumChannel * cd)821 gst_spectrum_reset_message_data (GstSpectrum * spectrum,
822     GstSpectrumChannel * cd)
823 {
824   guint bands = spectrum->bands;
825   gfloat *spect_magnitude = cd->spect_magnitude;
826   gfloat *spect_phase = cd->spect_phase;
827 
828   /* reset spectrum accumulators */
829   memset (spect_magnitude, 0, bands * sizeof (gfloat));
830   memset (spect_phase, 0, bands * sizeof (gfloat));
831 }
832 
833 static GstFlowReturn
gst_spectrum_transform_ip(GstBaseTransform * trans,GstBuffer * buffer)834 gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
835 {
836   GstSpectrum *spectrum = GST_SPECTRUM (trans);
837   guint rate = GST_AUDIO_FILTER_RATE (spectrum);
838   guint channels = GST_AUDIO_FILTER_CHANNELS (spectrum);
839   guint bps = GST_AUDIO_FILTER_BPS (spectrum);
840   guint bpf = GST_AUDIO_FILTER_BPF (spectrum);
841   guint output_channels = spectrum->multi_channel ? channels : 1;
842   guint c;
843   gfloat max_value = (1UL << ((bps << 3) - 1)) - 1;
844   guint bands = spectrum->bands;
845   guint nfft = 2 * bands - 2;
846   guint input_pos;
847   gfloat *input;
848   GstMapInfo map;
849   const guint8 *data;
850   gsize size;
851   guint fft_todo, msg_todo, block_size;
852   gboolean have_full_interval;
853   GstSpectrumChannel *cd;
854   GstSpectrumInputData input_data;
855 
856   g_mutex_lock (&spectrum->lock);
857   gst_buffer_map (buffer, &map, GST_MAP_READ);
858   data = map.data;
859   size = map.size;
860 
861   GST_LOG_OBJECT (spectrum, "input size: %" G_GSIZE_FORMAT " bytes", size);
862 
863   if (GST_BUFFER_IS_DISCONT (buffer)) {
864     GST_DEBUG_OBJECT (spectrum, "Discontinuity detected -- flushing");
865     gst_spectrum_flush (spectrum);
866   }
867 
868   /* If we don't have a FFT context yet (or it was reset due to parameter
869    * changes) get one and allocate memory for everything
870    */
871   if (spectrum->channel_data == NULL) {
872     GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands);
873 
874     gst_spectrum_alloc_channel_data (spectrum);
875 
876     /* number of sample frames we process before posting a message
877      * interval is in ns */
878     spectrum->frames_per_interval =
879         gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND);
880     spectrum->frames_todo = spectrum->frames_per_interval;
881     /* rounding error for frames_per_interval in ns,
882      * aggregated it in accumulated_error */
883     spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
884     if (spectrum->frames_per_interval == 0)
885       spectrum->frames_per_interval = 1;
886 
887     GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
888         G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
889         GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval,
890         GST_TIME_ARGS (spectrum->error_per_interval));
891 
892     spectrum->input_pos = 0;
893 
894     gst_spectrum_flush (spectrum);
895   }
896 
897   if (spectrum->num_frames == 0)
898     spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);
899 
900   input_pos = spectrum->input_pos;
901   input_data = spectrum->input_data;
902 
903   while (size >= bpf) {
904     /* run input_data for a chunk of data */
905     fft_todo = nfft - (spectrum->num_frames % nfft);
906     msg_todo = spectrum->frames_todo - spectrum->num_frames;
907     GST_LOG_OBJECT (spectrum,
908         "message frames todo: %u, fft frames todo: %u, input frames %"
909         G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf));
910     block_size = msg_todo;
911     if (block_size > (size / bpf))
912       block_size = (size / bpf);
913     if (block_size > fft_todo)
914       block_size = fft_todo;
915 
916     for (c = 0; c < output_channels; c++) {
917       cd = &spectrum->channel_data[c];
918       input = cd->input;
919       /* Move the current frames into our ringbuffers */
920       input_data (data + c * bps, input, block_size, channels, max_value,
921           input_pos, nfft);
922     }
923     data += block_size * bpf;
924     size -= block_size * bpf;
925     input_pos = (input_pos + block_size) % nfft;
926     spectrum->num_frames += block_size;
927 
928     have_full_interval = (spectrum->num_frames == spectrum->frames_todo);
929 
930     GST_LOG_OBJECT (spectrum,
931         "size: %" G_GSIZE_FORMAT ", do-fft = %d, do-message = %d", size,
932         (spectrum->num_frames % nfft == 0), have_full_interval);
933 
934     /* If we have enough frames for an FFT or we have all frames required for
935      * the interval and we haven't run a FFT, then run an FFT */
936     if ((spectrum->num_frames % nfft == 0) ||
937         (have_full_interval && !spectrum->num_fft)) {
938       for (c = 0; c < output_channels; c++) {
939         cd = &spectrum->channel_data[c];
940         gst_spectrum_run_fft (spectrum, cd, input_pos);
941       }
942       spectrum->num_fft++;
943     }
944 
945     /* Do we have the FFTs for one interval? */
946     if (have_full_interval) {
947       GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT
948           " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft,
949           spectrum->num_frames, spectrum->frames_per_interval,
950           GST_TIME_ARGS (spectrum->accumulated_error));
951 
952       spectrum->frames_todo = spectrum->frames_per_interval;
953       if (spectrum->accumulated_error >= GST_SECOND) {
954         spectrum->accumulated_error -= GST_SECOND;
955         spectrum->frames_todo++;
956       }
957       spectrum->accumulated_error += spectrum->error_per_interval;
958 
959       if (spectrum->post_messages) {
960         GstMessage *m;
961 
962         for (c = 0; c < output_channels; c++) {
963           cd = &spectrum->channel_data[c];
964           gst_spectrum_prepare_message_data (spectrum, cd);
965         }
966 
967         m = gst_spectrum_message_new (spectrum, spectrum->message_ts,
968             spectrum->interval);
969 
970         gst_element_post_message (GST_ELEMENT (spectrum), m);
971       }
972 
973       if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts))
974         spectrum->message_ts +=
975             gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);
976 
977       for (c = 0; c < output_channels; c++) {
978         cd = &spectrum->channel_data[c];
979         gst_spectrum_reset_message_data (spectrum, cd);
980       }
981       spectrum->num_frames = 0;
982       spectrum->num_fft = 0;
983     }
984   }
985 
986   spectrum->input_pos = input_pos;
987 
988   gst_buffer_unmap (buffer, &map);
989   g_mutex_unlock (&spectrum->lock);
990 
991   g_assert (size == 0);
992 
993   return GST_FLOW_OK;
994 }
995 
996 static gboolean
plugin_init(GstPlugin * plugin)997 plugin_init (GstPlugin * plugin)
998 {
999   return gst_element_register (plugin, "spectrum", GST_RANK_NONE,
1000       GST_TYPE_SPECTRUM);
1001 }
1002 
1003 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1004     GST_VERSION_MINOR,
1005     spectrum,
1006     "Run an FFT on the audio signal, output spectrum data",
1007     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
1008