static void gst_spectrum_finalize (GObject * object) { GstSpectrum *spectrum = GST_SPECTRUM (object); gst_spectrum_reset_state (spectrum); G_OBJECT_CLASS (parent_class)->finalize (object); }
static void gst_spectrum_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstSpectrum *filter = GST_SPECTRUM (object); switch (prop_id) { case PROP_MESSAGE: case PROP_POST_MESSAGES: filter->post_messages = g_value_get_boolean (value); break; case PROP_MESSAGE_MAGNITUDE: filter->message_magnitude = g_value_get_boolean (value); break; case PROP_MESSAGE_PHASE: filter->message_phase = g_value_get_boolean (value); break; case PROP_INTERVAL:{ guint64 interval = g_value_get_uint64 (value); if (filter->interval != interval) { GST_BASE_TRANSFORM_LOCK (filter); filter->interval = interval; gst_spectrum_reset_state (filter); GST_BASE_TRANSFORM_UNLOCK (filter); } } break; case PROP_BANDS:{ guint bands = g_value_get_uint (value); if (filter->bands != bands) { GST_BASE_TRANSFORM_LOCK (filter); filter->bands = bands; gst_spectrum_reset_state (filter); GST_BASE_TRANSFORM_UNLOCK (filter); } } break; case PROP_THRESHOLD: filter->threshold = g_value_get_int (value); break; case PROP_MULTI_CHANNEL:{ gboolean multi_channel = g_value_get_boolean (value); if (filter->multi_channel != multi_channel) { GST_BASE_TRANSFORM_LOCK (filter); filter->multi_channel = multi_channel; gst_spectrum_reset_state (filter); GST_BASE_TRANSFORM_UNLOCK (filter); } } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_spectrum_setup (GstAudioFilter * base, GstRingBufferSpec * format) { GstSpectrum *spectrum = GST_SPECTRUM (base); guint width = format->width / 8; gboolean is_float = (format->type == GST_BUFTYPE_FLOAT); /* max_value will be 0 when depth is 1, * interpret -1 and 0 as -1 and +1 if that's the case. */ guint max_value = (1UL << (format->depth - 1)) - 1; gboolean multi_channel = spectrum->multi_channel; GstSpectrumInputData input_data = NULL; if (is_float) { if (width == 4) { input_data = multi_channel ? input_data_float : input_data_mixed_float; } else if (width == 8) { input_data = multi_channel ? input_data_double : input_data_mixed_double; } else { g_assert_not_reached (); } } else { if (width == 4) { if (max_value) { input_data = multi_channel ? input_data_int32_max : input_data_mixed_int32_max; } else { input_data = multi_channel ? input_data_int32 : input_data_mixed_int32; } } else if (width == 3) { if (max_value) { input_data = multi_channel ? input_data_int24_max : input_data_mixed_int24_max; } else { input_data = multi_channel ? input_data_int24 : input_data_mixed_int24; } } else if (width == 2) { if (max_value) { input_data = multi_channel ? input_data_int16_max : input_data_mixed_int16_max; } else { input_data = multi_channel ? input_data_int16 : input_data_mixed_int16; } } else { g_assert_not_reached (); } } spectrum->input_data = input_data; gst_spectrum_reset_state (spectrum); return TRUE; }
static gboolean gst_spectrum_setup (GstAudioFilter * base, const GstAudioInfo * info) { GstSpectrum *spectrum = GST_SPECTRUM (base); gboolean multi_channel = spectrum->multi_channel; GstSpectrumInputData input_data = NULL; g_mutex_lock (&spectrum->lock); switch (GST_AUDIO_INFO_FORMAT (info)) { case GST_AUDIO_FORMAT_S16: input_data = multi_channel ? input_data_int16_max : input_data_mixed_int16_max; break; case GST_AUDIO_FORMAT_S24: input_data = multi_channel ? input_data_int24_max : input_data_mixed_int24_max; break; case GST_AUDIO_FORMAT_S32: input_data = multi_channel ? input_data_int32_max : input_data_mixed_int32_max; break; case GST_AUDIO_FORMAT_F32: input_data = multi_channel ? input_data_float : input_data_mixed_float; break; case GST_AUDIO_FORMAT_F64: input_data = multi_channel ? input_data_double : input_data_mixed_double; break; default: g_assert_not_reached (); break; } spectrum->input_data = input_data; gst_spectrum_reset_state (spectrum); g_mutex_unlock (&spectrum->lock); return TRUE; }
static GstFlowReturn gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) { #ifdef GSTREAMER_LITE GstSpectrum *spectrum = GST_SPECTRUM (trans); GstRingBufferSpec *format; GstSpectrumChannel *cd; GstSpectrumInputData input_data; guint rate, channels, output_channels, c, width, bands, nfft, input_pos, size, frame_size; guint fft_todo, msg_todo, block_size; gfloat max_value; gfloat *input; gboolean have_full_interval; const guint8 *data; GstMessage *m; if (!spectrum->post_messages) return GST_FLOW_OK; format = &GST_AUDIO_FILTER (spectrum)->format; rate = format->rate; channels = format->channels; output_channels = spectrum->multi_channel ? channels : 1; width = format->width / 8; max_value = (1UL << (format->depth - 1)) - 1; bands = spectrum->bands; nfft = 2 * bands - 2; data = GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer); frame_size = width * channels; #else // GSTREAMER_LITE GstSpectrum *spectrum = GST_SPECTRUM (trans); GstRingBufferSpec *format = &GST_AUDIO_FILTER (spectrum)->format; guint rate = format->rate; guint channels = format->channels; guint output_channels = spectrum->multi_channel ? channels : 1; guint c; guint width = format->width / 8; gfloat max_value = (1UL << (format->depth - 1)) - 1; guint bands = spectrum->bands; guint nfft = 2 * bands - 2; guint input_pos; gfloat *input; const guint8 *data = GST_BUFFER_DATA (buffer); guint size = GST_BUFFER_SIZE (buffer); guint frame_size = width * channels; guint fft_todo, msg_todo, block_size; gboolean have_full_interval; GstSpectrumChannel *cd; GstSpectrumInputData input_data; #endif // GSTREAMER_LITE GST_LOG_OBJECT (spectrum, "input size: %d bytes", GST_BUFFER_SIZE (buffer)); if (GST_BUFFER_IS_DISCONT (buffer)) { GST_DEBUG_OBJECT (spectrum, "Discontinuity detected -- flushing"); gst_spectrum_flush (spectrum); } /* If we don't have a FFT context yet (or it was reset due to parameter * changes) get one and allocate memory for everything */ if (spectrum->channel_data == NULL) { GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands); gst_spectrum_alloc_channel_data (spectrum); /* number of sample frames we process before posting a message * interval is in ns */ spectrum->frames_per_interval = gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND); spectrum->frames_todo = spectrum->frames_per_interval; /* rounding error for frames_per_interval in ns, * aggregated it in accumulated_error */ spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND; if (spectrum->frames_per_interval == 0) spectrum->frames_per_interval = 1; GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %" G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT, GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval, GST_TIME_ARGS (spectrum->error_per_interval)); spectrum->input_pos = 0; gst_spectrum_flush (spectrum); } if (spectrum->num_frames == 0) spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer); input_pos = spectrum->input_pos; input_data = spectrum->input_data; while (size >= frame_size) { /* run input_data for a chunk of data */ fft_todo = nfft - (spectrum->num_frames % nfft); msg_todo = spectrum->frames_todo - spectrum->num_frames; GST_LOG_OBJECT (spectrum, "message frames todo: %u, fft frames todo: %u, input frames %u", msg_todo, fft_todo, (size / frame_size)); block_size = msg_todo; if (block_size > (size / frame_size)) block_size = (size / frame_size); if (block_size > fft_todo) block_size = fft_todo; for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; input = cd->input; /* Move the current frames into our ringbuffers */ input_data (data + c * width, input, block_size, channels, max_value, input_pos, nfft); } data += block_size * frame_size; size -= block_size * frame_size; input_pos = (input_pos + block_size) % nfft; spectrum->num_frames += block_size; have_full_interval = (spectrum->num_frames == spectrum->frames_todo); GST_LOG_OBJECT (spectrum, "size: %u, do-fft = %d, do-message = %d", size, (spectrum->num_frames % nfft == 0), have_full_interval); /* If we have enough frames for an FFT or we have all frames required for * the interval and we haven't run a FFT, then run an FFT */ if ((spectrum->num_frames % nfft == 0) || (have_full_interval && !spectrum->num_fft)) { for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; gst_spectrum_run_fft (spectrum, cd, input_pos); } spectrum->num_fft++; } /* Do we have the FFTs for one interval? */ if (have_full_interval) { GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft, spectrum->num_frames, spectrum->frames_per_interval, GST_TIME_ARGS (spectrum->accumulated_error)); spectrum->frames_todo = spectrum->frames_per_interval; if (spectrum->accumulated_error >= GST_SECOND) { spectrum->accumulated_error -= GST_SECOND; spectrum->frames_todo++; } spectrum->accumulated_error += spectrum->error_per_interval; #ifndef GSTREAMER_LITE if (spectrum->post_messages) { GstMessage *m; #endif // GSTREAMER_LITE for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; gst_spectrum_prepare_message_data (spectrum, cd); } m = gst_spectrum_message_new (spectrum, spectrum->message_ts, spectrum->interval); gst_element_post_message (GST_ELEMENT (spectrum), m); #ifndef GSTREAMER_LITE } #endif // GSTREAMER_LITE if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts)) spectrum->message_ts += gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate); #ifdef GSTREAMER_LITE for (c = 0; c < spectrum->num_channels; c++) { #else // GSTREAMER_LITE for (c = 0; c < channels; c++) { #endif // GSTREAMER_LITE cd = &spectrum->channel_data[c]; gst_spectrum_reset_message_data (spectrum, cd); } spectrum->num_frames = 0; spectrum->num_fft = 0; } } spectrum->input_pos = input_pos; g_assert (size == 0); return GST_FLOW_OK; } #ifdef GSTREAMER_LITE gboolean plugin_init_spectrum (GstPlugin * plugin) #else // GSTREAMER_LITE static gboolean plugin_init (GstPlugin * plugin) #endif // GSTREAMER_LITE { return gst_element_register (plugin, "spectrum", GST_RANK_NONE, GST_TYPE_SPECTRUM); } #ifndef GSTREAMER_LITE GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "spectrum", "Run an FFT on the audio signal, output spectrum data", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
static GstFlowReturn gst_spectrum_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) { GstSpectrum *spectrum = GST_SPECTRUM (trans); guint rate = GST_AUDIO_FILTER_RATE (spectrum); guint channels = GST_AUDIO_FILTER_CHANNELS (spectrum); guint bps = GST_AUDIO_FILTER_BPS (spectrum); guint bpf = GST_AUDIO_FILTER_BPF (spectrum); guint output_channels = spectrum->multi_channel ? channels : 1; guint c; gfloat max_value = (1UL << ((bps << 3) - 1)) - 1; guint bands = spectrum->bands; guint nfft = 2 * bands - 2; guint input_pos; gfloat *input; GstMapInfo map; const guint8 *data; gsize size; guint fft_todo, msg_todo, block_size; gboolean have_full_interval; GstSpectrumChannel *cd; GstSpectrumInputData input_data; g_mutex_lock (&spectrum->lock); gst_buffer_map (buffer, &map, GST_MAP_READ); data = map.data; size = map.size; GST_LOG_OBJECT (spectrum, "input size: %" G_GSIZE_FORMAT " bytes", size); if (GST_BUFFER_IS_DISCONT (buffer)) { GST_DEBUG_OBJECT (spectrum, "Discontinuity detected -- flushing"); gst_spectrum_flush (spectrum); } /* If we don't have a FFT context yet (or it was reset due to parameter * changes) get one and allocate memory for everything */ if (spectrum->channel_data == NULL) { GST_DEBUG_OBJECT (spectrum, "allocating for bands %u", bands); gst_spectrum_alloc_channel_data (spectrum); /* number of sample frames we process before posting a message * interval is in ns */ spectrum->frames_per_interval = gst_util_uint64_scale (spectrum->interval, rate, GST_SECOND); spectrum->frames_todo = spectrum->frames_per_interval; /* rounding error for frames_per_interval in ns, * aggregated it in accumulated_error */ spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND; if (spectrum->frames_per_interval == 0) spectrum->frames_per_interval = 1; GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %" G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT, GST_TIME_ARGS (spectrum->interval), spectrum->frames_per_interval, GST_TIME_ARGS (spectrum->error_per_interval)); spectrum->input_pos = 0; gst_spectrum_flush (spectrum); } if (spectrum->num_frames == 0) spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer); input_pos = spectrum->input_pos; input_data = spectrum->input_data; while (size >= bpf) { /* run input_data for a chunk of data */ fft_todo = nfft - (spectrum->num_frames % nfft); msg_todo = spectrum->frames_todo - spectrum->num_frames; GST_LOG_OBJECT (spectrum, "message frames todo: %u, fft frames todo: %u, input frames %" G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf)); block_size = msg_todo; if (block_size > (size / bpf)) block_size = (size / bpf); if (block_size > fft_todo) block_size = fft_todo; for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; input = cd->input; /* Move the current frames into our ringbuffers */ input_data (data + c * bps, input, block_size, channels, max_value, input_pos, nfft); } data += block_size * bpf; size -= block_size * bpf; input_pos = (input_pos + block_size) % nfft; spectrum->num_frames += block_size; have_full_interval = (spectrum->num_frames == spectrum->frames_todo); GST_LOG_OBJECT (spectrum, "size: %" G_GSIZE_FORMAT ", do-fft = %d, do-message = %d", size, (spectrum->num_frames % nfft == 0), have_full_interval); /* If we have enough frames for an FFT or we have all frames required for * the interval and we haven't run a FFT, then run an FFT */ if ((spectrum->num_frames % nfft == 0) || (have_full_interval && !spectrum->num_fft)) { for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; gst_spectrum_run_fft (spectrum, cd, input_pos); } spectrum->num_fft++; } /* Do we have the FFTs for one interval? */ if (have_full_interval) { GST_DEBUG_OBJECT (spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft, spectrum->num_frames, spectrum->frames_per_interval, GST_TIME_ARGS (spectrum->accumulated_error)); spectrum->frames_todo = spectrum->frames_per_interval; if (spectrum->accumulated_error >= GST_SECOND) { spectrum->accumulated_error -= GST_SECOND; spectrum->frames_todo++; } spectrum->accumulated_error += spectrum->error_per_interval; if (spectrum->post_messages) { GstMessage *m; for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; gst_spectrum_prepare_message_data (spectrum, cd); } m = gst_spectrum_message_new (spectrum, spectrum->message_ts, spectrum->interval); gst_element_post_message (GST_ELEMENT (spectrum), m); } if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts)) spectrum->message_ts += gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate); for (c = 0; c < output_channels; c++) { cd = &spectrum->channel_data[c]; gst_spectrum_reset_message_data (spectrum, cd); } spectrum->num_frames = 0; spectrum->num_fft = 0; } } spectrum->input_pos = input_pos; gst_buffer_unmap (buffer, &map); g_mutex_unlock (&spectrum->lock); g_assert (size == 0); return GST_FLOW_OK; }