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