strawberry-audio-player-win.../ext/gstmoodbar/gstfastspectrum.cpp

521 lines
16 KiB
C++
Raw Permalink Normal View History

2019-04-18 15:03:01 +02:00
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* <2006,2011> Stefan Kost <ensonic@users.sf.net>
* <2007-2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
2020-02-08 03:40:30 +01:00
#include <QtGlobal>
2019-04-18 15:03:01 +02:00
#include <cstring>
2021-03-26 21:30:13 +01:00
#include <cmath>
2020-02-08 03:40:30 +01:00
#include <glib.h>
2019-04-18 15:03:01 +02:00
2019-06-12 06:34:59 +02:00
#include <gst/gst.h>
#include <gst/audio/gstaudiofilter.h>
2019-04-18 15:03:01 +02:00
#include <QMutex>
#include "gstfastspectrum.h"
2021-10-30 00:48:21 +02:00
GST_DEBUG_CATEGORY_STATIC(gst_fastspectrum_debug);
2019-04-18 15:03:01 +02:00
2021-11-09 19:16:28 +01:00
namespace {
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
// Spectrum properties
2021-11-09 19:16:28 +01:00
constexpr auto DEFAULT_INTERVAL = (GST_SECOND / 10);
constexpr auto DEFAULT_BANDS = 128;
2019-04-18 15:03:01 +02:00
enum {
PROP_0,
PROP_INTERVAL,
PROP_BANDS
};
2021-11-09 19:16:28 +01:00
} // namespace
2019-04-18 15:03:01 +02:00
#define gst_fastspectrum_parent_class parent_class
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
2021-10-30 00:48:21 +02:00
G_DEFINE_TYPE(GstFastSpectrum, gst_fastspectrum, GST_TYPE_AUDIO_FILTER)
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_finalize(GObject *object);
static void gst_fastspectrum_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
static void gst_fastspectrum_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static gboolean gst_fastspectrum_start(GstBaseTransform *trans);
static gboolean gst_fastspectrum_stop(GstBaseTransform *trans);
static GstFlowReturn gst_fastspectrum_transform_ip(GstBaseTransform *trans, GstBuffer *buffer);
static gboolean gst_fastspectrum_setup(GstAudioFilter *base, const GstAudioInfo *info);
static void gst_fastspectrum_class_init(GstFastSpectrumClass *klass) {
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS(klass);
GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS(klass);
2021-03-26 21:30:13 +01:00
GstCaps *caps = nullptr;
2019-04-18 15:03:01 +02:00
gobject_class->set_property = gst_fastspectrum_set_property;
gobject_class->get_property = gst_fastspectrum_get_property;
gobject_class->finalize = gst_fastspectrum_finalize;
2021-10-30 00:48:21 +02:00
trans_class->start = GST_DEBUG_FUNCPTR(gst_fastspectrum_start);
trans_class->stop = GST_DEBUG_FUNCPTR(gst_fastspectrum_stop);
trans_class->transform_ip = GST_DEBUG_FUNCPTR(gst_fastspectrum_transform_ip);
2019-04-18 15:03:01 +02:00
trans_class->passthrough_on_same_caps = TRUE;
2021-10-30 00:48:21 +02:00
filter_class->setup = GST_DEBUG_FUNCPTR(gst_fastspectrum_setup);
2019-04-18 15:03:01 +02:00
2023-01-08 06:21:34 +01:00
g_object_class_install_property(gobject_class, PROP_INTERVAL, g_param_spec_uint64("interval", "Interval", "Interval of time between message posts (in nanoseconds)", 1, G_MAXUINT64, DEFAULT_INTERVAL, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
2019-04-18 15:03:01 +02:00
2023-01-08 06:21:34 +01:00
g_object_class_install_property(gobject_class, PROP_BANDS, g_param_spec_uint("bands", "Bands", "Number of frequency bands", 0, G_MAXUINT, DEFAULT_BANDS, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GST_DEBUG_CATEGORY_INIT(gst_fastspectrum_debug, "spectrum", 0, "audio spectrum analyser element");
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_element_class_set_static_metadata(element_class, "Spectrum analyzer",
"Filter/Analyzer/Audio",
"Run an FFT on the audio signal, output spectrum data",
"Erik Walthinsen <omega@cse.ogi.edu>, "
"Stefan Kost <ensonic@users.sf.net>, "
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
2019-04-18 15:03:01 +02:00
2021-11-09 19:16:28 +01:00
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
caps = gst_caps_from_string(GST_AUDIO_CAPS_MAKE("{ S16LE, S24LE, S32LE, F32LE, F64LE }") ", layout = (string) interleaved, channels = 1");
#else
caps = gst_caps_from_string(GST_AUDIO_CAPS_MAKE("{ S16BE, S24BE, S32BE, F32BE, F64BE }") ", layout = (string) interleaved, channels = 1");
#endif
2021-10-30 00:48:21 +02:00
gst_audio_filter_class_add_pad_templates(filter_class, caps);
gst_caps_unref(caps);
2019-04-18 15:03:01 +02:00
klass->fftw_lock = new QMutex;
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_init(GstFastSpectrum *spectrum) {
2019-04-18 15:03:01 +02:00
spectrum->interval = DEFAULT_INTERVAL;
spectrum->bands = DEFAULT_BANDS;
2020-10-17 17:29:09 +02:00
spectrum->channel_data_initialized = false;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
g_mutex_init(&spectrum->lock);
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_alloc_channel_data(GstFastSpectrum *spectrum) {
2019-04-18 15:03:01 +02:00
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
spectrum->input_ring_buffer = new double[nfft];
2021-10-30 00:48:21 +02:00
spectrum->fft_input = reinterpret_cast<double*>(fftw_malloc(sizeof(double) * nfft));
spectrum->fft_output = reinterpret_cast<fftw_complex*>(fftw_malloc(sizeof(fftw_complex) * (nfft / 2 + 1)));
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
spectrum->spect_magnitude = new double[bands] {};
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GstFastSpectrumClass *klass = reinterpret_cast<GstFastSpectrumClass*>(G_OBJECT_GET_CLASS(spectrum));
2019-04-18 15:03:01 +02:00
{
QMutexLocker l(klass->fftw_lock);
2021-10-30 00:48:21 +02:00
spectrum->plan = fftw_plan_dft_r2c_1d(static_cast<int>(nfft), spectrum->fft_input, spectrum->fft_output, FFTW_ESTIMATE);
2019-04-18 15:03:01 +02:00
}
2020-10-17 17:29:09 +02:00
spectrum->channel_data_initialized = true;
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_free_channel_data(GstFastSpectrum *spectrum) {
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GstFastSpectrumClass *klass = reinterpret_cast<GstFastSpectrumClass*>(G_OBJECT_GET_CLASS(spectrum));
2020-10-17 17:29:09 +02:00
if (spectrum->channel_data_initialized) {
2019-04-18 15:03:01 +02:00
{
QMutexLocker l(klass->fftw_lock);
fftw_destroy_plan(spectrum->plan);
}
fftw_free(spectrum->fft_input);
fftw_free(spectrum->fft_output);
delete[] spectrum->input_ring_buffer;
delete[] spectrum->spect_magnitude;
2020-10-17 17:29:09 +02:00
spectrum->channel_data_initialized = false;
2019-04-18 15:03:01 +02:00
}
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_flush(GstFastSpectrum *spectrum) {
2019-04-18 15:03:01 +02:00
spectrum->num_frames = 0;
spectrum->num_fft = 0;
spectrum->accumulated_error = 0;
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_reset_state(GstFastSpectrum *spectrum) {
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GST_DEBUG_OBJECT(spectrum, "resetting state");
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_free_channel_data(spectrum);
gst_fastspectrum_flush(spectrum);
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_finalize(GObject *object) {
2019-04-18 15:03:01 +02:00
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(object);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state(spectrum);
g_mutex_clear(&spectrum->lock);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
G_OBJECT_CLASS(parent_class)->finalize(object);
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
2019-04-18 15:03:01 +02:00
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
2019-04-18 15:03:01 +02:00
switch (prop_id) {
2021-10-30 00:48:21 +02:00
case PROP_INTERVAL: {
guint64 interval = g_value_get_uint64(value);
g_mutex_lock(&filter->lock);
2019-04-18 15:03:01 +02:00
if (filter->interval != interval) {
filter->interval = interval;
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state(filter);
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
g_mutex_unlock(&filter->lock);
2019-04-18 15:03:01 +02:00
break;
}
2021-10-30 00:48:21 +02:00
case PROP_BANDS: {
guint bands = g_value_get_uint(value);
g_mutex_lock(&filter->lock);
2019-04-18 15:03:01 +02:00
if (filter->bands != bands) {
filter->bands = bands;
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state(filter);
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
g_mutex_unlock(&filter->lock);
2019-04-18 15:03:01 +02:00
break;
}
default:
2021-10-30 00:48:21 +02:00
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2019-04-18 15:03:01 +02:00
break;
}
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
2019-04-18 15:03:01 +02:00
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
2019-04-18 15:03:01 +02:00
switch (prop_id) {
case PROP_INTERVAL:
2021-10-30 00:48:21 +02:00
g_value_set_uint64(value, filter->interval);
2019-04-18 15:03:01 +02:00
break;
case PROP_BANDS:
2021-10-30 00:48:21 +02:00
g_value_set_uint(value, filter->bands);
2019-04-18 15:03:01 +02:00
break;
default:
2021-10-30 00:48:21 +02:00
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2019-04-18 15:03:01 +02:00
break;
}
}
2021-10-30 00:48:21 +02:00
static gboolean gst_fastspectrum_start(GstBaseTransform *trans) {
2019-04-18 15:03:01 +02:00
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state(spectrum);
2019-04-18 15:03:01 +02:00
return TRUE;
}
2021-10-30 00:48:21 +02:00
static gboolean gst_fastspectrum_stop(GstBaseTransform *trans) {
2019-04-18 15:03:01 +02:00
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state(spectrum);
2019-04-18 15:03:01 +02:00
return TRUE;
}
2021-10-30 00:48:21 +02:00
// Mixing data readers
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
static void input_data_mixed_float(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
2019-04-18 15:03:01 +02:00
2019-09-15 20:27:32 +02:00
Q_UNUSED(max_value);
2020-06-15 17:59:02 +02:00
const gfloat *in = reinterpret_cast<const gfloat*>(_in);
2021-10-30 00:48:21 +02:00
guint ip = 0;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for (guint j = 0; j < len; j++) {
2019-04-18 15:03:01 +02:00
out[op] = in[ip++];
op = (op + 1) % nfft;
}
}
2021-10-30 00:48:21 +02:00
static void input_data_mixed_double(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
2019-04-18 15:03:01 +02:00
2019-09-15 20:27:32 +02:00
Q_UNUSED(max_value);
2020-06-15 17:59:02 +02:00
const gdouble *in = reinterpret_cast<const gdouble*>(_in);
2021-10-30 00:48:21 +02:00
guint ip = 0;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for (guint j = 0; j < len; j++) {
2019-04-18 15:03:01 +02:00
out[op] = in[ip++];
op = (op + 1) % nfft;
}
}
2021-10-30 00:48:21 +02:00
static void input_data_mixed_int32_max(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
2019-04-18 15:03:01 +02:00
2020-06-15 17:59:02 +02:00
const gint32 *in = reinterpret_cast<const gint32*>(_in);
2021-10-30 00:48:21 +02:00
guint ip = 0;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for (guint j = 0; j < len; j++) {
2019-04-18 15:03:01 +02:00
out[op] = in[ip++] / max_value;
op = (op + 1) % nfft;
}
}
2021-10-30 00:48:21 +02:00
static void input_data_mixed_int24_max(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for (guint j = 0; j < len; j++) {
2019-04-18 15:03:01 +02:00
#if G_BYTE_ORDER == G_BIG_ENDIAN
2021-10-30 00:48:21 +02:00
guint32 value = GST_READ_UINT24_BE(_in);
2019-04-18 15:03:01 +02:00
#else
2021-10-30 00:48:21 +02:00
guint32 value = GST_READ_UINT24_LE(_in);
2019-04-18 15:03:01 +02:00
#endif
2021-08-23 21:21:08 +02:00
if (value & 0x00800000) {
2019-04-18 15:03:01 +02:00
value |= 0xff000000;
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
out[op] = value / max_value;
op = (op + 1) % nfft;
_in += 3;
}
}
2021-10-30 00:48:21 +02:00
static void input_data_mixed_int16_max(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
2019-04-18 15:03:01 +02:00
2020-06-15 17:59:02 +02:00
const gint16 *in = reinterpret_cast<const gint16*>(_in);
2021-10-30 00:48:21 +02:00
guint ip = 0;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
for (guint j = 0; j < len; j++) {
2019-04-18 15:03:01 +02:00
out[op] = in[ip++] / max_value;
op = (op + 1) % nfft;
}
}
2021-10-30 00:48:21 +02:00
static gboolean gst_fastspectrum_setup(GstAudioFilter *base, const GstAudioInfo *info) {
2019-04-18 15:03:01 +02:00
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(base);
2020-06-14 17:02:47 +02:00
GstFastSpectrumInputData input_data = nullptr;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
g_mutex_lock(&spectrum->lock);
switch (GST_AUDIO_INFO_FORMAT(info)) {
2019-04-18 15:03:01 +02:00
case GST_AUDIO_FORMAT_S16:
input_data = input_data_mixed_int16_max;
break;
case GST_AUDIO_FORMAT_S24:
input_data = input_data_mixed_int24_max;
break;
case GST_AUDIO_FORMAT_S32:
input_data = input_data_mixed_int32_max;
break;
case GST_AUDIO_FORMAT_F32:
input_data = input_data_mixed_float;
break;
case GST_AUDIO_FORMAT_F64:
input_data = input_data_mixed_double;
break;
default:
2021-10-30 00:48:21 +02:00
g_assert_not_reached();
2019-04-18 15:03:01 +02:00
break;
}
spectrum->input_data = input_data;
2021-10-30 00:48:21 +02:00
gst_fastspectrum_reset_state(spectrum);
g_mutex_unlock(&spectrum->lock);
2019-04-18 15:03:01 +02:00
return TRUE;
}
2021-10-30 00:48:21 +02:00
static void gst_fastspectrum_run_fft(GstFastSpectrum *spectrum, guint input_pos) {
2019-04-18 15:03:01 +02:00
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
2021-10-30 00:48:21 +02:00
for (guint i = 0; i < nfft; i++) {
2019-04-18 18:28:11 +02:00
spectrum->fft_input[i] = spectrum->input_ring_buffer[(input_pos + i) % nfft];
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
// Should be safe to execute the same plan multiple times in parallel.
fftw_execute(spectrum->plan);
2021-10-30 00:48:21 +02:00
// Calculate magnitude in db
for (guint i = 0; i < bands; i++) {
gdouble val = spectrum->fft_output[i][0] * spectrum->fft_output[i][0];
2019-04-18 15:03:01 +02:00
val += spectrum->fft_output[i][1] * spectrum->fft_output[i][1];
val /= nfft * nfft;
spectrum->spect_magnitude[i] += val;
}
}
2021-10-30 00:48:21 +02:00
static GstFlowReturn gst_fastspectrum_transform_ip(GstBaseTransform *trans, GstBuffer *buffer) {
2019-04-18 15:03:01 +02:00
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
2021-10-30 00:48:21 +02:00
guint rate = GST_AUDIO_FILTER_RATE(spectrum);
guint bps = GST_AUDIO_FILTER_BPS(spectrum);
guint bpf = GST_AUDIO_FILTER_BPF(spectrum);
double max_value = static_cast<double>((1UL << ((bps << 3) - 1)) - 1);
2019-04-18 15:03:01 +02:00
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
2021-03-26 21:30:13 +01:00
guint input_pos = 0;
2019-04-18 15:03:01 +02:00
GstMapInfo map;
2021-03-26 21:30:13 +01:00
const guint8 *data = nullptr;
gsize size = 0;
GstFastSpectrumInputData input_data = nullptr;
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
g_mutex_lock(&spectrum->lock);
gst_buffer_map(buffer, &map, GST_MAP_READ);
2019-04-18 15:03:01 +02:00
data = map.data;
size = map.size;
2021-10-30 00:48:21 +02:00
GST_LOG_OBJECT(spectrum, "input size: %" G_GSIZE_FORMAT " bytes", size);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
if (GST_BUFFER_IS_DISCONT(buffer)) {
GST_DEBUG_OBJECT(spectrum, "Discontinuity detected -- flushing");
gst_fastspectrum_flush(spectrum);
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
// If we don't have a FFT context yet (or it was reset due to parameter changes) get one and allocate memory for everything
2020-10-17 17:29:09 +02:00
if (!spectrum->channel_data_initialized) {
2021-10-30 00:48:21 +02:00
GST_DEBUG_OBJECT(spectrum, "allocating for bands %u", bands);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
gst_fastspectrum_alloc_channel_data(spectrum);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
// 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);
2019-04-18 15:03:01 +02:00
spectrum->frames_todo = spectrum->frames_per_interval;
2021-10-30 00:48:21 +02:00
// Rounding error for frames_per_interval in ns, aggregated it in accumulated_error
2019-04-18 15:03:01 +02:00
spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
2021-08-23 21:21:08 +02:00
if (spectrum->frames_per_interval == 0) {
2019-04-18 15:03:01 +02:00
spectrum->frames_per_interval = 1;
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
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));
2019-04-18 15:03:01 +02:00
spectrum->input_pos = 0;
2021-10-30 00:48:21 +02:00
gst_fastspectrum_flush(spectrum);
2019-04-18 15:03:01 +02:00
}
2021-08-23 21:21:08 +02:00
if (spectrum->num_frames == 0) {
2021-10-30 00:48:21 +02:00
spectrum->message_ts = GST_BUFFER_TIMESTAMP(buffer);
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
input_pos = spectrum->input_pos;
input_data = spectrum->input_data;
while (size >= bpf) {
2021-10-30 00:48:21 +02:00
// Run input_data for a chunk of data
guint fft_todo = nfft - (spectrum->num_frames % nfft);
guint 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));
guint block_size = msg_todo;
2021-08-23 21:21:08 +02:00
if (block_size > (size / bpf)) {
2019-04-18 15:03:01 +02:00
block_size = (size / bpf);
2021-08-23 21:21:08 +02:00
}
if (block_size > fft_todo) {
2019-04-18 15:03:01 +02:00
block_size = fft_todo;
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
// Move the current frames into our ringbuffers
2019-04-18 15:03:01 +02:00
input_data(data, spectrum->input_ring_buffer, block_size, 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;
2021-10-30 00:48:21 +02:00
gboolean have_full_interval = (spectrum->num_frames == spectrum->frames_todo);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
GST_LOG_OBJECT(spectrum, "size: %" G_GSIZE_FORMAT ", do-fft = %d, do-message = %d", size, (spectrum->num_frames % nfft == 0), have_full_interval);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
// 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
2019-04-18 15:03:01 +02:00
if ((spectrum->num_frames % nfft == 0) || (have_full_interval && !spectrum->num_fft)) {
2021-10-30 00:48:21 +02:00
gst_fastspectrum_run_fft(spectrum, input_pos);
2019-04-18 15:03:01 +02:00
spectrum->num_fft++;
}
2021-10-30 00:48:21 +02:00
// Do we have the FFTs for one interval?
2019-04-18 15:03:01 +02:00
if (have_full_interval) {
2021-10-30 00:48:21 +02:00
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));
2019-04-18 15:03:01 +02:00
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->output_callback) {
// Calculate average
for (guint i = 0; i < spectrum->bands; i++) {
2021-10-30 00:48:21 +02:00
spectrum->spect_magnitude[i] /= static_cast<double>(spectrum->num_fft);
2019-04-18 15:03:01 +02:00
}
2021-10-30 00:48:21 +02:00
spectrum->output_callback(spectrum->spect_magnitude, static_cast<int>(spectrum->bands));
2019-04-18 15:03:01 +02:00
// Reset spectrum accumulators
memset(spectrum->spect_magnitude, 0, spectrum->bands * sizeof(double));
}
2021-10-30 00:48:21 +02:00
if (GST_CLOCK_TIME_IS_VALID(spectrum->message_ts)) {
spectrum->message_ts += gst_util_uint64_scale(spectrum->num_frames, GST_SECOND, rate);
2021-08-23 21:21:08 +02:00
}
2019-04-18 15:03:01 +02:00
spectrum->num_frames = 0;
spectrum->num_fft = 0;
}
}
spectrum->input_pos = input_pos;
2021-10-30 00:48:21 +02:00
gst_buffer_unmap(buffer, &map);
g_mutex_unlock(&spectrum->lock);
2019-04-18 15:03:01 +02:00
2021-10-30 00:48:21 +02:00
g_assert(size == 0);
2019-04-18 15:03:01 +02:00
return GST_FLOW_OK;
}