From e9684cd1a13f885e497f74648fce4aaed7d6789d Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Thu, 12 Sep 2024 19:42:03 +0200 Subject: [PATCH] Move gstfastspectrum to 3rdparty --- 3rdparty/README.md | 8 + .../gstfastspectrum}/CMakeLists.txt | 13 +- 3rdparty/gstfastspectrum/gstfastspectrum.cpp | 520 ++++++++++++++++++ .../gstfastspectrum}/gstfastspectrum.h | 39 +- CMakeLists.txt | 2 +- ext/gstmoodbar/gstfastspectrum.cpp | 520 ------------------ ext/gstmoodbar/gstmoodbarplugin.cpp | 46 -- ext/gstmoodbar/gstmoodbarplugin.h | 25 - src/CMakeLists.txt | 4 +- src/engine/gststartup.cpp | 4 +- src/moodbar/gstfastspectrumplugin.cpp | 54 ++ src/moodbar/gstfastspectrumplugin.h | 27 + src/moodbar/moodbarpipeline.cpp | 4 +- 13 files changed, 640 insertions(+), 626 deletions(-) rename {ext/gstmoodbar => 3rdparty/gstfastspectrum}/CMakeLists.txt (61%) create mode 100644 3rdparty/gstfastspectrum/gstfastspectrum.cpp rename {ext/gstmoodbar => 3rdparty/gstfastspectrum}/gstfastspectrum.h (64%) delete mode 100644 ext/gstmoodbar/gstfastspectrum.cpp delete mode 100644 ext/gstmoodbar/gstmoodbarplugin.cpp delete mode 100644 ext/gstmoodbar/gstmoodbarplugin.h create mode 100644 src/moodbar/gstfastspectrumplugin.cpp create mode 100644 src/moodbar/gstfastspectrumplugin.h diff --git a/3rdparty/README.md b/3rdparty/README.md index c33873c77..be40f8232 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -26,3 +26,11 @@ getopt included on Windows for command line options parsing with Unicode support The directory can safely be deleted on other platforms. URL: https://github.com/ludvikjerabek/getopt-win + +gstfastspectrum +--------------- +A GStreamer spectrum plugin using FFTW3. +It is needed for moodbar support, and is currently not available +in GStreamer. +The plan is to submit it to GStreamer, or move it to +a seperate repository outside of Strawberry. diff --git a/ext/gstmoodbar/CMakeLists.txt b/3rdparty/gstfastspectrum/CMakeLists.txt similarity index 61% rename from ext/gstmoodbar/CMakeLists.txt rename to 3rdparty/gstfastspectrum/CMakeLists.txt index e44f4e23a..5e5308c7e 100644 --- a/ext/gstmoodbar/CMakeLists.txt +++ b/3rdparty/gstfastspectrum/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.13) -set(SOURCES gstfastspectrum.cpp gstmoodbarplugin.cpp) +set(SOURCES gstfastspectrum.cpp) -add_library(gstmoodbar STATIC ${SOURCES}) +add_library(gstfastspectrum STATIC ${SOURCES}) -target_include_directories(gstmoodbar SYSTEM PRIVATE +target_include_directories(gstfastspectrum SYSTEM PRIVATE ${GLIB_INCLUDE_DIRS} ${GOBJECT_INCLUDE_DIRS} ${GSTREAMER_INCLUDE_DIRS} @@ -13,9 +13,9 @@ target_include_directories(gstmoodbar SYSTEM PRIVATE ${FFTW3_INCLUDE_DIR} ) -target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(gstfastspectrum PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_directories(gstmoodbar PRIVATE +target_link_directories(gstfastspectrum PRIVATE ${GLIB_LIBRARY_DIRS} ${GOBJECT_LIBRARY_DIRS} ${GSTREAMER_LIBRARY_DIRS} @@ -24,12 +24,11 @@ target_link_directories(gstmoodbar PRIVATE ${FFTW3_LIBRARY_DIRS} ) -target_link_libraries(gstmoodbar PRIVATE +target_link_libraries(gstfastspectrum PRIVATE ${GLIB_LIBRARIES} ${GOBJECT_LIBRARIES} ${GSTREAMER_LIBRARIES} ${GSTREAMER_BASE_LIBRARIES} ${GSTREAMER_AUDIO_LIBRARIES} ${FFTW3_FFTW_LIBRARY} - Qt${QT_VERSION_MAJOR}::Core ) diff --git a/3rdparty/gstfastspectrum/gstfastspectrum.cpp b/3rdparty/gstfastspectrum/gstfastspectrum.cpp new file mode 100644 index 000000000..eb726aab8 --- /dev/null +++ b/3rdparty/gstfastspectrum/gstfastspectrum.cpp @@ -0,0 +1,520 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * <2006,2011> Stefan Kost + * <2007-2009> Sebastian Dröge + * <2018-2024> Jonas Kvinge + * + * 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. + */ + +#include +#include + +#include + +#include +#include + +#include + +#include "gstfastspectrum.h" + +GST_DEBUG_CATEGORY_STATIC(gst_strawberry_fastspectrum_debug); + +namespace { + +// Spectrum properties +constexpr auto DEFAULT_INTERVAL = (GST_SECOND / 10); +constexpr auto DEFAULT_BANDS = 128; + +enum { + PROP_0, + PROP_INTERVAL, + PROP_BANDS +}; + +} // namespace + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +G_DEFINE_TYPE(GstStrawberryFastSpectrum, gst_strawberry_fastspectrum, GST_TYPE_AUDIO_FILTER) +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +static void gst_strawberry_fastspectrum_finalize(GObject *object); +static void gst_strawberry_fastspectrum_set_property(GObject *object, const guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_strawberry_fastspectrum_get_property(GObject *object, const guint prop_id, GValue *value, GParamSpec *pspec); +static gboolean gst_strawberry_fastspectrum_start(GstBaseTransform *transform); +static gboolean gst_strawberry_fastspectrum_stop(GstBaseTransform *transform); +static GstFlowReturn gst_strawberry_fastspectrum_transform_ip(GstBaseTransform *transform, GstBuffer *buffer); +static gboolean gst_strawberry_fastspectrum_setup(GstAudioFilter *audio_filter, const GstAudioInfo *audio_info); + +static void gst_strawberry_fastspectrum_class_init(GstStrawberryFastSpectrumClass *klass) { + + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); + GstBaseTransformClass *transform_class = GST_BASE_TRANSFORM_CLASS(klass); + GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS(klass); + + gobject_class->set_property = gst_strawberry_fastspectrum_set_property; + gobject_class->get_property = gst_strawberry_fastspectrum_get_property; + gobject_class->finalize = gst_strawberry_fastspectrum_finalize; + + transform_class->start = GST_DEBUG_FUNCPTR(gst_strawberry_fastspectrum_start); + transform_class->stop = GST_DEBUG_FUNCPTR(gst_strawberry_fastspectrum_stop); + transform_class->transform_ip = GST_DEBUG_FUNCPTR(gst_strawberry_fastspectrum_transform_ip); + transform_class->passthrough_on_same_caps = TRUE; + + filter_class->setup = GST_DEBUG_FUNCPTR(gst_strawberry_fastspectrum_setup); + + 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(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + 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(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + GST_DEBUG_CATEGORY_INIT(gst_strawberry_fastspectrum_debug, "spectrum", 0, "audio spectrum analyser element"); + + gst_element_class_set_static_metadata(element_class, + "Fast spectrum analyzer using FFTW", + "Filter/Analyzer/Audio", + "Run an FFT on the audio signal, output spectrum data", + "Erik Walthinsen , " + "Stefan Kost , " + "Sebastian Dröge , " + "Jonas Kvinge "); + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + GstCaps *caps = gst_caps_from_string(GST_AUDIO_CAPS_MAKE("{ S16LE, S24LE, S32LE, F32LE, F64LE }") ", layout = (string) interleaved, channels = 1"); +#else + GstCaps *caps = gst_caps_from_string(GST_AUDIO_CAPS_MAKE("{ S16BE, S24BE, S32BE, F32BE, F64BE }") ", layout = (string) interleaved, channels = 1"); +#endif + + gst_audio_filter_class_add_pad_templates(filter_class, caps); + gst_caps_unref(caps); + + g_mutex_init(&klass->fftw_lock); + +} + +static void gst_strawberry_fastspectrum_init(GstStrawberryFastSpectrum *fastspectrum) { + + fastspectrum->interval = DEFAULT_INTERVAL; + fastspectrum->bands = DEFAULT_BANDS; + + fastspectrum->channel_data_initialized = false; + + g_mutex_init(&fastspectrum->lock); + +} + +static void gst_strawberry_fastspectrum_alloc_channel_data(GstStrawberryFastSpectrum *fastspectrum) { + + const guint bands = fastspectrum->bands; + const guint nfft = 2 * bands - 2; + + fastspectrum->input_ring_buffer = new double[nfft]; + fastspectrum->fft_input = reinterpret_cast(fftw_malloc(sizeof(double) * nfft)); + fastspectrum->fft_output = reinterpret_cast(fftw_malloc(sizeof(fftw_complex) * (nfft / 2 + 1))); + + fastspectrum->spect_magnitude = new double[bands] {}; + + GstStrawberryFastSpectrumClass *klass = reinterpret_cast(G_OBJECT_GET_CLASS(fastspectrum)); + { + g_mutex_lock(&klass->fftw_lock); + fastspectrum->plan = fftw_plan_dft_r2c_1d(static_cast(nfft), fastspectrum->fft_input, fastspectrum->fft_output, FFTW_ESTIMATE); + g_mutex_unlock(&klass->fftw_lock); + } + fastspectrum->channel_data_initialized = true; + +} + +static void gst_strawberry_fastspectrum_free_channel_data(GstStrawberryFastSpectrum *fastspectrum) { + + GstStrawberryFastSpectrumClass *klass = reinterpret_cast(G_OBJECT_GET_CLASS(fastspectrum)); + + if (fastspectrum->channel_data_initialized) { + { + g_mutex_lock(&klass->fftw_lock); + fftw_destroy_plan(fastspectrum->plan); + g_mutex_unlock(&klass->fftw_lock); + } + fftw_free(fastspectrum->fft_input); + fftw_free(fastspectrum->fft_output); + delete[] fastspectrum->input_ring_buffer; + delete[] fastspectrum->spect_magnitude; + + fastspectrum->channel_data_initialized = false; + } + +} + +static void gst_strawberry_fastspectrum_flush(GstStrawberryFastSpectrum *fastspectrum) { + + fastspectrum->num_frames = 0; + fastspectrum->num_fft = 0; + fastspectrum->accumulated_error = 0; + +} + +static void gst_strawberry_fastspectrum_reset_state(GstStrawberryFastSpectrum *fastspectrum) { + + GST_DEBUG_OBJECT(fastspectrum, "resetting state"); + + gst_strawberry_fastspectrum_free_channel_data(fastspectrum); + gst_strawberry_fastspectrum_flush(fastspectrum); + +} + +static void gst_strawberry_fastspectrum_finalize(GObject *object) { + + GstStrawberryFastSpectrum *fastspectrum = reinterpret_cast(object); + + gst_strawberry_fastspectrum_reset_state(fastspectrum); + g_mutex_clear(&fastspectrum->lock); + + G_OBJECT_CLASS(gst_strawberry_fastspectrum_parent_class)->finalize(object); + +} + +static void gst_strawberry_fastspectrum_set_property(GObject *object, const guint prop_id, const GValue *value, GParamSpec *pspec) { + + GstStrawberryFastSpectrum *filter = reinterpret_cast(object); + + switch (prop_id) { + case PROP_INTERVAL: { + const guint64 interval = g_value_get_uint64(value); + g_mutex_lock(&filter->lock); + if (filter->interval != interval) { + filter->interval = interval; + gst_strawberry_fastspectrum_reset_state(filter); + } + g_mutex_unlock(&filter->lock); + break; + } + case PROP_BANDS: { + const guint bands = g_value_get_uint(value); + g_mutex_lock(&filter->lock); + if (filter->bands != bands) { + filter->bands = bands; + gst_strawberry_fastspectrum_reset_state(filter); + } + g_mutex_unlock(&filter->lock); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } + +} + +static void gst_strawberry_fastspectrum_get_property(GObject *object, const guint prop_id, GValue *value, GParamSpec *pspec) { + + GstStrawberryFastSpectrum *fastspectrum = reinterpret_cast(object); + + switch (prop_id) { + case PROP_INTERVAL: + g_value_set_uint64(value, fastspectrum->interval); + break; + case PROP_BANDS: + g_value_set_uint(value, fastspectrum->bands); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } + +} + +static gboolean gst_strawberry_fastspectrum_start(GstBaseTransform *transform) { + + GstStrawberryFastSpectrum *fastspectrum = reinterpret_cast(transform); + + gst_strawberry_fastspectrum_reset_state(fastspectrum); + + return TRUE; + +} + +static gboolean gst_strawberry_fastspectrum_stop(GstBaseTransform *transform) { + + GstStrawberryFastSpectrum *fastspectrum = reinterpret_cast(transform); + + gst_strawberry_fastspectrum_reset_state(fastspectrum); + + return TRUE; + +} + +// Mixing data readers + +static void gst_strawberry_fastspectrum_input_data_mixed_float(const guint8 *_in, double *out, const guint len, const double max_value, guint op, const guint nfft) { + + (void) max_value; + + const gfloat *in = reinterpret_cast(_in); + guint ip = 0; + + for (guint j = 0; j < len; j++) { + out[op] = in[ip++]; + op = (op + 1) % nfft; + } + +} + +static void gst_strawberry_fastspectrum_input_data_mixed_double(const guint8 *_in, double *out, const guint len, const double max_value, guint op, const guint nfft) { + + (void) max_value; + + const gdouble *in = reinterpret_cast(_in); + guint ip = 0; + + for (guint j = 0; j < len; j++) { + out[op] = in[ip++]; + op = (op + 1) % nfft; + } + +} + +static void gst_strawberry_fastspectrum_input_data_mixed_int32_max(const guint8 *_in, double *out, const guint len, const double max_value, guint op, const guint nfft) { + + const gint32 *in = reinterpret_cast(_in); + guint ip = 0; + + for (guint j = 0; j < len; j++) { + out[op] = in[ip++] / max_value; + op = (op + 1) % nfft; + } + +} + +static void gst_strawberry_fastspectrum_input_data_mixed_int24_max(const guint8 *_in, double *out, const guint len, const double max_value, guint op, const guint nfft) { + + for (guint j = 0; j < len; j++) { +#if G_BYTE_ORDER == G_BIG_ENDIAN + guint32 value = GST_READ_UINT24_BE(_in); +#else + guint32 value = GST_READ_UINT24_LE(_in); +#endif + if (value & 0x00800000) { + value |= 0xff000000; + } + + out[op] = value / max_value; + op = (op + 1) % nfft; + _in += 3; + } + +} + +static void gst_strawberry_fastspectrum_input_data_mixed_int16_max(const guint8 *_in, double *out, const guint len, const double max_value, guint op, const guint nfft) { + + const gint16 *in = reinterpret_cast(_in); + guint ip = 0; + + for (guint j = 0; j < len; j++) { + out[op] = in[ip++] / max_value; + op = (op + 1) % nfft; + } + +} + +static gboolean gst_strawberry_fastspectrum_setup(GstAudioFilter *audio_filter, const GstAudioInfo *audio_info) { + + GstStrawberryFastSpectrum *fastspectrum = reinterpret_cast(audio_filter); + GstStrawberryFastSpectrumInputData input_data = nullptr; + + g_mutex_lock(&fastspectrum->lock); + switch (GST_AUDIO_INFO_FORMAT(audio_info)) { + case GST_AUDIO_FORMAT_S16: + input_data = gst_strawberry_fastspectrum_input_data_mixed_int16_max; + break; + case GST_AUDIO_FORMAT_S24: + input_data = gst_strawberry_fastspectrum_input_data_mixed_int24_max; + break; + case GST_AUDIO_FORMAT_S32: + input_data = gst_strawberry_fastspectrum_input_data_mixed_int32_max; + break; + case GST_AUDIO_FORMAT_F32: + input_data = gst_strawberry_fastspectrum_input_data_mixed_float; + break; + case GST_AUDIO_FORMAT_F64: + input_data = gst_strawberry_fastspectrum_input_data_mixed_double; + break; + default: + g_assert_not_reached(); + break; + } + fastspectrum->input_data = input_data; + + gst_strawberry_fastspectrum_reset_state(fastspectrum); + g_mutex_unlock(&fastspectrum->lock); + + return TRUE; + +} + +static void gst_strawberry_fastspectrum_run_fft(GstStrawberryFastSpectrum *fastspectrum, const guint input_pos) { + + const guint bands = fastspectrum->bands; + const guint nfft = 2 * bands - 2; + + for (guint i = 0; i < nfft; i++) { + fastspectrum->fft_input[i] = fastspectrum->input_ring_buffer[(input_pos + i) % nfft]; + } + + // Should be safe to execute the same plan multiple times in parallel. + fftw_execute(fastspectrum->plan); + + // Calculate magnitude in db + for (guint i = 0; i < bands; i++) { + gdouble value = fastspectrum->fft_output[i][0] * fastspectrum->fft_output[i][0]; + value += fastspectrum->fft_output[i][1] * fastspectrum->fft_output[i][1]; + value /= nfft * nfft; + fastspectrum->spect_magnitude[i] += value; + } + +} + +static GstFlowReturn gst_strawberry_fastspectrum_transform_ip(GstBaseTransform *transform, GstBuffer *buffer) { + + GstStrawberryFastSpectrum *fastspectrum = reinterpret_cast(transform); + + const guint rate = GST_AUDIO_FILTER_RATE(fastspectrum); + const guint bps = GST_AUDIO_FILTER_BPS(fastspectrum); + const guint bpf = GST_AUDIO_FILTER_BPF(fastspectrum); + const double max_value = static_cast((1UL << ((bps << 3) - 1)) - 1); + const guint bands = fastspectrum->bands; + const guint nfft = 2 * bands - 2; + + g_mutex_lock(&fastspectrum->lock); + + GstMapInfo map; + gst_buffer_map(buffer, &map, GST_MAP_READ); + const guint8 *data = map.data; + gsize size = map.size; + + GST_LOG_OBJECT(fastspectrum, "input size: %" G_GSIZE_FORMAT " bytes", size); + + if (GST_BUFFER_IS_DISCONT(buffer)) { + GST_DEBUG_OBJECT(fastspectrum, "Discontinuity detected -- flushing"); + gst_strawberry_fastspectrum_flush(fastspectrum); + } + + // 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 (!fastspectrum->channel_data_initialized) { + GST_DEBUG_OBJECT(fastspectrum, "allocating for bands %u", bands); + + gst_strawberry_fastspectrum_alloc_channel_data(fastspectrum); + + // Number of sample frames we process before posting a message interval is in ns + fastspectrum->frames_per_interval = gst_util_uint64_scale(fastspectrum->interval, rate, GST_SECOND); + fastspectrum->frames_todo = fastspectrum->frames_per_interval; + // Rounding error for frames_per_interval in ns, aggregated it in accumulated_error + fastspectrum->error_per_interval = (fastspectrum->interval * rate) % GST_SECOND; + if (fastspectrum->frames_per_interval == 0) { + fastspectrum->frames_per_interval = 1; + } + + GST_INFO_OBJECT(fastspectrum, "interval %" GST_TIME_FORMAT ", fpi %" G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT, GST_TIME_ARGS(fastspectrum->interval), fastspectrum->frames_per_interval, GST_TIME_ARGS(fastspectrum->error_per_interval)); + + fastspectrum->input_pos = 0; + + gst_strawberry_fastspectrum_flush(fastspectrum); + } + + if (fastspectrum->num_frames == 0) { + fastspectrum->message_ts = GST_BUFFER_TIMESTAMP(buffer); + } + + guint input_pos = fastspectrum->input_pos; + GstStrawberryFastSpectrumInputData input_data = fastspectrum->input_data; + + while (size >= bpf) { + // Run input_data for a chunk of data + guint fft_todo = nfft - (fastspectrum->num_frames % nfft); + guint msg_todo = fastspectrum->frames_todo - fastspectrum->num_frames; + GST_LOG_OBJECT(fastspectrum, "message frames todo: %u, fft frames todo: %u, input frames %" G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf)); + guint block_size = msg_todo; + if (block_size > (size / bpf)) { + block_size = (size / bpf); + } + if (block_size > fft_todo) { + block_size = fft_todo; + } + + // Move the current frames into our ringbuffers + input_data(data, fastspectrum->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; + fastspectrum->num_frames += block_size; + + gboolean have_full_interval = (fastspectrum->num_frames == fastspectrum->frames_todo); + + GST_LOG_OBJECT(fastspectrum, "size: %" G_GSIZE_FORMAT ", do-fft = %d, do-message = %d", size, (fastspectrum->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 ((fastspectrum->num_frames % nfft == 0) || (have_full_interval && !fastspectrum->num_fft)) { + gst_strawberry_fastspectrum_run_fft(fastspectrum, input_pos); + fastspectrum->num_fft++; + } + + // Do we have the FFTs for one interval? + if (have_full_interval) { + GST_DEBUG_OBJECT(fastspectrum, "nfft: %u frames: %" G_GUINT64_FORMAT " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft, fastspectrum->num_frames, fastspectrum->frames_per_interval, GST_TIME_ARGS(fastspectrum->accumulated_error)); + + fastspectrum->frames_todo = fastspectrum->frames_per_interval; + if (fastspectrum->accumulated_error >= GST_SECOND) { + fastspectrum->accumulated_error -= GST_SECOND; + fastspectrum->frames_todo++; + } + fastspectrum->accumulated_error += fastspectrum->error_per_interval; + + if (fastspectrum->output_callback) { + // Calculate average + for (guint i = 0; i < fastspectrum->bands; i++) { + fastspectrum->spect_magnitude[i] /= static_cast(fastspectrum->num_fft); + } + + fastspectrum->output_callback(fastspectrum->spect_magnitude, static_cast(fastspectrum->bands)); + + // Reset spectrum accumulators + memset(fastspectrum->spect_magnitude, 0, fastspectrum->bands * sizeof(double)); + } + + if (GST_CLOCK_TIME_IS_VALID(fastspectrum->message_ts)) { + fastspectrum->message_ts += gst_util_uint64_scale(fastspectrum->num_frames, GST_SECOND, rate); + } + + fastspectrum->num_frames = 0; + fastspectrum->num_fft = 0; + } + } + + fastspectrum->input_pos = input_pos; + + gst_buffer_unmap(buffer, &map); + g_mutex_unlock(&fastspectrum->lock); + + g_assert(size == 0); + + return GST_FLOW_OK; + +} diff --git a/ext/gstmoodbar/gstfastspectrum.h b/3rdparty/gstfastspectrum/gstfastspectrum.h similarity index 64% rename from ext/gstmoodbar/gstfastspectrum.h rename to 3rdparty/gstfastspectrum/gstfastspectrum.h index 7de688095..4c6f5dc71 100644 --- a/ext/gstmoodbar/gstfastspectrum.h +++ b/3rdparty/gstfastspectrum/gstfastspectrum.h @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen * Copyright (C) <2009> Sebastian Dröge + * Copyright (C) <2018-2024> Jonas Kvinge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,9 +26,8 @@ // - Send output via a callback instead of GST messages (less overhead). // - Removed all properties except interval and band. - -#ifndef GST_MOODBAR_FASTSPECTRUM_H -#define GST_MOODBAR_FASTSPECTRUM_H +#ifndef GST_STRAWBERRY_FASTSPECTRUM_H +#define GST_STRAWBERRY_FASTSPECTRUM_H #include @@ -37,19 +37,17 @@ G_BEGIN_DECLS -#define GST_TYPE_FASTSPECTRUM (gst_fastspectrum_get_type()) -#define GST_FASTSPECTRUM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_FASTSPECTRUM, GstFastSpectrum)) -#define GST_IS_FASTSPECTRUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_FASTSPECTRUM)) -#define GST_FASTSPECTRUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_FASTSPECTRUM, GstFastSpectrumClass)) -#define GST_IS_FASTSPECTRUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_FASTSPECTRUM)) +#define GST_TYPE_STRAWBERRY_FASTSPECTRUM (gst_strawberry_fastspectrum_get_type()) +#define GST_STRAWBERRY_FASTSPECTRUM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_FASTSPECTRUM, GstStrawberryFastSpectrum)) +#define GST_IS_STRAWBERRY_FASTSPECTRUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_FASTSPECTRUM)) +#define GST_STRAWBERRY_FASTSPECTRUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_FASTSPECTRUM, GstStrawberryFastSpectrumClass)) +#define GST_IS_STRAWBERRY_FASTSPECTRUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_FASTSPECTRUM)) -class QMutex; +typedef void (*GstStrawberryFastSpectrumInputData)(const guint8 *in, double *out, guint len, double max_value, guint op, guint nfft); -typedef void (*GstFastSpectrumInputData)(const guint8 *in, double *out, guint len, double max_value, guint op, guint nfft); +using GstStrawberryFastSpectrumOutputCallback = std::function; -using OutputCallback = std::function; - -struct GstFastSpectrum { +struct GstStrawberryFastSpectrum { GstAudioFilter parent; // Properties @@ -77,20 +75,17 @@ struct GstFastSpectrum { GMutex lock; - GstFastSpectrumInputData input_data; - - OutputCallback output_callback; + GstStrawberryFastSpectrumInputData input_data; + GstStrawberryFastSpectrumOutputCallback output_callback; }; -struct GstFastSpectrumClass { +struct GstStrawberryFastSpectrumClass { GstAudioFilterClass parent_class; - - // Static lock for creating & destroying FFTW plans. - QMutex *fftw_lock; + GMutex fftw_lock; }; -GType gst_fastspectrum_get_type(void); +GType gst_strawberry_fastspectrum_get_type(void); G_END_DECLS -#endif // GST_MOODBAR_FASTSPECTRUM_H +#endif // GST_STRAWBERRY_FASTSPECTRUM_H diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c9e4c0b9..bfc7a2e1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -471,7 +471,7 @@ add_subdirectory(ext/libstrawberry-common) add_subdirectory(ext/libstrawberry-tagreader) add_subdirectory(ext/strawberry-tagreader) if(HAVE_MOODBAR) - add_subdirectory(ext/gstmoodbar) + add_subdirectory(3rdparty/gstfastspectrum) endif() if(GTest_FOUND AND GMOCK_LIBRARY AND Qt${QT_VERSION_MAJOR}Test_FOUND) diff --git a/ext/gstmoodbar/gstfastspectrum.cpp b/ext/gstmoodbar/gstfastspectrum.cpp deleted file mode 100644 index 91a47ac34..000000000 --- a/ext/gstmoodbar/gstfastspectrum.cpp +++ /dev/null @@ -1,520 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * <2006,2011> Stefan Kost - * <2007-2009> Sebastian Dröge - * - * 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. - */ - -#include - -#include -#include - -#include - -#include -#include - -#include - -#include "gstfastspectrum.h" - -GST_DEBUG_CATEGORY_STATIC(gst_fastspectrum_debug); - -namespace { - -// Spectrum properties -constexpr auto DEFAULT_INTERVAL = (GST_SECOND / 10); -constexpr auto DEFAULT_BANDS = 128; - -enum { - PROP_0, - PROP_INTERVAL, - PROP_BANDS -}; - -} // namespace - -#define gst_fastspectrum_parent_class parent_class -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#endif -G_DEFINE_TYPE(GstFastSpectrum, gst_fastspectrum, GST_TYPE_AUDIO_FILTER) -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -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); - GstCaps *caps = nullptr; - - gobject_class->set_property = gst_fastspectrum_set_property; - gobject_class->get_property = gst_fastspectrum_get_property; - gobject_class->finalize = gst_fastspectrum_finalize; - - 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); - trans_class->passthrough_on_same_caps = TRUE; - - filter_class->setup = GST_DEBUG_FUNCPTR(gst_fastspectrum_setup); - - 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(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - 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(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - GST_DEBUG_CATEGORY_INIT(gst_fastspectrum_debug, "spectrum", 0, "audio spectrum analyser element"); - - 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 , " - "Stefan Kost , " - "Sebastian Dröge "); - -#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 - - gst_audio_filter_class_add_pad_templates(filter_class, caps); - gst_caps_unref(caps); - - klass->fftw_lock = new QMutex; -} - -static void gst_fastspectrum_init(GstFastSpectrum *spectrum) { - - spectrum->interval = DEFAULT_INTERVAL; - spectrum->bands = DEFAULT_BANDS; - - spectrum->channel_data_initialized = false; - - g_mutex_init(&spectrum->lock); - -} - -static void gst_fastspectrum_alloc_channel_data(GstFastSpectrum *spectrum) { - - guint bands = spectrum->bands; - guint nfft = 2 * bands - 2; - - spectrum->input_ring_buffer = new double[nfft]; - spectrum->fft_input = reinterpret_cast(fftw_malloc(sizeof(double) * nfft)); - spectrum->fft_output = reinterpret_cast(fftw_malloc(sizeof(fftw_complex) * (nfft / 2 + 1))); - - spectrum->spect_magnitude = new double[bands] {}; - - GstFastSpectrumClass *klass = reinterpret_cast(G_OBJECT_GET_CLASS(spectrum)); - { - QMutexLocker l(klass->fftw_lock); - spectrum->plan = fftw_plan_dft_r2c_1d(static_cast(nfft), spectrum->fft_input, spectrum->fft_output, FFTW_ESTIMATE); - } - spectrum->channel_data_initialized = true; - -} - -static void gst_fastspectrum_free_channel_data(GstFastSpectrum *spectrum) { - - GstFastSpectrumClass *klass = reinterpret_cast(G_OBJECT_GET_CLASS(spectrum)); - if (spectrum->channel_data_initialized) { - { - 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; - - spectrum->channel_data_initialized = false; - } - -} - -static void gst_fastspectrum_flush(GstFastSpectrum *spectrum) { - - spectrum->num_frames = 0; - spectrum->num_fft = 0; - - spectrum->accumulated_error = 0; - -} - -static void gst_fastspectrum_reset_state(GstFastSpectrum *spectrum) { - - GST_DEBUG_OBJECT(spectrum, "resetting state"); - - gst_fastspectrum_free_channel_data(spectrum); - gst_fastspectrum_flush(spectrum); - -} - -static void gst_fastspectrum_finalize(GObject *object) { - - GstFastSpectrum *spectrum = reinterpret_cast(object); - - gst_fastspectrum_reset_state(spectrum); - g_mutex_clear(&spectrum->lock); - - G_OBJECT_CLASS(parent_class)->finalize(object); - -} - -static void gst_fastspectrum_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - - GstFastSpectrum *filter = reinterpret_cast(object); - - switch (prop_id) { - case PROP_INTERVAL: { - guint64 interval = g_value_get_uint64(value); - g_mutex_lock(&filter->lock); - if (filter->interval != interval) { - filter->interval = interval; - gst_fastspectrum_reset_state(filter); - } - g_mutex_unlock(&filter->lock); - break; - } - case PROP_BANDS: { - guint bands = g_value_get_uint(value); - g_mutex_lock(&filter->lock); - if (filter->bands != bands) { - filter->bands = bands; - gst_fastspectrum_reset_state(filter); - } - g_mutex_unlock(&filter->lock); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } - -} - -static void gst_fastspectrum_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - - GstFastSpectrum *filter = reinterpret_cast(object); - - switch (prop_id) { - case PROP_INTERVAL: - g_value_set_uint64(value, filter->interval); - break; - case PROP_BANDS: - g_value_set_uint(value, filter->bands); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } - -} - -static gboolean gst_fastspectrum_start(GstBaseTransform *trans) { - - GstFastSpectrum *spectrum = reinterpret_cast(trans); - - gst_fastspectrum_reset_state(spectrum); - - return TRUE; - -} - -static gboolean gst_fastspectrum_stop(GstBaseTransform *trans) { - - GstFastSpectrum *spectrum = reinterpret_cast(trans); - - gst_fastspectrum_reset_state(spectrum); - - return TRUE; - -} - -// Mixing data readers - -static void input_data_mixed_float(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) { - - Q_UNUSED(max_value); - - const gfloat *in = reinterpret_cast(_in); - guint ip = 0; - - for (guint j = 0; j < len; j++) { - out[op] = in[ip++]; - op = (op + 1) % nfft; - } - -} - -static void input_data_mixed_double(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) { - - Q_UNUSED(max_value); - - const gdouble *in = reinterpret_cast(_in); - guint ip = 0; - - for (guint j = 0; j < len; j++) { - out[op] = in[ip++]; - op = (op + 1) % nfft; - } - -} - -static void input_data_mixed_int32_max(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) { - - const gint32 *in = reinterpret_cast(_in); - guint ip = 0; - - for (guint j = 0; j < len; j++) { - out[op] = in[ip++] / max_value; - op = (op + 1) % nfft; - } - -} - -static void input_data_mixed_int24_max(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) { - - for (guint j = 0; j < len; j++) { -#if G_BYTE_ORDER == G_BIG_ENDIAN - guint32 value = GST_READ_UINT24_BE(_in); -#else - guint32 value = GST_READ_UINT24_LE(_in); -#endif - if (value & 0x00800000) { - value |= 0xff000000; - } - - out[op] = value / max_value; - op = (op + 1) % nfft; - _in += 3; - } - -} - -static void input_data_mixed_int16_max(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) { - - const gint16 *in = reinterpret_cast(_in); - guint ip = 0; - - for (guint j = 0; j < len; j++) { - out[op] = in[ip++] / max_value; - op = (op + 1) % nfft; - } - -} - -static gboolean gst_fastspectrum_setup(GstAudioFilter *base, const GstAudioInfo *info) { - - GstFastSpectrum *spectrum = reinterpret_cast(base); - GstFastSpectrumInputData input_data = nullptr; - - g_mutex_lock(&spectrum->lock); - switch (GST_AUDIO_INFO_FORMAT(info)) { - 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: - g_assert_not_reached(); - break; - } - spectrum->input_data = input_data; - - gst_fastspectrum_reset_state(spectrum); - g_mutex_unlock(&spectrum->lock); - - return TRUE; - -} - -static void gst_fastspectrum_run_fft(GstFastSpectrum *spectrum, guint input_pos) { - - guint bands = spectrum->bands; - guint nfft = 2 * bands - 2; - - for (guint i = 0; i < nfft; i++) { - spectrum->fft_input[i] = spectrum->input_ring_buffer[(input_pos + i) % nfft]; - } - - // Should be safe to execute the same plan multiple times in parallel. - fftw_execute(spectrum->plan); - - // Calculate magnitude in db - for (guint i = 0; i < bands; i++) { - gdouble val = spectrum->fft_output[i][0] * spectrum->fft_output[i][0]; - val += spectrum->fft_output[i][1] * spectrum->fft_output[i][1]; - val /= nfft * nfft; - spectrum->spect_magnitude[i] += val; - } - -} - -static GstFlowReturn gst_fastspectrum_transform_ip(GstBaseTransform *trans, GstBuffer *buffer) { - - GstFastSpectrum *spectrum = reinterpret_cast(trans); - 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((1UL << ((bps << 3) - 1)) - 1); - guint bands = spectrum->bands; - guint nfft = 2 * bands - 2; - guint input_pos = 0; - GstMapInfo map; - const guint8 *data = nullptr; - gsize size = 0; - GstFastSpectrumInputData input_data = nullptr; - - 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_fastspectrum_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_initialized) { - GST_DEBUG_OBJECT(spectrum, "allocating for bands %u", bands); - - gst_fastspectrum_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_fastspectrum_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 - 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; - if (block_size > (size / bpf)) { - block_size = (size / bpf); - } - if (block_size > fft_todo) { - block_size = fft_todo; - } - - // Move the current frames into our ringbuffers - 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; - - gboolean 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)) { - gst_fastspectrum_run_fft(spectrum, 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->output_callback) { - // Calculate average - for (guint i = 0; i < spectrum->bands; i++) { - spectrum->spect_magnitude[i] /= static_cast(spectrum->num_fft); - } - - spectrum->output_callback(spectrum->spect_magnitude, static_cast(spectrum->bands)); - - // Reset spectrum accumulators - memset(spectrum->spect_magnitude, 0, spectrum->bands * sizeof(double)); - } - - if (GST_CLOCK_TIME_IS_VALID(spectrum->message_ts)) { - spectrum->message_ts += gst_util_uint64_scale(spectrum->num_frames, GST_SECOND, rate); - } - - 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; - -} diff --git a/ext/gstmoodbar/gstmoodbarplugin.cpp b/ext/gstmoodbar/gstmoodbarplugin.cpp deleted file mode 100644 index 6c354f2ab..000000000 --- a/ext/gstmoodbar/gstmoodbarplugin.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* This file was part of Clementine. - Copyright 2014, David Sansome - - Strawberry is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Strawberry 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Strawberry. If not, see . -*/ - -#include -#include - -#include "gstfastspectrum.h" -#include "gstmoodbarplugin.h" - -static gboolean gst_moodbar_plugin_init(GstPlugin *plugin) { - - if (!gst_element_register(plugin, "fastspectrum", GST_RANK_NONE, GST_TYPE_FASTSPECTRUM)) { - return FALSE; - } - - return TRUE; -} - -int gstfastspectrum_register_static() { - - return gst_plugin_register_static( - GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "fastspectrum", - "Fast spectrum analyzer for generating Moodbars", - gst_moodbar_plugin_init, - "0.1", - "GPL", - "FastSpectrum", - "FastSpectrum", - "https://www.strawberrymusicplayer.org"); -} diff --git a/ext/gstmoodbar/gstmoodbarplugin.h b/ext/gstmoodbar/gstmoodbarplugin.h deleted file mode 100644 index 510e59436..000000000 --- a/ext/gstmoodbar/gstmoodbarplugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* This file was part of Clementine. - Copyright 2014, David Sansome - - Strawberry is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Strawberry 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Strawberry. If not, see . -*/ - -#ifndef GST_MOODBAR_PLUGIN_H -#define GST_MOODBAR_PLUGIN_H - -extern "C" { -int gstfastspectrum_register_static(); -} - -#endif // GST_MOODBAR_PLUGIN_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0b1faef01..b405a5a5a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -988,6 +988,7 @@ optional_source(HAVE_MOODBAR moodbar/moodbarpipeline.cpp moodbar/moodbarproxystyle.cpp moodbar/moodbarrenderer.cpp + moodbar/gstfastspectrumplugin.cpp settings/moodbarsettingspage.cpp HEADERS moodbar/moodbarcontroller.h @@ -1154,7 +1155,8 @@ if(HAVE_GSTREAMER) endif() if(HAVE_MOODBAR) - target_link_libraries(strawberry_lib PRIVATE gstmoodbar) + target_include_directories(strawberry_lib SYSTEM PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/gstfastspectrum) + target_link_libraries(strawberry_lib PRIVATE gstfastspectrum) endif() if(HAVE_VLC) diff --git a/src/engine/gststartup.cpp b/src/engine/gststartup.cpp index 819e91c91..0e8b12904 100644 --- a/src/engine/gststartup.cpp +++ b/src/engine/gststartup.cpp @@ -39,7 +39,7 @@ #include "utilities/envutils.h" #ifdef HAVE_MOODBAR -# include "ext/gstmoodbar/gstmoodbarplugin.h" +# include "moodbar/gstfastspectrumplugin.h" #endif #include "gststartup.h" @@ -83,7 +83,7 @@ void GstStartup::InitializeGStreamer() { gst_pb_utils_init(); #ifdef HAVE_MOODBAR - gstfastspectrum_register_static(); + gst_strawberry_fastspectrum_register_static(); #endif #ifdef Q_OS_WIN32 diff --git a/src/moodbar/gstfastspectrumplugin.cpp b/src/moodbar/gstfastspectrumplugin.cpp new file mode 100644 index 000000000..738ee3ae1 --- /dev/null +++ b/src/moodbar/gstfastspectrumplugin.cpp @@ -0,0 +1,54 @@ +/* + * Strawberry Music Player + * Copyright 2024, Jonas Kvinge + * + * Strawberry is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Strawberry 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Strawberry. If not, see . + * + */ + +#include +#include + +#include "gstfastspectrum.h" +#include "gstfastspectrumplugin.h" + +static gboolean gst_strawberry_fastspectrum_plugin_init(GstPlugin *plugin) { + + GstRegistry *reg = gst_registry_get(); + if (reg) { + GstPluginFeature *fastspectrum = gst_registry_lookup_feature(reg, "fastspectrum"); + if (fastspectrum) { + gst_object_unref(fastspectrum); + return TRUE; + } + } + + return gst_element_register(plugin, "fastspectrum", GST_RANK_NONE, GST_TYPE_STRAWBERRY_FASTSPECTRUM); + +} + +int gst_strawberry_fastspectrum_register_static() { + + return gst_plugin_register_static( + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "fastspectrum", + "Fast spectrum analyzer for generating Moodbars", + gst_strawberry_fastspectrum_plugin_init, + "0.1", + "GPL", + "FastSpectrum", + "gst-strawberry-fastspectrum", + "https://www.strawberrymusicplayer.org"); +} diff --git a/src/moodbar/gstfastspectrumplugin.h b/src/moodbar/gstfastspectrumplugin.h new file mode 100644 index 000000000..3ac8b6e27 --- /dev/null +++ b/src/moodbar/gstfastspectrumplugin.h @@ -0,0 +1,27 @@ +/* + * Strawberry Music Player + * Copyright 2024, Jonas Kvinge + * + * Strawberry is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Strawberry 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Strawberry. If not, see . + * + */ + +#ifndef GST_STRAWBERRY_FASTSPECTRUM_PLUGIN_H +#define GST_STRAWBERRY_FASTSPECTRUM_PLUGIN_H + +extern "C" { +int gst_strawberry_fastspectrum_register_static(); +} + +#endif // GST_STRAWBERRY_FASTSPECTRUM_PLUGIN_H diff --git a/src/moodbar/moodbarpipeline.cpp b/src/moodbar/moodbarpipeline.cpp index 55cfa096c..638d9f713 100644 --- a/src/moodbar/moodbarpipeline.cpp +++ b/src/moodbar/moodbarpipeline.cpp @@ -37,7 +37,7 @@ #include "utilities/threadutils.h" #include "moodbar/moodbarbuilder.h" -#include "ext/gstmoodbar/gstfastspectrum.h" +#include "gstfastspectrum.h" using namespace Qt::StringLiterals; using std::make_unique; @@ -122,7 +122,7 @@ void MoodbarPipeline::Start() { g_object_set(decodebin, "uri", gst_url.constData(), nullptr); g_object_set(spectrum, "bands", kBands, nullptr); - GstFastSpectrum *fast_spectrum = reinterpret_cast(spectrum); + GstStrawberryFastSpectrum *fast_spectrum = reinterpret_cast(spectrum); fast_spectrum->output_callback = [this](double *magnitudes, int size) { builder_->AddFrame(magnitudes, size); }; // Connect signals