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>"spectrum"</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>"timestamp"</classname>:
36 * the timestamp of the buffer that triggered the message.
37 * </para>
38 * </listitem>
39 * <listitem>
40 * <para>
41 * #GstClockTime
42 * <classname>"stream-time"</classname>:
43 * the stream time of the buffer.
44 * </para>
45 * </listitem>
46 * <listitem>
47 * <para>
48 * #GstClockTime
49 * <classname>"running-time"</classname>:
50 * the running_time of the buffer.
51 * </para>
52 * </listitem>
53 * <listitem>
54 * <para>
55 * #GstClockTime
56 * <classname>"duration"</classname>:
57 * the duration of the buffer.
58 * </para>
59 * </listitem>
60 * <listitem>
61 * <para>
62 * #GstClockTime
63 * <classname>"endtime"</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>"magnitude"</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>"phase"</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