static void gst_spectrum_alloc_channel_data (GstSpectrum * spectrum) { gint i; GstSpectrumChannel *cd; guint bands = spectrum->bands; guint nfft = 2 * bands - 2; g_assert (spectrum->channel_data == NULL); spectrum->num_channels = (spectrum->multi_channel) ? GST_AUDIO_FILTER_CHANNELS (spectrum) : 1; GST_DEBUG_OBJECT (spectrum, "allocating data for %d channels", spectrum->num_channels); spectrum->channel_data = g_new (GstSpectrumChannel, spectrum->num_channels); for (i = 0; i < spectrum->num_channels; i++) { cd = &spectrum->channel_data[i]; cd->fft_ctx = gst_fft_f32_new (nfft, FALSE); cd->input = g_new0 (gfloat, nfft); cd->input_tmp = g_new0 (gfloat, nfft); cd->freqdata = g_new0 (GstFFTF32Complex, bands); cd->spect_magnitude = g_new0 (gfloat, bands); cd->spect_phase = g_new0 (gfloat, bands); } }
static GstFlowReturn gst_iir_equalizer_transform_ip (GstBaseTransform * btrans, GstBuffer * buf) { GstAudioFilter *filter = GST_AUDIO_FILTER (btrans); GstIirEqualizer *equ = GST_IIR_EQUALIZER (btrans); GstClockTime timestamp; GstMapInfo map; gint channels = GST_AUDIO_FILTER_CHANNELS (filter); gboolean need_new_coefficients; if (G_UNLIKELY (channels < 1 || equ->process == NULL)) return GST_FLOW_NOT_NEGOTIATED; BANDS_LOCK (equ); need_new_coefficients = equ->need_new_coefficients; BANDS_UNLOCK (equ); if (!need_new_coefficients && gst_base_transform_is_passthrough (btrans)) return GST_FLOW_OK; timestamp = GST_BUFFER_TIMESTAMP (buf); timestamp = gst_segment_to_stream_time (&btrans->segment, GST_FORMAT_TIME, timestamp); if (GST_CLOCK_TIME_IS_VALID (timestamp)) { GstIirEqualizerBand **filters = equ->bands; guint f, nf = equ->freq_band_count; gst_object_sync_values (GST_OBJECT (equ), timestamp); /* sync values for bands too */ /* FIXME: iterating equ->bands is not thread-safe here */ for (f = 0; f < nf; f++) { gst_object_sync_values (GST_OBJECT (filters[f]), timestamp); } } BANDS_LOCK (equ); if (need_new_coefficients) { update_coefficients (equ); set_passthrough (equ); } BANDS_UNLOCK (equ); gst_buffer_map (buf, &map, GST_MAP_READWRITE); equ->process (equ, map.data, map.size, channels); gst_buffer_unmap (buf, &map); return GST_FLOW_OK; }
static void gst_audio_fx_base_fir_filter_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (object); switch (prop_id) { case PROP_LOW_LATENCY:{ gboolean low_latency; if (GST_STATE (self) >= GST_STATE_PAUSED) { g_warning ("Changing the \"low-latency\" property " "is only allowed in states < PAUSED"); return; } g_mutex_lock (&self->lock); low_latency = g_value_get_boolean (value); if (self->low_latency != low_latency) { self->low_latency = low_latency; gst_audio_fx_base_fir_filter_calculate_frequency_response (self); gst_audio_fx_base_fir_filter_select_process_function (self, GST_AUDIO_FILTER_FORMAT (self), GST_AUDIO_FILTER_CHANNELS (self)); } g_mutex_unlock (&self->lock); break; } case PROP_DRAIN_ON_CHANGES:{ g_mutex_lock (&self->lock); self->drain_on_changes = g_value_get_boolean (value); g_mutex_unlock (&self->lock); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static GstFlowReturn gst_audio_fx_base_fir_filter_transform (GstBaseTransform * base, GstBuffer * inbuf, GstBuffer * outbuf) { GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (base); GstClockTime timestamp, expected_timestamp; gint channels = GST_AUDIO_FILTER_CHANNELS (self); gint rate = GST_AUDIO_FILTER_RATE (self); gint bps = GST_AUDIO_FILTER_BPS (self); GstMapInfo inmap, outmap; guint input_samples; guint output_samples; guint generated_samples; guint64 output_offset; gint64 diff = 0; GstClockTime stream_time; timestamp = GST_BUFFER_TIMESTAMP (outbuf); if (!GST_CLOCK_TIME_IS_VALID (timestamp) && !GST_CLOCK_TIME_IS_VALID (self->start_ts)) { GST_ERROR_OBJECT (self, "Invalid timestamp"); return GST_FLOW_ERROR; } g_mutex_lock (&self->lock); stream_time = gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp); GST_DEBUG_OBJECT (self, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); if (GST_CLOCK_TIME_IS_VALID (stream_time)) gst_object_sync_values (GST_OBJECT (self), stream_time); g_return_val_if_fail (self->kernel != NULL, GST_FLOW_ERROR); g_return_val_if_fail (channels != 0, GST_FLOW_ERROR); if (GST_CLOCK_TIME_IS_VALID (self->start_ts)) expected_timestamp = self->start_ts + gst_util_uint64_scale_int (self->nsamples_in, GST_SECOND, rate); else expected_timestamp = GST_CLOCK_TIME_NONE; /* Reset the residue if already existing on discont buffers */ if (GST_BUFFER_IS_DISCONT (inbuf) || (GST_CLOCK_TIME_IS_VALID (expected_timestamp) && (ABS (GST_CLOCK_DIFF (timestamp, expected_timestamp) > 5 * GST_MSECOND)))) { GST_DEBUG_OBJECT (self, "Discontinuity detected - flushing"); if (GST_CLOCK_TIME_IS_VALID (expected_timestamp)) gst_audio_fx_base_fir_filter_push_residue (self); self->buffer_fill = 0; g_free (self->buffer); self->buffer = NULL; self->start_ts = timestamp; self->start_off = GST_BUFFER_OFFSET (inbuf); self->nsamples_out = 0; self->nsamples_in = 0; } else if (!GST_CLOCK_TIME_IS_VALID (self->start_ts)) { self->start_ts = timestamp; self->start_off = GST_BUFFER_OFFSET (inbuf); } gst_buffer_map (inbuf, &inmap, GST_MAP_READ); gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); input_samples = (inmap.size / bps) / channels; output_samples = (outmap.size / bps) / channels; self->nsamples_in += input_samples; generated_samples = self->process (self, inmap.data, outmap.data, input_samples); gst_buffer_unmap (inbuf, &inmap); gst_buffer_unmap (outbuf, &outmap); g_assert (generated_samples <= output_samples); self->nsamples_out += generated_samples; if (generated_samples == 0) goto no_samples; /* Calculate the number of samples we can push out now without outputting * latency zeros in the beginning */ diff = ((gint64) self->nsamples_out) - ((gint64) self->latency); if (diff < 0) goto no_samples; if (diff < generated_samples) { gint64 tmp = diff; diff = generated_samples - diff; generated_samples = tmp; } else { diff = 0; } gst_buffer_resize (outbuf, diff * bps * channels, generated_samples * bps * channels); output_offset = self->nsamples_out - self->latency - generated_samples; GST_BUFFER_TIMESTAMP (outbuf) = self->start_ts + gst_util_uint64_scale_int (output_offset, GST_SECOND, rate); GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int (output_samples, GST_SECOND, rate); if (self->start_off != GST_BUFFER_OFFSET_NONE) { GST_BUFFER_OFFSET (outbuf) = self->start_off + output_offset; GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + generated_samples; } else { GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE; } g_mutex_unlock (&self->lock); GST_DEBUG_OBJECT (self, "Pushing buffer of size %" G_GSIZE_FORMAT " with timestamp: %" GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %" G_GUINT64_FORMAT ", offset_end: %" G_GUINT64_FORMAT ", nsamples_out: %d", gst_buffer_get_size (outbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf), generated_samples); return GST_FLOW_OK; no_samples: { g_mutex_unlock (&self->lock); return GST_BASE_TRANSFORM_FLOW_DROPPED; } }
void gst_audio_fx_base_fir_filter_push_residue (GstAudioFXBaseFIRFilter * self) { GstBuffer *outbuf; GstFlowReturn res; gint rate = GST_AUDIO_FILTER_RATE (self); gint channels = GST_AUDIO_FILTER_CHANNELS (self); gint bps = GST_AUDIO_FILTER_BPS (self); gint outsize, outsamples; GstMapInfo map; guint8 *in, *out; if (channels == 0 || rate == 0 || self->nsamples_in == 0) { self->buffer_fill = 0; g_free (self->buffer); self->buffer = NULL; return; } /* Calculate the number of samples and their memory size that * should be pushed from the residue */ outsamples = self->nsamples_in - (self->nsamples_out - self->latency); if (outsamples <= 0) { self->buffer_fill = 0; g_free (self->buffer); self->buffer = NULL; return; } outsize = outsamples * channels * bps; if (!self->fft || self->low_latency) { gint64 diffsize, diffsamples; /* Process the difference between latency and residue length samples * to start at the actual data instead of starting at the zeros before * when we only got one buffer smaller than latency */ diffsamples = ((gint64) self->latency) - ((gint64) self->buffer_fill) / channels; if (diffsamples > 0) { diffsize = diffsamples * channels * bps; in = g_new0 (guint8, diffsize); out = g_new0 (guint8, diffsize); self->nsamples_out += self->process (self, in, out, diffsamples); g_free (in); g_free (out); } outbuf = gst_buffer_new_and_alloc (outsize); /* Convolve the residue with zeros to get the actual remaining data */ in = g_new0 (guint8, outsize); gst_buffer_map (outbuf, &map, GST_MAP_READWRITE); self->nsamples_out += self->process (self, in, map.data, outsamples); gst_buffer_unmap (outbuf, &map); g_free (in); } else { guint gensamples = 0; outbuf = gst_buffer_new_and_alloc (outsize); gst_buffer_map (outbuf, &map, GST_MAP_READWRITE); while (gensamples < outsamples) { guint step_insamples = self->block_length - self->buffer_fill; guint8 *zeroes = g_new0 (guint8, step_insamples * channels * bps); guint8 *out = g_new (guint8, self->block_length * channels * bps); guint step_gensamples; step_gensamples = self->process (self, zeroes, out, step_insamples); g_free (zeroes); memcpy (map.data + gensamples * bps, out, MIN (step_gensamples, outsamples - gensamples) * bps); gensamples += MIN (step_gensamples, outsamples - gensamples); g_free (out); } self->nsamples_out += gensamples; gst_buffer_unmap (outbuf, &map); } /* Set timestamp, offset, etc from the values we * saved when processing the regular buffers */ if (GST_CLOCK_TIME_IS_VALID (self->start_ts)) GST_BUFFER_TIMESTAMP (outbuf) = self->start_ts; else GST_BUFFER_TIMESTAMP (outbuf) = 0; GST_BUFFER_TIMESTAMP (outbuf) += gst_util_uint64_scale_int (self->nsamples_out - outsamples - self->latency, GST_SECOND, rate); GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int (outsamples, GST_SECOND, rate); if (self->start_off != GST_BUFFER_OFFSET_NONE) { GST_BUFFER_OFFSET (outbuf) = self->start_off + self->nsamples_out - outsamples - self->latency; GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + outsamples; } GST_DEBUG_OBJECT (self, "Pushing residue buffer of size %" G_GSIZE_FORMAT " with timestamp: %" GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %" G_GUINT64_FORMAT ", offset_end: %" G_GUINT64_FORMAT ", nsamples_out: %d", gst_buffer_get_size (outbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf), outsamples); res = gst_pad_push (GST_BASE_TRANSFORM_CAST (self)->srcpad, outbuf); if (G_UNLIKELY (res != GST_FLOW_OK)) { GST_WARNING_OBJECT (self, "failed to push residue"); } self->buffer_fill = 0; }
void gst_audio_fx_base_fir_filter_set_kernel (GstAudioFXBaseFIRFilter * self, gdouble * kernel, guint kernel_length, guint64 latency, const GstAudioInfo * info) { gboolean latency_changed; GstAudioFormat format; gint channels; g_return_if_fail (kernel != NULL); g_return_if_fail (self != NULL); g_mutex_lock (&self->lock); latency_changed = (self->latency != latency || (!self->low_latency && self->kernel_length < FFT_THRESHOLD && kernel_length >= FFT_THRESHOLD) || (!self->low_latency && self->kernel_length >= FFT_THRESHOLD && kernel_length < FFT_THRESHOLD)); /* FIXME: If the latency changes, the buffer size changes too and we * have to drain in any case until this is fixed in the future */ if (self->buffer && (!self->drain_on_changes || latency_changed)) { gst_audio_fx_base_fir_filter_push_residue (self); self->start_ts = GST_CLOCK_TIME_NONE; self->start_off = GST_BUFFER_OFFSET_NONE; self->nsamples_out = 0; self->nsamples_in = 0; self->buffer_fill = 0; } g_free (self->kernel); if (!self->drain_on_changes || latency_changed) { g_free (self->buffer); self->buffer = NULL; self->buffer_fill = 0; self->buffer_length = 0; } self->kernel = kernel; self->kernel_length = kernel_length; if (info) { format = GST_AUDIO_INFO_FORMAT (info); channels = GST_AUDIO_INFO_CHANNELS (info); } else { format = GST_AUDIO_FILTER_FORMAT (self); channels = GST_AUDIO_FILTER_CHANNELS (self); } gst_audio_fx_base_fir_filter_calculate_frequency_response (self); gst_audio_fx_base_fir_filter_select_process_function (self, format, channels); if (latency_changed) { self->latency = latency; gst_element_post_message (GST_ELEMENT (self), gst_message_new_latency (GST_OBJECT (self))); } g_mutex_unlock (&self->lock); }
static void gst_audio_wsinclimit_build_kernel (GstAudioWSincLimit * self, const GstAudioInfo * info) { gint i = 0; gdouble sum = 0.0; gint len = 0; gdouble w; gdouble *kernel = NULL; gint rate, channels; len = self->kernel_length; if (info) { rate = GST_AUDIO_INFO_RATE (info); channels = GST_AUDIO_INFO_CHANNELS (info); } else { rate = GST_AUDIO_FILTER_RATE (self); channels = GST_AUDIO_FILTER_CHANNELS (self); } if (rate == 0) { GST_DEBUG ("rate not set yet"); return; } if (channels == 0) { GST_DEBUG ("channels not set yet"); return; } /* Clamp cutoff frequency between 0 and the nyquist frequency */ self->cutoff = CLAMP (self->cutoff, 0.0, rate / 2); GST_DEBUG ("gst_audio_wsinclimit_: initializing filter kernel of length %d " "with cutoff %.2lf Hz " "for mode %s", len, self->cutoff, (self->mode == MODE_LOW_PASS) ? "low-pass" : "high-pass"); /* fill the kernel */ w = 2 * G_PI * (self->cutoff / rate); kernel = g_new (gdouble, len); for (i = 0; i < len; ++i) { if (i == (len - 1) / 2.0) kernel[i] = w; else kernel[i] = sin (w * (i - (len - 1) / 2)) / (i - (len - 1) / 2.0); /* windowing */ switch (self->window) { case WINDOW_HAMMING: kernel[i] *= (0.54 - 0.46 * cos (2 * G_PI * i / (len - 1))); break; case WINDOW_BLACKMAN: kernel[i] *= (0.42 - 0.5 * cos (2 * G_PI * i / (len - 1)) + 0.08 * cos (4 * G_PI * i / (len - 1))); break; case WINDOW_GAUSSIAN: kernel[i] *= exp (-0.5 * POW2 (3.0 / len * (2 * i - (len - 1)))); break; case WINDOW_COSINE: kernel[i] *= cos (G_PI * i / (len - 1) - G_PI / 2); break; case WINDOW_HANN: kernel[i] *= 0.5 * (1 - cos (2 * G_PI * i / (len - 1))); break; } } /* normalize for unity gain at DC */ for (i = 0; i < len; ++i) sum += kernel[i]; for (i = 0; i < len; ++i) kernel[i] /= sum; /* convert to highpass if specified */ if (self->mode == MODE_HIGH_PASS) { for (i = 0; i < len; ++i) kernel[i] = -kernel[i]; if (len % 2 == 1) { kernel[(len - 1) / 2] += 1.0; } else { kernel[len / 2 - 1] += 0.5; kernel[len / 2] += 0.5; } } gst_audio_fx_base_fir_filter_set_kernel (GST_AUDIO_FX_BASE_FIR_FILTER (self), kernel, self->kernel_length, (len - 1) / 2, info); }
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; }
static GstMessage * gst_spectrum_message_new (GstSpectrum * spectrum, GstClockTime timestamp, GstClockTime duration) { GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (spectrum); GstSpectrumChannel *cd; GstStructure *s; GValue *mcv = NULL, *pcv = NULL; GstClockTime endtime, running_time, stream_time; GST_DEBUG_OBJECT (spectrum, "preparing message, bands =%d ", spectrum->bands); running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME, timestamp); stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp); /* endtime is for backwards compatibility */ endtime = stream_time + duration; s = gst_structure_new ("spectrum", "endtime", GST_TYPE_CLOCK_TIME, endtime, "timestamp", G_TYPE_UINT64, timestamp, "stream-time", G_TYPE_UINT64, stream_time, "running-time", G_TYPE_UINT64, running_time, "duration", G_TYPE_UINT64, duration, NULL); if (!spectrum->multi_channel) { cd = &spectrum->channel_data[0]; if (spectrum->message_magnitude) { /* FIXME 0.11: this should be an array, not a list */ mcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "magnitude"); gst_spectrum_message_add_list (mcv, cd->spect_magnitude, spectrum->bands); } if (spectrum->message_phase) { /* FIXME 0.11: this should be an array, not a list */ pcv = gst_spectrum_message_add_container (s, GST_TYPE_LIST, "phase"); gst_spectrum_message_add_list (pcv, cd->spect_phase, spectrum->bands); } } else { guint c; guint channels = GST_AUDIO_FILTER_CHANNELS (spectrum); if (spectrum->message_magnitude) { mcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "magnitude"); } if (spectrum->message_phase) { pcv = gst_spectrum_message_add_container (s, GST_TYPE_ARRAY, "phase"); } for (c = 0; c < channels; c++) { cd = &spectrum->channel_data[c]; if (spectrum->message_magnitude) { gst_spectrum_message_add_array (mcv, cd->spect_magnitude, spectrum->bands); } if (spectrum->message_phase) { gst_spectrum_message_add_array (pcv, cd->spect_magnitude, spectrum->bands); } } } return gst_message_new_element (GST_OBJECT (spectrum), s); }
static void gst_audio_wsincband_build_kernel (GstAudioWSincBand * self, const GstAudioInfo * info) { gint i = 0; gdouble sum = 0.0; gint len = 0; gdouble *kernel_lp, *kernel_hp; gdouble w; gdouble *kernel; gint rate, channels; len = self->kernel_length; if (info) { rate = GST_AUDIO_INFO_RATE (info); channels = GST_AUDIO_INFO_CHANNELS (info); } else { rate = GST_AUDIO_FILTER_RATE (self); channels = GST_AUDIO_FILTER_CHANNELS (self); } if (rate == 0) { GST_DEBUG ("rate not set yet"); return; } if (channels == 0) { GST_DEBUG ("channels not set yet"); return; } /* Clamp frequencies */ self->lower_frequency = CLAMP (self->lower_frequency, 0.0, rate / 2); self->upper_frequency = CLAMP (self->upper_frequency, 0.0, rate / 2); if (self->lower_frequency > self->upper_frequency) { gint tmp = self->lower_frequency; self->lower_frequency = self->upper_frequency; self->upper_frequency = tmp; } GST_DEBUG ("gst_audio_wsincband: initializing filter kernel of length %d " "with lower frequency %.2lf Hz " ", upper frequency %.2lf Hz for mode %s", len, self->lower_frequency, self->upper_frequency, (self->mode == MODE_BAND_PASS) ? "band-pass" : "band-reject"); /* fill the lp kernel */ w = 2 * G_PI * (self->lower_frequency / rate); kernel_lp = g_new (gdouble, len); for (i = 0; i < len; ++i) { if (i == (len - 1) / 2.0) kernel_lp[i] = w; else kernel_lp[i] = sin (w * (i - (len - 1) / 2.0)) / (i - (len - 1) / 2.0); /* windowing */ switch (self->window) { case WINDOW_HAMMING: kernel_lp[i] *= (0.54 - 0.46 * cos (2 * G_PI * i / (len - 1))); break; case WINDOW_BLACKMAN: kernel_lp[i] *= (0.42 - 0.5 * cos (2 * G_PI * i / (len - 1)) + 0.08 * cos (4 * G_PI * i / (len - 1))); break; case WINDOW_GAUSSIAN: kernel_lp[i] *= exp (-0.5 * POW2 (3.0 / len * (2 * i - (len - 1)))); break; case WINDOW_COSINE: kernel_lp[i] *= cos (G_PI * i / (len - 1) - G_PI / 2); break; case WINDOW_HANN: kernel_lp[i] *= 0.5 * (1 - cos (2 * G_PI * i / (len - 1))); break; } } /* normalize for unity gain at DC */ sum = 0.0; for (i = 0; i < len; ++i) sum += kernel_lp[i]; for (i = 0; i < len; ++i) kernel_lp[i] /= sum; /* fill the hp kernel */ w = 2 * G_PI * (self->upper_frequency / rate); kernel_hp = g_new (gdouble, len); for (i = 0; i < len; ++i) { if (i == (len - 1) / 2.0) kernel_hp[i] = w; else kernel_hp[i] = sin (w * (i - (len - 1) / 2.0)) / (i - (len - 1) / 2.0); /* Windowing */ switch (self->window) { case WINDOW_HAMMING: kernel_hp[i] *= (0.54 - 0.46 * cos (2 * G_PI * i / (len - 1))); break; case WINDOW_BLACKMAN: kernel_hp[i] *= (0.42 - 0.5 * cos (2 * G_PI * i / (len - 1)) + 0.08 * cos (4 * G_PI * i / (len - 1))); break; case WINDOW_GAUSSIAN: kernel_hp[i] *= exp (-0.5 * POW2 (3.0 / len * (2 * i - (len - 1)))); break; case WINDOW_COSINE: kernel_hp[i] *= cos (G_PI * i / (len - 1) - G_PI / 2); break; case WINDOW_HANN: kernel_hp[i] *= 0.5 * (1 - cos (2 * G_PI * i / (len - 1))); break; } } /* normalize for unity gain at DC */ sum = 0.0; for (i = 0; i < len; ++i) sum += kernel_hp[i]; for (i = 0; i < len; ++i) kernel_hp[i] /= sum; /* do spectral inversion to go from lowpass to highpass */ for (i = 0; i < len; ++i) kernel_hp[i] = -kernel_hp[i]; if (len % 2 == 1) { kernel_hp[(len - 1) / 2] += 1.0; } else { kernel_hp[len / 2 - 1] += 0.5; kernel_hp[len / 2] += 0.5; } /* combine the two kernels */ kernel = g_new (gdouble, len); for (i = 0; i < len; ++i) kernel[i] = kernel_lp[i] + kernel_hp[i]; /* free the helper kernels */ g_free (kernel_lp); g_free (kernel_hp); /* do spectral inversion to go from bandreject to bandpass * if specified */ if (self->mode == MODE_BAND_PASS) { for (i = 0; i < len; ++i) kernel[i] = -kernel[i]; kernel[len / 2] += 1; } gst_audio_fx_base_fir_filter_set_kernel (GST_AUDIO_FX_BASE_FIR_FILTER (self), kernel, self->kernel_length, (len - 1) / 2, info); }