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>
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.
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.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
22 * SECTION:element-spectrum
24 * The Spectrum element analyzes the frequency spectrum of an audio signal.
25 * If the #GstSpectrum:post-messages property is #TRUE, it sends analysis results
26 * as application messages named
27 * <classname>"spectrum"</classname> after each interval of time given
28 * by the #GstSpectrum:interval property.
30 * The message's structure contains some combination of these fields:
35 * <classname>"timestamp"</classname>:
36 * the timestamp of the buffer that triggered the message.
42 * <classname>"stream-time"</classname>:
43 * the stream time of the buffer.
49 * <classname>"running-time"</classname>:
50 * the running_time of the buffer.
56 * <classname>"duration"</classname>:
57 * the duration of the buffer.
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)
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.
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.
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.
92 * <title>Example application</title>
94 * <xi:include xmlns:xi="http://www.w3.org/2003/XInclude" parse="text" href="../../../../tests/examples/spectrum/spectrum-example.c" />
98 * Last reviewed on 2011-03-10 (0.10.29)
107 #include "gstspectrum.h"
109 GST_DEBUG_CATEGORY_STATIC (gst_spectrum_debug);
110 #define GST_CAT_DEFAULT gst_spectrum_debug
112 /* elementfactory information */
113 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
114 # define FORMATS "{ S16LE, S24LE, S32LE, F32LE, F64LE }"
116 # define FORMATS "{ S16BE, S24BE, S32BE, F32BE, F64BE }"
119 #define ALLOWED_CAPS \
120 GST_AUDIO_CAPS_MAKE (FORMATS) ", " \
121 "layout = (string) interleaved"
123 /* Spectrum properties */
124 #define DEFAULT_POST_MESSAGES TRUE
125 #define DEFAULT_MESSAGE_MAGNITUDE TRUE
126 #define DEFAULT_MESSAGE_PHASE FALSE
127 #define DEFAULT_INTERVAL (GST_SECOND / 10)
128 #define DEFAULT_BANDS 128
129 #define DEFAULT_THRESHOLD -60
130 #define DEFAULT_MULTI_CHANNEL FALSE
136 PROP_MESSAGE_MAGNITUDE,
144 #define gst_spectrum_parent_class parent_class
145 G_DEFINE_TYPE (GstSpectrum, gst_spectrum, GST_TYPE_AUDIO_FILTER);
147 static void gst_spectrum_finalize (GObject * object);
148 static void gst_spectrum_set_property (GObject * object, guint prop_id,
149 const GValue * value, GParamSpec * pspec);
150 static void gst_spectrum_get_property (GObject * object, guint prop_id,
151 GValue * value, GParamSpec * pspec);
152 static gboolean gst_spectrum_start (GstBaseTransform * trans);
153 static gboolean gst_spectrum_stop (GstBaseTransform * trans);
154 static GstFlowReturn gst_spectrum_transform_ip (GstBaseTransform * trans,
156 static gboolean gst_spectrum_setup (GstAudioFilter * base,
157 const GstAudioInfo * info);
160 gst_spectrum_class_init (GstSpectrumClass * klass)
162 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
163 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
164 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
165 GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (klass);
168 gobject_class->set_property = gst_spectrum_set_property;
169 gobject_class->get_property = gst_spectrum_get_property;
170 gobject_class->finalize = gst_spectrum_finalize;
172 trans_class->start = GST_DEBUG_FUNCPTR (gst_spectrum_start);
173 trans_class->stop = GST_DEBUG_FUNCPTR (gst_spectrum_stop);
174 trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_spectrum_transform_ip);
175 trans_class->passthrough_on_same_caps = TRUE;
177 filter_class->setup = GST_DEBUG_FUNCPTR (gst_spectrum_setup);
180 * GstSpectrum:post-messages
182 * Post messages on the bus with spectrum information.
186 g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
187 g_param_spec_boolean ("post-messages", "Post Messages",
188 "Whether to post a 'spectrum' element message on the bus for each "
189 "passed interval", DEFAULT_POST_MESSAGES,
190 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
192 g_object_class_install_property (gobject_class, PROP_MESSAGE_MAGNITUDE,
193 g_param_spec_boolean ("message-magnitude", "Magnitude",
194 "Whether to add a 'magnitude' field to the structure of any "
195 "'spectrum' element messages posted on the bus",
196 DEFAULT_MESSAGE_MAGNITUDE,
197 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
199 g_object_class_install_property (gobject_class, PROP_MESSAGE_PHASE,
200 g_param_spec_boolean ("message-phase", "Phase",
201 "Whether to add a 'phase' field to the structure of any "
202 "'spectrum' element messages posted on the bus",
203 DEFAULT_MESSAGE_PHASE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
205 g_object_class_install_property (gobject_class, PROP_INTERVAL,
206 g_param_spec_uint64 ("interval", "Interval",
207 "Interval of time between message posts (in nanoseconds)",
208 1, G_MAXUINT64, DEFAULT_INTERVAL,
209 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
211 g_object_class_install_property (gobject_class, PROP_BANDS,
212 g_param_spec_uint ("bands", "Bands", "Number of frequency bands",
213 0, G_MAXUINT, DEFAULT_BANDS,
214 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
216 g_object_class_install_property (gobject_class, PROP_THRESHOLD,
217 g_param_spec_int ("threshold", "Threshold",
218 "dB threshold for result. All lower values will be set to this",
219 G_MININT, 0, DEFAULT_THRESHOLD,
220 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
223 * GstSpectrum:multi-channel
225 * Send separate results for each channel
229 g_object_class_install_property (gobject_class, PROP_MULTI_CHANNEL,
230 g_param_spec_boolean ("multi-channel", "Multichannel results",
231 "Send separate results for each channel",
232 DEFAULT_MULTI_CHANNEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
234 GST_DEBUG_CATEGORY_INIT (gst_spectrum_debug, "spectrum", 0,
235 "audio spectrum analyser element");
237 gst_element_class_set_details_simple (element_class, "Spectrum analyzer",
238 "Filter/Analyzer/Audio",
239 "Run an FFT on the audio signal, output spectrum data",
240 "Erik Walthinsen <omega@cse.ogi.edu>, "
241 "Stefan Kost <ensonic@users.sf.net>, "
242 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
244 caps = gst_caps_from_string (ALLOWED_CAPS);
245 gst_audio_filter_class_add_pad_templates (filter_class, caps);
246 gst_caps_unref (caps);
250 gst_spectrum_init (GstSpectrum * spectrum)
252 spectrum->post_messages = DEFAULT_POST_MESSAGES;
253 spectrum->message_magnitude = DEFAULT_MESSAGE_MAGNITUDE;
254 spectrum->message_phase = DEFAULT_MESSAGE_PHASE;
255 spectrum->interval = DEFAULT_INTERVAL;
256 spectrum->bands = DEFAULT_BANDS;
257 spectrum->threshold = DEFAULT_THRESHOLD;
259 g_mutex_init (&spectrum->lock);
263 gst_spectrum_alloc_channel_data (GstSpectrum * spectrum)
266 GstSpectrumChannel *cd;
267 guint bands = spectrum->bands;
268 guint nfft = 2 * bands - 2;
270 g_assert (spectrum->channel_data == NULL);
272 spectrum->num_channels = (spectrum->multi_channel) ?
273 GST_AUDIO_FILTER_CHANNELS (spectrum) : 1;
275 GST_DEBUG_OBJECT (spectrum, "allocating data for %d channels",
276 spectrum->num_channels);
278 spectrum->channel_data = g_new (GstSpectrumChannel, spectrum->num_channels);
279 for (i = 0; i < spectrum->num_channels; i++) {
280 cd = &spectrum->channel_data[i];
281 cd->fft_ctx = gst_fft_f32_new (nfft, FALSE);
282 cd->input = g_new0 (gfloat, nfft);
283 cd->input_tmp = g_new0 (gfloat, nfft);
284 cd->freqdata = g_new0 (GstFFTF32Complex, bands);
285 cd->spect_magnitude = g_new0 (gfloat, bands);
286 cd->spect_phase = g_new0 (gfloat, bands);
291 gst_spectrum_free_channel_data (GstSpectrum * spectrum)
293 if (spectrum->channel_data) {
295 GstSpectrumChannel *cd;
297 GST_DEBUG_OBJECT (spectrum, "freeing data for %d channels",
298 spectrum->num_channels);
300 for (i = 0; i < spectrum->num_channels; i++) {
301 cd = &spectrum->channel_data[i];
303 gst_fft_f32_free (cd->fft_ctx);
305 g_free (cd->input_tmp);
306 g_free (cd->freqdata);
307 g_free (cd->spect_magnitude);
308 g_free (cd->spect_phase);
310 g_free (spectrum->channel_data);
311 spectrum->channel_data = NULL;
316 gst_spectrum_flush (GstSpectrum * spectrum)
318 spectrum->num_frames = 0;
319 spectrum->num_fft = 0;
321 spectrum->accumulated_error = 0;
325 gst_spectrum_reset_state (GstSpectrum * spectrum)
327 GST_DEBUG_OBJECT (spectrum, "resetting state");
329 gst_spectrum_free_channel_data (spectrum);
330 gst_spectrum_flush (spectrum);
334 gst_spectrum_finalize (GObject * object)
336 GstSpectrum *spectrum = GST_SPECTRUM (object);
338 gst_spectrum_reset_state (spectrum);
339 g_mutex_clear (&spectrum->lock);
341 G_OBJECT_CLASS (parent_class)->finalize (object);
345 gst_spectrum_set_property (GObject * object, guint prop_id,
346 const GValue * value, GParamSpec * pspec)
348 GstSpectrum *filter = GST_SPECTRUM (object);
351 case PROP_POST_MESSAGES:
352 filter->post_messages = g_value_get_boolean (value);
354 case PROP_MESSAGE_MAGNITUDE:
355 filter->message_magnitude = g_value_get_boolean (value);
357 case PROP_MESSAGE_PHASE:
358 filter->message_phase = g_value_get_boolean (value);
361 guint64 interval = g_value_get_uint64 (value);
362 g_mutex_lock (&filter->lock);
363 if (filter->interval != interval) {
364 filter->interval = interval;
365 gst_spectrum_reset_state (filter);
367 g_mutex_unlock (&filter->lock);
371 guint bands = g_value_get_uint (value);
372 g_mutex_lock (&filter->lock);
373 if (filter->bands != bands) {
374 filter->bands = bands;
375 gst_spectrum_reset_state (filter);
377 g_mutex_unlock (&filter->lock);
381 filter->threshold = g_value_get_int (value);
383 case PROP_MULTI_CHANNEL:{
384 gboolean multi_channel = g_value_get_boolean (value);
385 g_mutex_lock (&filter->lock);
386 if (filter->multi_channel != multi_channel) {
387 filter->multi_channel = multi_channel;
388 gst_spectrum_reset_state (filter);
390 g_mutex_unlock (&filter->lock);
394 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
400 gst_spectrum_get_property (GObject * object, guint prop_id,
401 GValue * value, GParamSpec * pspec)
403 GstSpectrum *filter = GST_SPECTRUM (object);
406 case PROP_POST_MESSAGES:
407 g_value_set_boolean (value, filter->post_messages);
409 case PROP_MESSAGE_MAGNITUDE:
410 g_value_set_boolean (value, filter->message_magnitude);
412 case PROP_MESSAGE_PHASE:
413 g_value_set_boolean (value, filter->message_phase);
416 g_value_set_uint64 (value, filter->interval);
419 g_value_set_uint (value, filter->bands);
422 g_value_set_int (value, filter->threshold);
424 case PROP_MULTI_CHANNEL:
425 g_value_set_boolean (value, filter->multi_channel);
428 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
434 gst_spectrum_start (GstBaseTransform * trans)
436 GstSpectrum *spectrum = GST_SPECTRUM (trans);
438 gst_spectrum_reset_state (spectrum);
444 gst_spectrum_stop (GstBaseTransform * trans)
446 GstSpectrum *spectrum = GST_SPECTRUM (trans);
448 gst_spectrum_reset_state (spectrum);
453 /* mixing data readers */
456 input_data_mixed_float (const guint8 * _in, gfloat * out, guint len,
457 guint channels, gfloat max_value, guint op, guint nfft)
461 gfloat *in = (gfloat *) _in;
463 for (j = 0; j < len; j++) {
465 for (i = 1; i < channels; i++)
467 out[op] = v / channels;
468 op = (op + 1) % nfft;
473 input_data_mixed_double (const guint8 * _in, gfloat * out, guint len,
474 guint channels, gfloat max_value, guint op, guint nfft)
478 gdouble *in = (gdouble *) _in;
480 for (j = 0; j < len; j++) {
482 for (i = 1; i < channels; i++)
484 out[op] = v / channels;
485 op = (op + 1) % nfft;
490 input_data_mixed_int32_max (const guint8 * _in, gfloat * out, guint len,
491 guint channels, gfloat max_value, guint op, guint nfft)
494 gint32 *in = (gint32 *) _in;
497 for (j = 0; j < len; j++) {
498 v = in[ip++] / max_value;
499 for (i = 1; i < channels; i++)
500 v += in[ip++] / max_value;
501 out[op] = v / channels;
502 op = (op + 1) % nfft;
507 input_data_mixed_int24_max (const guint8 * _in, gfloat * out, guint len,
508 guint channels, gfloat max_value, guint op, guint nfft)
513 for (j = 0; j < len; j++) {
514 for (i = 0; i < channels; i++) {
515 #if G_BYTE_ORDER == G_BIG_ENDIAN
516 gint32 value = GST_READ_UINT24_BE (_in);
518 gint32 value = GST_READ_UINT24_LE (_in);
520 if (value & 0x00800000)
522 v += value / max_value;
525 out[op] = v / channels;
526 op = (op + 1) % nfft;
531 input_data_mixed_int16_max (const guint8 * _in, gfloat * out, guint len,
532 guint channels, gfloat max_value, guint op, guint nfft)
535 gint16 *in = (gint16 *) _in;
538 for (j = 0; j < len; j++) {
539 v = in[ip++] / max_value;
540 for (i = 1; i < channels; i++)
541 v += in[ip++] / max_value;
542 out[op] = v / channels;
543 op = (op + 1) % nfft;
547 /* non mixing data readers */
550 input_data_float (const guint8 * _in, gfloat * out, guint len, guint channels,
551 gfloat max_value, guint op, guint nfft)
554 gfloat *in = (gfloat *) _in;
556 for (j = 0, ip = 0; j < len; j++, ip += channels) {
558 op = (op + 1) % nfft;
563 input_data_double (const guint8 * _in, gfloat * out, guint len, guint channels,
564 gfloat max_value, guint op, guint nfft)
567 gdouble *in = (gdouble *) _in;
569 for (j = 0, ip = 0; j < len; j++, ip += channels) {
571 op = (op + 1) % nfft;
576 input_data_int32_max (const guint8 * _in, gfloat * out, guint len,
577 guint channels, gfloat max_value, guint op, guint nfft)
580 gint32 *in = (gint32 *) _in;
582 for (j = 0, ip = 0; j < len; j++, ip += channels) {
583 out[op] = in[ip] / max_value;
584 op = (op + 1) % nfft;
589 input_data_int24_max (const guint8 * _in, gfloat * out, guint len,
590 guint channels, gfloat max_value, guint op, guint nfft)
594 for (j = 0; j < len; j++) {
595 #if G_BYTE_ORDER == G_BIG_ENDIAN
596 gint32 v = GST_READ_UINT24_BE (_in);
598 gint32 v = GST_READ_UINT24_LE (_in);
603 out[op] = v / max_value;
604 op = (op + 1) % nfft;
609 input_data_int16_max (const guint8 * _in, gfloat * out, guint len,
610 guint channels, gfloat max_value, guint op, guint nfft)
613 gint16 *in = (gint16 *) _in;
615 for (j = 0, ip = 0; j < len; j++, ip += channels) {
616 out[op] = in[ip] / max_value;
617 op = (op + 1) % nfft;
622 gst_spectrum_setup (GstAudioFilter * base, const GstAudioInfo * info)
624 GstSpectrum *spectrum = GST_SPECTRUM (base);
625 gboolean multi_channel = spectrum->multi_channel;
626 GstSpectrumInputData input_data = NULL;
628 g_mutex_lock (&spectrum->lock);
629 switch (GST_AUDIO_INFO_FORMAT (info)) {
630 case GST_AUDIO_FORMAT_S16:
632 multi_channel ? input_data_int16_max : input_data_mixed_int16_max;
634 case GST_AUDIO_FORMAT_S24:
636 multi_channel ? input_data_int24_max : input_data_mixed_int24_max;
638 case GST_AUDIO_FORMAT_S32:
640 multi_channel ? input_data_int32_max : input_data_mixed_int32_max;
642 case GST_AUDIO_FORMAT_F32:
643 input_data = multi_channel ? input_data_float : input_data_mixed_float;
645 case GST_AUDIO_FORMAT_F64:
646 input_data = multi_channel ? input_data_double : input_data_mixed_double;
649 g_assert_not_reached ();
652 spectrum->input_data = input_data;
654 gst_spectrum_reset_state (spectrum);
655 g_mutex_unlock (&spectrum->lock);
661 gst_spectrum_message_add_container (GstStructure * s, GType type,
666 g_value_init (&v, type);
667 /* will copy-by-value */
668 gst_structure_set_value (s, name, &v);
670 return (GValue *) gst_structure_get_value (s, name);
674 gst_spectrum_message_add_list (GValue * cv, gfloat * data, guint num_values)
679 g_value_init (&v, G_TYPE_FLOAT);
680 for (i = 0; i < num_values; i++) {
681 g_value_set_float (&v, data[i]);
682 gst_value_list_append_value (cv, &v); /* copies by value */
688 gst_spectrum_message_add_array (GValue * cv, gfloat * data, guint num_values)
694 g_value_init (&a, GST_TYPE_ARRAY);
696 g_value_init (&v, G_TYPE_FLOAT);
697 for (i = 0; i < num_values; i++) {
698 g_value_set_float (&v, data[i]);
699 gst_value_array_append_value (&a, &v); /* copies by value */
703 gst_value_array_append_value (cv, &a); /* copies by value */
708 gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime timestamp,
709 GstClockTime duration)
711 GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (spectrum);
712 GstSpectrumChannel *cd;
714 GValue *mcv = NULL, *pcv = NULL;
715 GstClockTime endtime, running_time, stream_time;
717 GST_DEBUG_OBJECT (spectrum, "preparing message, bands =%d ", spectrum->bands);
719 running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
721 stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
723 /* endtime is for backwards compatibility */
724 endtime = stream_time + duration;
726 s = gst_structure_new ("spectrum",
727 "endtime", GST_TYPE_CLOCK_TIME, endtime,
728 "timestamp", G_TYPE_UINT64, timestamp,
729 "stream-time", G_TYPE_UINT64, stream_time,
730 "running-time", G_TYPE_UINT64, running_time,
731 "duration", G_TYPE_UINT64, duration, NULL);
733 if (!spectrum->multi_channel) {
734 cd = &spectrum->channel_data[0];
736 if (spectrum->message_magnitude) {
737 /* FIXME 0.11: this should be an array, not a list */
738 mcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "magnitude");
739 gst_spectrum_message_add_list (mcv, cd->spect_magnitude, spectrum->bands);
741 if (spectrum->message_phase) {
742 /* FIXME 0.11: this should be an array, not a list */
743 pcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "phase");
744 gst_spectrum_message_add_list (pcv, cd->spect_phase, spectrum->bands);
748 guint channels = GST_AUDIO_FILTER_CHANNELS (spectrum);
750 if (spectrum->message_magnitude) {
751 mcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "magnitude");
753 if (spectrum->message_phase) {
754 pcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "phase");
757 for (c = 0; c < channels; c++) {
758 cd = &spectrum->channel_data[c];
760 if (spectrum->message_magnitude) {
761 gst_spectrum_message_add_array (mcv, cd->spect_magnitude,
764 if (spectrum->message_phase) {
765 gst_spectrum_message_add_array (pcv, cd->spect_magnitude,
770 return gst_message_new_element (GST_OBJECT (spectrum), s);
774 gst_spectrum_run_fft (GstSpectrum * spectrum, GstSpectrumChannel * cd,
778 guint bands = spectrum->bands;
779 guint nfft = 2 * bands - 2;
780 gint threshold = spectrum->threshold;
781 gfloat *input = cd->input;
782 gfloat *input_tmp = cd->input_tmp;
783 gfloat *spect_magnitude = cd->spect_magnitude;
784 gfloat *spect_phase = cd->spect_phase;
785 GstFFTF32Complex *freqdata = cd->freqdata;
786 GstFFTF32 *fft_ctx = cd->fft_ctx;
788 for (i = 0; i < nfft; i++)
789 input_tmp[i] = input[(input_pos + i) % nfft];
791 gst_fft_f32_window (fft_ctx, input_tmp, GST_FFT_WINDOW_HAMMING);
793 gst_fft_f32_fft (fft_ctx, input_tmp, freqdata);
795 if (spectrum->message_magnitude) {
797 /* Calculate magnitude in db */
798 for (i = 0; i < bands; i++) {
799 val = freqdata[i].r * freqdata[i].r;
800 val += freqdata[i].i * freqdata[i].i;
802 val = 10.0 * log10 (val);
805 spect_magnitude[i] += val;
809 if (spectrum->message_phase) {
810 /* Calculate phase */
811 for (i = 0; i < bands; i++)
812 spect_phase[i] += atan2 (freqdata[i].i, freqdata[i].r);
817 gst_spectrum_prepare_message_data (GstSpectrum * spectrum,
818 GstSpectrumChannel * cd)
821 guint bands = spectrum->bands;
822 guint num_fft = spectrum->num_fft;
824 /* Calculate average */
825 if (spectrum->message_magnitude) {
826 gfloat *spect_magnitude = cd->spect_magnitude;
827 for (i = 0; i < bands; i++)
828 spect_magnitude[i] /= num_fft;
830 if (spectrum->message_phase) {
831 gfloat *spect_phase = cd->spect_phase;
832 for (i = 0; i < bands; i++)
833 spect_phase[i] /= num_fft;
838 gst_spectrum_reset_message_data (GstSpectrum * spectrum,
839 GstSpectrumChannel * cd)
841 guint bands = spectrum->bands;
842 gfloat *spect_magnitude = cd->spect_magnitude;
843 gfloat *spect_phase = cd->spect_phase;
845 /* reset spectrum accumulators */
846 memset (spect_magnitude, 0, bands * sizeof (gfloat));
847 memset (spect_phase, 0, bands * sizeof (gfloat));
851 gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
853 GstSpectrum *spectrum = GST_SPECTRUM (trans);
854 guint rate = GST_AUDIO_FILTER_RATE (spectrum);
855 guint channels = GST_AUDIO_FILTER_CHANNELS (spectrum);
856 guint bps = GST_AUDIO_FILTER_BPS (spectrum);
857 guint bpf = GST_AUDIO_FILTER_BPF (spectrum);
858 guint output_channels = spectrum->multi_channel ? channels : 1;
860 gfloat max_value = (1UL << ((bps << 3) - 1)) - 1;
861 guint bands = spectrum->bands;
862 guint nfft = 2 * bands - 2;
868 guint fft_todo, msg_todo, block_size;
869 gboolean have_full_interval;
870 GstSpectrumChannel *cd;
871 GstSpectrumInputData input_data;
873 g_mutex_lock (&spectrum->lock);
874 gst_buffer_map (buffer, &map, GST_MAP_READ);
878 GST_LOG_OBJECT (spectrum, "input size: %" G_GSIZE_FORMAT " bytes", size);
880 if (GST_BUFFER_IS_DISCONT (buffer)) {
881 GST_DEBUG_OBJECT (spectrum, "Discontinuity detected -- flushing");
882 gst_spectrum_flush (spectrum);
885 /* If we don't have a FFT context yet (or it was reset due to parameter
886 * changes) get one and allocate memory for everything
888 if (spectrum->channel_data == NULL) {
889 GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands);
891 gst_spectrum_alloc_channel_data (spectrum);
893 /* number of sample frames we process before posting a message
894 * interval is in ns */
895 spectrum->frames_per_interval =
896 gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND);
897 spectrum->frames_todo = spectrum->frames_per_interval;
898 /* rounding error for frames_per_interval in ns,
899 * aggregated it in accumulated_error */
900 spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
901 if (spectrum->frames_per_interval == 0)
902 spectrum->frames_per_interval = 1;
904 GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
905 G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
906 GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval,
907 GST_TIME_ARGS (spectrum->error_per_interval));
909 spectrum->input_pos = 0;
911 gst_spectrum_flush (spectrum);
914 if (spectrum->num_frames == 0)
915 spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);
917 input_pos = spectrum->input_pos;
918 input_data = spectrum->input_data;
920 while (size >= bpf) {
921 /* run input_data for a chunk of data */
922 fft_todo = nfft - (spectrum->num_frames % nfft);
923 msg_todo = spectrum->frames_todo - spectrum->num_frames;
924 GST_LOG_OBJECT (spectrum,
925 "message frames todo: %u, fft frames todo: %u, input frames %"
926 G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf));
927 block_size = msg_todo;
928 if (block_size > (size / bpf))
929 block_size = (size / bpf);
930 if (block_size > fft_todo)
931 block_size = fft_todo;
933 for (c = 0; c < output_channels; c++) {
934 cd = &spectrum->channel_data[c];
936 /* Move the current frames into our ringbuffers */
937 input_data (data + c * bps, input, block_size, channels, max_value,
940 data += block_size * bpf;
941 size -= block_size * bpf;
942 input_pos = (input_pos + block_size) % nfft;
943 spectrum->num_frames += block_size;
945 have_full_interval = (spectrum->num_frames == spectrum->frames_todo);
947 GST_LOG_OBJECT (spectrum,
948 "size: %" G_GSIZE_FORMAT ", do-fft = %d, do-message = %d", size,
949 (spectrum->num_frames % nfft == 0), have_full_interval);
951 /* If we have enough frames for an FFT or we have all frames required for
952 * the interval and we haven't run a FFT, then run an FFT */
953 if ((spectrum->num_frames % nfft == 0) ||
954 (have_full_interval && !spectrum->num_fft)) {
955 for (c = 0; c < output_channels; c++) {
956 cd = &spectrum->channel_data[c];
957 gst_spectrum_run_fft (spectrum, cd, input_pos);
962 /* Do we have the FFTs for one interval? */
963 if (have_full_interval) {
964 GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT
965 " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft,
966 spectrum->num_frames, spectrum->frames_per_interval,
967 GST_TIME_ARGS (spectrum->accumulated_error));
969 spectrum->frames_todo = spectrum->frames_per_interval;
970 if (spectrum->accumulated_error >= GST_SECOND) {
971 spectrum->accumulated_error -= GST_SECOND;
972 spectrum->frames_todo++;
974 spectrum->accumulated_error += spectrum->error_per_interval;
976 if (spectrum->post_messages) {
979 for (c = 0; c < output_channels; c++) {
980 cd = &spectrum->channel_data[c];
981 gst_spectrum_prepare_message_data (spectrum, cd);
984 m = gst_spectrum_message_new (spectrum, spectrum->message_ts,
987 gst_element_post_message (GST_ELEMENT (spectrum), m);
990 if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts))
991 spectrum->message_ts +=
992 gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);
994 for (c = 0; c < output_channels; c++) {
995 cd = &spectrum->channel_data[c];
996 gst_spectrum_reset_message_data (spectrum, cd);
998 spectrum->num_frames = 0;
999 spectrum->num_fft = 0;
1003 spectrum->input_pos = input_pos;
1005 gst_buffer_unmap (buffer, &map);
1006 g_mutex_unlock (&spectrum->lock);
1008 g_assert (size == 0);
1014 plugin_init (GstPlugin * plugin)
1016 return gst_element_register (plugin, "spectrum", GST_RANK_NONE,
1020 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1023 "Run an FFT on the audio signal, output spectrum data",
1024 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)