diff --git a/3rdparty/chromaprint/.gitignore b/3rdparty/chromaprint/.gitignore deleted file mode 100644 index f9889eb31..000000000 --- a/3rdparty/chromaprint/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -CMakeFiles -CMakeCache.txt -Makefile -cmake_install.cmake -config.h -tests/all_tests -tools/fpgen -tools/fpcollect -*.exe -*.dll -*.so -*.a -tools/fpeval -tools/learn_filters -tools/decode -tools/chromagram -tools/resample -libchromaprint.pc -install_manifest.txt -tools/spectrogram -src/libchromaprint.so.* -*.vcxproj -*.vcxproj.* -*.dir -*.sln -*.suo -Debug -src/Debug -*.dll -*.lib -*.pdb -*.dll.manifest -*.exp -*.dylib -*.vcproj -*.user -*.ncb -src/Release diff --git a/3rdparty/chromaprint/CMakeLists.txt b/3rdparty/chromaprint/CMakeLists.txt index 12452de00..09247ac0b 100644 --- a/3rdparty/chromaprint/CMakeLists.txt +++ b/3rdparty/chromaprint/CMakeLists.txt @@ -103,7 +103,7 @@ if(WITH_FFTW3) message(STATUS "Using FFTW3 for FFT calculations") endif(WITH_FFTW3) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/chromaprint_config.h) add_subdirectory(src) diff --git a/3rdparty/chromaprint/src/avresample/avcodec.h b/3rdparty/chromaprint/src/avresample/avcodec.h index 9df0b22dd..e1e22c435 100644 --- a/3rdparty/chromaprint/src/avresample/avcodec.h +++ b/3rdparty/chromaprint/src/avresample/avcodec.h @@ -26,7 +26,7 @@ * modification -- Lennart */ #if defined(HAVE_CONFIG_H) -#include +#include #endif #include diff --git a/3rdparty/chromaprint/src/fft_lib.h b/3rdparty/chromaprint/src/fft_lib.h index dc621a3aa..5eaea8194 100644 --- a/3rdparty/chromaprint/src/fft_lib.h +++ b/3rdparty/chromaprint/src/fft_lib.h @@ -22,7 +22,7 @@ #define CHROMAPRINT_FFT_LIB_H_ #ifdef HAVE_CONFIG_H -#include +#include #endif #ifdef WITH_AVFFT diff --git a/3rdparty/chromaprint/src/foo.cpp b/3rdparty/chromaprint/src/foo.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/3rdparty/chromaprint/src/utils.h b/3rdparty/chromaprint/src/utils.h index 76fb2402b..4a8b7be05 100644 --- a/3rdparty/chromaprint/src/utils.h +++ b/3rdparty/chromaprint/src/utils.h @@ -22,7 +22,7 @@ #define CHROMAPRINT_UTILS_H_ #if defined(HAVE_CONFIG_H) -#include +#include #endif #include diff --git a/CMakeLists.txt b/CMakeLists.txt index 7af1936dd..8608e3aa6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,11 +60,11 @@ pkg_check_modules(CHROMAPRINT libchromaprint) pkg_check_modules(GIO gio-2.0) pkg_check_modules(GLIB glib-2.0) pkg_check_modules(GOBJECT gobject-2.0) -pkg_check_modules(GSTREAMER gstreamer-0.10) -pkg_check_modules(GSTREAMER_APP gstreamer-app-0.10) -pkg_check_modules(GSTREAMER_BASE gstreamer-base-0.10) -pkg_check_modules(GSTREAMER_CDDA gstreamer-cdda-0.10) -pkg_check_modules(GSTREAMER_TAG gstreamer-tag-0.10) +pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0) +pkg_check_modules(GSTREAMER_APP REQUIRED gstreamer-app-1.0) +pkg_check_modules(GSTREAMER_AUDIO REQUIRED gstreamer-audio-1.0) +pkg_check_modules(GSTREAMER_BASE REQUIRED gstreamer-base-1.0) +pkg_check_modules(GSTREAMER_TAG REQUIRED gstreamer-tag-1.0) pkg_check_modules(INDICATEQT indicate-qt) pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92) pkg_check_modules(LIBMTP libmtp>=1.0) @@ -151,8 +151,8 @@ include_directories(${TAGLIB_INCLUDE_DIRS}) include_directories(${QJSON_INCLUDE_DIRS}) include_directories(${GSTREAMER_INCLUDE_DIRS}) include_directories(${GSTREAMER_APP_INCLUDE_DIRS}) +include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS}) include_directories(${GSTREAMER_BASE_INCLUDE_DIRS}) -include_directories(${GSTREAMER_CDDA_INCLUDE_DIRS}) include_directories(${GSTREAMER_TAG_INCLUDE_DIRS}) include_directories(${GLIB_INCLUDE_DIRS}) include_directories(${GLIBCONFIG_INCLUDE_DIRS}) @@ -215,7 +215,8 @@ optional_component(SEAFILE ON "Seafile support" DEPENDS "Taglib 1.8" "TAGLIB_VERSION VERSION_GREATER 1.7.999" ) -optional_component(AUDIOCD ON "Devices: Audio CD support" +# Note: broken with gstreamer-1.0 - API changes in the audiocdsrc element. +optional_component(AUDIOCD OFF "Devices: Audio CD support" DEPENDS "libcdio" CDIO_FOUND ) diff --git a/cmake/SpotifyVersion.cmake b/cmake/SpotifyVersion.cmake index eaaf9a9b0..d25c7c2f6 100644 --- a/cmake/SpotifyVersion.cmake +++ b/cmake/SpotifyVersion.cmake @@ -1,3 +1,3 @@ # Increment this whenever the user needs to download a new blob # Remember to upload and sign the new version of the blob. -set(SPOTIFY_BLOB_VERSION 14) +set(SPOTIFY_BLOB_VERSION 15) diff --git a/debian/control b/debian/control index b96b75fe4..2ada39b1a 100644 --- a/debian/control +++ b/debian/control @@ -19,8 +19,8 @@ Build-Depends: debhelper (>= 7), qt4-dev-tools, libqt4-opengl-dev, cmake, - libgstreamer0.10-dev, - libgstreamer-plugins-base0.10-dev, + libgstreamer1.0-dev, + libgstreamer-plugins-base1.0-dev, libgpod-dev, libimobiledevice-dev, libplist-dev, @@ -43,10 +43,10 @@ Package: clementine Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, libsqlite3-0, - gstreamer0.10-plugins-base, - gstreamer0.10-plugins-good, - gstreamer0.10-alsa | gstreamer0.10-audiosink, - gstreamer0.10-plugins-ugly, + gstreamer1.0-plugins-base, + gstreamer1.0-plugins-good, + gstreamer1.0-alsa | gstreamer1.0-audiosink, + gstreamer1.0-plugins-ugly, libprojectm-data | projectm-data, libqca2-plugin-ossl Description: Modern music player and library organiser inspired by Amarok 1.4 diff --git a/dist/clementine.spec.in b/dist/clementine.spec.in index 9b31570b8..612f03ea5 100644 --- a/dist/clementine.spec.in +++ b/dist/clementine.spec.in @@ -23,15 +23,15 @@ Requires: libgpod protobuf-lite libcdio qjson qca-ossl sqlite Requires: gstreamer-plugins-ugly %ifarch x86_64 -Requires: gstreamer0.10(decoder-audio/x-vorbis)()(64bit) -Requires: gstreamer0.10(decoder-audio/x-flac)()(64bit) -Requires: gstreamer0.10(decoder-audio/x-speex)()(64bit) -Requires: gstreamer0.10(decoder-audio/x-wav)()(64bit) +Requires: gstreamer1.0(decoder-audio/x-vorbis)()(64bit) +Requires: gstreamer1.0(decoder-audio/x-flac)()(64bit) +Requires: gstreamer1.0(decoder-audio/x-speex)()(64bit) +Requires: gstreamer1.0(decoder-audio/x-wav)()(64bit) %else -Requires: gstreamer0.10(decoder-audio/x-vorbis) -Requires: gstreamer0.10(decoder-audio/x-flac) -Requires: gstreamer0.10(decoder-audio/x-speex) -Requires: gstreamer0.10(decoder-audio/x-wav) +Requires: gstreamer1.0(decoder-audio/x-vorbis) +Requires: gstreamer1.0(decoder-audio/x-flac) +Requires: gstreamer1.0(decoder-audio/x-speex) +Requires: gstreamer1.0(decoder-audio/x-wav) %endif %description diff --git a/ext/clementine-spotifyblob/mediapipeline.cpp b/ext/clementine-spotifyblob/mediapipeline.cpp index 0dfd8bc38..d7c973a4f 100644 --- a/ext/clementine-spotifyblob/mediapipeline.cpp +++ b/ext/clementine-spotifyblob/mediapipeline.cpp @@ -101,17 +101,18 @@ bool MediaPipeline::Init(int sample_rate, int channels) { gst_app_src_set_callbacks(appsrc_, &callbacks, this, nullptr); #if Q_BYTE_ORDER == Q_BIG_ENDIAN - const int endianness = G_BIG_ENDIAN; + static const char* format = "S16BE"; #elif Q_BYTE_ORDER == Q_LITTLE_ENDIAN - const int endianness = G_LITTLE_ENDIAN; + static const char* format = "S16LE"; #endif // Set caps - GstCaps* caps = gst_caps_new_simple( - "audio/x-raw-int", "endianness", G_TYPE_INT, endianness, "signed", - G_TYPE_BOOLEAN, TRUE, "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, - "rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, channels, - nullptr); + GstCaps* caps = gst_caps_new_simple("audio/x-raw", + "format", G_TYPE_STRING, format, + "rate", G_TYPE_INT, sample_rate, + "channels", G_TYPE_INT, channels, + "layout", G_TYPE_STRING, "interleaved", + nullptr); gst_app_src_set_caps(appsrc_, caps); gst_caps_unref(caps); @@ -129,16 +130,18 @@ bool MediaPipeline::Init(int sample_rate, int channels) { void MediaPipeline::WriteData(const char* data, qint64 length) { if (!is_initialised()) return; - GstBuffer* buffer = gst_buffer_new_and_alloc(length); + GstBuffer* buffer = gst_buffer_new_allocate(nullptr, length, nullptr); + GstMapInfo map_info; + gst_buffer_map(buffer, &map_info, GST_MAP_WRITE); - memcpy(GST_BUFFER_DATA(buffer), data, length); + memcpy(map_info.data, data, length); - GST_BUFFER_OFFSET(buffer) = offset_bytes_; - GST_BUFFER_TIMESTAMP(buffer) = offset_bytes_ * kNsecPerSec / byte_rate_; + gst_buffer_unmap(buffer, &map_info); + + GST_BUFFER_PTS(buffer) = offset_bytes_ * kNsecPerSec / byte_rate_; GST_BUFFER_DURATION(buffer) = length * kNsecPerSec / byte_rate_; offset_bytes_ += length; - GST_BUFFER_OFFSET_END(buffer) = offset_bytes_; gst_app_src_push_buffer(appsrc_, buffer); } diff --git a/gst/moodbar/01-fftw-plan-mutex.patch b/gst/moodbar/01-fftw-plan-mutex.patch deleted file mode 100644 index 8e4862939..000000000 --- a/gst/moodbar/01-fftw-plan-mutex.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0b62ebc38d1cc0202c6f57c4e096fa0b68a41baf Mon Sep 17 00:00:00 2001 -From: David Sansome -Date: Sun, 27 May 2012 17:00:32 +0100 -Subject: [PATCH] Protect calls to fftwf_plan_dft_r2c_1d with a mutex - ---- - plugin/gstfftwspectrum.c | 4 ++++ - 1 files changed, 4 insertions(+), 0 deletions(-) - -diff --git a/plugin/gstfftwspectrum.c b/plugin/gstfftwspectrum.c -index 147e606..f6e2427 100644 ---- a/plugin/gstfftwspectrum.c -+++ b/plugin/gstfftwspectrum.c -@@ -302,10 +302,14 @@ alloc_fftw_data (GstFFTWSpectrum *conv) - * outputs are the hermetian conjugates). This should be optimal for - * implementing filters. - */ -+ -+ static GStaticMutex mutex = G_STATIC_MUTEX_INIT; -+ g_static_mutex_lock(&mutex); - conv->fftw_plan - = fftwf_plan_dft_r2c_1d(conv->size, conv->fftw_in, - (fftwf_complex *) conv->fftw_out, - conv->hi_q ? FFTW_MEASURE : FFTW_ESTIMATE); -+ g_static_mutex_unlock(&mutex); - } - - --- -1.7.5.4 - diff --git a/gst/moodbar/CMakeLists.txt b/gst/moodbar/CMakeLists.txt index dc3934b7b..44c136253 100644 --- a/gst/moodbar/CMakeLists.txt +++ b/gst/moodbar/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.6) set(CMAKE_C_FLAGS "-Wall") -set(CMAKE_CXX_FLAGS "-Woverloaded-virtual -Wall") +set(CMAKE_CXX_FLAGS "-Woverloaded-virtual -Wall --std=c++0x") include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) @@ -11,9 +11,8 @@ include_directories(${GSTREAMER_INCLUDE_DIRS}) include_directories(${FFTW3_INCLUDE_DIR}) set(SOURCES - gstfftwspectrum.c - gstmoodbar.c - spectrum.c + gstfastspectrum.cpp + plugin.cpp ) add_library(gstmoodbar STATIC @@ -24,6 +23,7 @@ target_link_libraries(gstmoodbar ${GOBJECT_LIBRARIES} ${GLIB_LIBRARIES} ${GSTREAMER_LIBRARIES} + ${GSTREAMER_AUDIO_LIBRARIES} ${GSTREAMER_BASE_LIBRARIES} ${FFTW3_FFTW_LIBRARY} ) diff --git a/gst/moodbar/gstfastspectrum.cpp b/gst/moodbar/gstfastspectrum.cpp new file mode 100644 index 000000000..2b57ccacc --- /dev/null +++ b/gst/moodbar/gstfastspectrum.cpp @@ -0,0 +1,535 @@ +/* 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 "gstfastspectrum.h" + +GST_DEBUG_CATEGORY_STATIC (gst_fastspectrum_debug); +#define GST_CAT_DEFAULT gst_fastspectrum_debug + +/* elementfactory information */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +# define FORMATS "{ S16LE, S24LE, S32LE, F32LE, F64LE }" +#else +# define FORMATS "{ S16BE, S24BE, S32BE, F32BE, F64BE }" +#endif + +#define ALLOWED_CAPS \ + GST_AUDIO_CAPS_MAKE (FORMATS) ", " \ + "layout = (string) interleaved, " \ + "channels = 1" + +/* Spectrum properties */ +#define DEFAULT_INTERVAL (GST_SECOND / 10) +#define DEFAULT_BANDS 128 + +enum { + PROP_0, + PROP_INTERVAL, + PROP_BANDS +}; + +#define gst_fastspectrum_parent_class parent_class +G_DEFINE_TYPE (GstFastSpectrum, gst_fastspectrum, GST_TYPE_AUDIO_FILTER); + +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 * in); +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; + + 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, + GParamFlags(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, + GParamFlags(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 "); + + caps = gst_caps_from_string (ALLOWED_CAPS); + gst_audio_filter_class_add_pad_templates (filter_class, caps); + gst_caps_unref (caps); +} + +static void +gst_fastspectrum_init (GstFastSpectrum * spectrum) +{ + spectrum->interval = DEFAULT_INTERVAL; + spectrum->bands = DEFAULT_BANDS; + + spectrum->channel_data_initialised = 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]; + spectrum->plan = fftw_plan_dft_r2c_1d( + nfft, + spectrum->fft_input, + spectrum->fft_output, + FFTW_ESTIMATE); + spectrum->channel_data_initialised = true; +} + +static void +gst_fastspectrum_free_channel_data (GstFastSpectrum * spectrum) +{ + if (spectrum->channel_data_initialised) { + 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_initialised = 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 = GST_FASTSPECTRUM (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 = GST_FASTSPECTRUM (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 = GST_FASTSPECTRUM (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 = GST_FASTSPECTRUM (trans); + + gst_fastspectrum_reset_state (spectrum); + + return TRUE; +} + +static gboolean +gst_fastspectrum_stop (GstBaseTransform * trans) +{ + GstFastSpectrum *spectrum = GST_FASTSPECTRUM (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) +{ + guint j, ip = 0; + gfloat *in = (gfloat *) _in; + + for (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) +{ + guint j, ip = 0; + gdouble *in = (gdouble *) _in; + + for (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) +{ + guint j, ip = 0; + gint32 *in = (gint32 *) _in; + + for (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) +{ + guint j; + + for (j = 0; j < len; j++) { +#if G_BYTE_ORDER == G_BIG_ENDIAN + gint32 value = GST_READ_UINT24_BE (_in); +#else + gint32 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) +{ + guint j, ip = 0; + gint16 *in = (gint16 *) _in; + + for (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 = GST_FASTSPECTRUM (base); + GstFastSpectrumInputData input_data = NULL; + + 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 i; + guint bands = spectrum->bands; + guint nfft = 2 * bands - 2; + + for (i = 0; i < nfft; i++) + spectrum->fft_input[i] = + spectrum->input_ring_buffer[(input_pos + i) % nfft]; + + fftw_execute(spectrum->plan); + + gdouble val; + /* Calculate magnitude in db */ + for (i = 0; i < bands; i++) { + 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 = GST_FASTSPECTRUM (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 = (1UL << ((bps << 3) - 1)) - 1; + guint bands = spectrum->bands; + guint nfft = 2 * bands - 2; + guint input_pos; + GstMapInfo map; + const guint8 *data; + gsize size; + guint fft_todo, msg_todo, block_size; + gboolean have_full_interval; + GstFastSpectrumInputData 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_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_initialised) { + 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 */ + 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; + + /* 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; + + 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 (uint i = 0; i < spectrum->bands; i++) { + spectrum->spect_magnitude[i] /= spectrum->num_fft; + } + + spectrum->output_callback(spectrum->spect_magnitude, 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/gst/moodbar/gstfastspectrum.h b/gst/moodbar/gstfastspectrum.h new file mode 100644 index 000000000..6147f3eb9 --- /dev/null +++ b/gst/moodbar/gstfastspectrum.h @@ -0,0 +1,93 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <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. + */ + +// Adapted from gstspectrum for Clementine with the following changes: +// - Uses fftw instead of kiss fft (2x faster). +// - Hardcoded to 1 channel (use an audioconvert element to do the work +// instead, simplifies this code a lot). +// - 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_ + +#include + +#include +#include +#include + +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)) + +using GstFastSpectrumInputData = void(*)(const guint8* in, double* out, + guint len, double max_value, guint op, guint nfft); + +using OutputCallback = std::function; + +struct GstFastSpectrum { + GstAudioFilter parent; + + /* properties */ + guint64 interval; /* how many nanoseconds between emits */ + guint64 frames_per_interval; /* how many frames per interval */ + guint64 frames_todo; + guint bands; /* number of spectrum bands */ + gboolean multi_channel; /* send separate channel results */ + + guint64 num_frames; /* frame count (1 sample per channel) + * since last emit */ + guint64 num_fft; /* number of FFTs since last emit */ + GstClockTime message_ts; /* starttime for next message */ + + /* */ + bool channel_data_initialised; + double* input_ring_buffer; + double* fft_input; + fftw_complex* fft_output; + double* spect_magnitude; + fftw_plan plan; + + guint input_pos; + guint64 error_per_interval; + guint64 accumulated_error; + + GMutex lock; + + GstFastSpectrumInputData input_data; + + OutputCallback output_callback; +}; + +struct GstFastSpectrumClass { + GstAudioFilterClass parent_class; +}; + +GType gst_fastspectrum_get_type (void); + +G_END_DECLS + +#endif // GST_MOODBAR_FASTSPECTRUM_H_ diff --git a/gst/moodbar/gstfftwspectrum.c b/gst/moodbar/gstfftwspectrum.c deleted file mode 100644 index 2fa3e8570..000000000 --- a/gst/moodbar/gstfftwspectrum.c +++ /dev/null @@ -1,641 +0,0 @@ -/* GStreamer FFTW-based signal-to-spectrum converter - * Copyright (C) 2006 Joseph Rabinoff - */ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -/** - * SECTION:element-fftwspectrum - * - * - * Example launch line - * - * - * gst-launch audiotestsrc ! audioconvert ! fftwspectrum ! fftwunspectrum ! audioconvert ! alsasink - * - * - * - */ - -/* This is a simple plugin to take an audio signal and return its - * Fourier transform, using fftw3. It takes a specified number N of - * samples and returns the first N/2+1 (complex) Fourier transform - * values (the other half of the values being the complex conjugates - * of the first). The modulus of these values correspond to the - * strength of the signal in their various bands, and the phase gives - * information about the phase of the signal. The step by which the - * transform increments is also variable, so it can return redundant - * data (to reduce artifacts when converting back into a signal). - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include - -#include "gstfftwspectrum.h" -#include "spectrum.h" - -GST_DEBUG_CATEGORY (gst_fftwspectrum_debug); -#define GST_CAT_DEFAULT gst_fftwspectrum_debug - -/* Filter signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -/* The size and step arguments are actually only default values - * used to fixate the size and step properties of the source cap. - */ -enum -{ - ARG_0, - ARG_DEF_SIZE, - ARG_DEF_STEP, - ARG_HIQUALITY -}; - -#define DEF_SIZE_DEFAULT 1024 -#define DEF_STEP_DEFAULT 512 -#define HIQUALITY_DEFAULT TRUE - -static GstStaticPadTemplate sink_factory - = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS - ( SPECTRUM_SIGNAL_CAPS ) - ); - -/* See spectrum.h for a definition of the frequency caps */ -static GstStaticPadTemplate src_factory - = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS - ( SPECTRUM_FREQ_CAPS ) - ); - -GST_BOILERPLATE (GstFFTWSpectrum, gst_fftwspectrum, GstElement, - GST_TYPE_ELEMENT); - -static void gst_fftwspectrum_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec); -static void gst_fftwspectrum_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec); - -static gboolean gst_fftwspectrum_set_sink_caps (GstPad *pad, GstCaps *caps); -static gboolean gst_fftwspectrum_set_src_caps (GstPad *pad, GstCaps *caps); -static void gst_fftwspectrum_fixatecaps (GstPad *pad, GstCaps *caps); -static GstCaps *gst_fftwspectrum_getcaps (GstPad *pad); - -static GstFlowReturn gst_fftwspectrum_chain (GstPad *pad, GstBuffer *buf); -static GstStateChangeReturn gst_fftwspectrum_change_state (GstElement *element, - GstStateChange transition); - - -#define OUTPUT_SIZE(conv) (((conv)->size/2+1)*sizeof(fftw_complex)) - - -/***************************************************************/ -/* GObject boilerplate stuff */ -/***************************************************************/ - - -static void -gst_fftwspectrum_base_init (gpointer gclass) -{ - static GstElementDetails element_details = - { - "FFTW-based Fourier transform", - "Filter/Converter/Spectrum", - "Convert a raw audio stream into a frequency spectrum", - "Joe Rabinoff " - }; - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_factory)); - gst_element_class_set_details (element_class, &element_details); -} - -/* initialize the plugin's class */ -static void -gst_fftwspectrum_class_init (GstFFTWSpectrumClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - gobject_class->set_property = gst_fftwspectrum_set_property; - gobject_class->get_property = gst_fftwspectrum_get_property; - - g_object_class_install_property (gobject_class, ARG_DEF_SIZE, - g_param_spec_int ("def-size", "Default Size", - "Apply a Fourier transform to this many samples at a time (default value)", - 1, G_MAXINT32, DEF_SIZE_DEFAULT, G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, ARG_DEF_STEP, - g_param_spec_int ("def-step", "Default Step", - "Advance the stream this many samples each time (default value)", - 1, G_MAXINT32, DEF_STEP_DEFAULT, G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, ARG_HIQUALITY, - g_param_spec_boolean ("hiquality", "High Quality", - "Use a more time-consuming, higher quality algorithm chooser", - HIQUALITY_DEFAULT, G_PARAM_READWRITE)); - - gstelement_class->change_state - = GST_DEBUG_FUNCPTR (gst_fftwspectrum_change_state); - - g_mutex_init(&klass->mutex); -} - -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ -static void -gst_fftwspectrum_init (GstFFTWSpectrum * conv, - GstFFTWSpectrumClass * gclass) -{ - GstElementClass *klass = GST_ELEMENT_GET_CLASS (conv); - - conv->sinkpad = - gst_pad_new_from_template - (gst_element_class_get_pad_template (klass, "sink"), "sink"); - gst_pad_set_setcaps_function (conv->sinkpad, - GST_DEBUG_FUNCPTR (gst_fftwspectrum_set_sink_caps)); - gst_pad_set_getcaps_function (conv->sinkpad, - GST_DEBUG_FUNCPTR (gst_fftwspectrum_getcaps)); - gst_pad_set_chain_function (conv->sinkpad, - GST_DEBUG_FUNCPTR (gst_fftwspectrum_chain)); - - conv->srcpad = - gst_pad_new_from_template - (gst_element_class_get_pad_template (klass, "src"), "src"); - gst_pad_set_setcaps_function (conv->srcpad, - GST_DEBUG_FUNCPTR (gst_fftwspectrum_set_src_caps)); - gst_pad_set_getcaps_function (conv->srcpad, - GST_DEBUG_FUNCPTR (gst_fftwspectrum_getcaps)); - gst_pad_set_fixatecaps_function (conv->srcpad, - GST_DEBUG_FUNCPTR (gst_fftwspectrum_fixatecaps)); - - - gst_element_add_pad (GST_ELEMENT (conv), conv->sinkpad); - gst_element_add_pad (GST_ELEMENT (conv), conv->srcpad); - - /* These are set once the (source) capabilities are determined */ - conv->rate = 0; - conv->size = 0; - conv->step = 0; - - /* These are set when we change to READY */ - conv->fftw_in = NULL; - conv->fftw_out = NULL; - conv->fftw_plan = NULL; - - /* These are set when we start receiving data */ - conv->samples = NULL; - conv->numsamples = 0; - conv->timestamp = 0; - conv->offset = 0; - - /* Properties */ - conv->def_size = DEF_SIZE_DEFAULT; - conv->def_step = DEF_STEP_DEFAULT; - conv->hi_q = HIQUALITY_DEFAULT; - - conv->mutex = &gclass->mutex; -} - -static void -gst_fftwspectrum_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstFFTWSpectrum *conv = GST_FFTWSPECTRUM (object); - - switch (prop_id) - { - case ARG_DEF_SIZE: - conv->def_size = g_value_get_int (value); - break; - case ARG_DEF_STEP: - conv->def_step = g_value_get_int (value); - break; - case ARG_HIQUALITY: - conv->hi_q = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_fftwspectrum_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstFFTWSpectrum *conv = GST_FFTWSPECTRUM (object); - - switch (prop_id) - { - case ARG_DEF_SIZE: - g_value_set_int (value, conv->def_size); - break; - case ARG_DEF_STEP: - g_value_set_int (value, conv->def_step); - break; - case ARG_HIQUALITY: - g_value_set_boolean (value, conv->hi_q); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -/* Allocate and deallocate fftw state data */ -static void -free_fftw_data (GstFFTWSpectrum *conv) -{ - if(conv->fftw_plan != NULL) - fftw_destroy_plan (conv->fftw_plan); - if(conv->fftw_in != NULL) - fftw_free (conv->fftw_in); - if(conv->fftw_out != NULL) - fftw_free (conv->fftw_out); - - conv->fftw_in = NULL; - conv->fftw_out = NULL; - conv->fftw_plan = NULL; -} - -static void -alloc_fftw_data (GstFFTWSpectrum *conv) -{ - free_fftw_data (conv); - - GST_DEBUG ("Allocating data for size = %d and step = %d", - conv->size, conv->step); - - conv->fftw_in = (double *) fftw_malloc (sizeof(double) * conv->size); - conv->fftw_out = (double *) fftw_malloc (OUTPUT_SIZE (conv)); - - /* We use the simplest real-to-complex algorithm, which takes n real - * inputs and returns floor(n/2) + 1 complex outputs (the other n/2 - * outputs are the hermetian conjugates). This should be optimal for - * implementing filters. - */ - - g_mutex_lock(conv->mutex); - conv->fftw_plan - = fftw_plan_dft_r2c_1d(conv->size, conv->fftw_in, - (fftw_complex *) conv->fftw_out, - conv->hi_q ? FFTW_MEASURE : FFTW_ESTIMATE); - g_mutex_unlock(conv->mutex); -} - - -/***************************************************************/ -/* Capabilities negotiation */ -/***************************************************************/ - -/* The input and output capabilities are only related by the "rate" - * parameter, which is propagated so that an audio signal can be - * reconstructed eventually. This module does no rate conversion. - * - * The way I understand it, there are two times when caps negotiation - * takes place: (1) when a sink pad receives either its first buffer, - * or a buffer with a new caps type, and (2) when a source pad request - * a buffer from something downstream, and the returned allocated - * buffer has different caps from the ones already negotiated. In the - * first case, _set_sink_caps is called, and in the second, _set_src_caps - * is called. - * When (1) occurs, we remember the rate (the only variable parameter - * in the source) and set the source caps. Then _set_src_caps is called. - * In _set_src_caps, we check that the rate hasn't changed, and figure out - * or remember appropriate size and step attributes. If _set_src_caps is - * called from _set_sink_caps, this completes our setting up our internal - * configuration; if it is called from (2), we reconfigure just the source - * part of the internal configuration. - */ - -static gboolean -gst_fftwspectrum_set_sink_caps (GstPad * pad, GstCaps * caps) -{ - GstFFTWSpectrum *conv; - GstCaps *srccaps, *newsrccaps; - GstStructure *newstruct; - gint rate; - gboolean res; - - conv = GST_FFTWSPECTRUM (gst_pad_get_parent (pad)); - - srccaps = gst_pad_get_allowed_caps (conv->srcpad); - newsrccaps = gst_caps_copy_nth (srccaps, 0); - gst_caps_unref (srccaps); - - newstruct = gst_caps_get_structure (caps, 0); - if (!gst_structure_get_int (newstruct, "rate", &rate)) - { - gst_caps_unref (newsrccaps); - gst_object_unref (conv); - return FALSE; - } - - /* Fixate the source caps with the given rate */ - gst_caps_set_simple (newsrccaps, "rate", G_TYPE_INT, rate, NULL); - gst_pad_fixate_caps (conv->srcpad, newsrccaps); - conv->rate = rate; - res = gst_pad_set_caps (conv->srcpad, newsrccaps); - if (!res) - conv->rate = 0; - - gst_caps_unref (newsrccaps); - gst_object_unref (conv); - - return res; -} - -static gboolean -gst_fftwspectrum_set_src_caps (GstPad * pad, GstCaps * caps) -{ - GstFFTWSpectrum *conv; - gboolean res = FALSE; - GstStructure *newstruct; - gint rate; - - conv = GST_FFTWSPECTRUM (gst_pad_get_parent (pad)); - - newstruct = gst_caps_get_structure (caps, 0); - if (!gst_structure_get_int (newstruct, "rate", &rate)) - goto out; - - /* Assume caps negotiation has already taken place */ - if (rate == conv->rate) - { - gint size, step; - - if (!gst_structure_get_int (newstruct, "size", &size)) - goto out; - if (!gst_structure_get_int (newstruct, "step", &step)) - goto out; - - if (conv->size != size || conv->step != step) - { - conv->size = size; - conv->step = step; - - /* Re-allocate the fftw data */ - if (GST_STATE (GST_ELEMENT (conv)) >= GST_STATE_READY) - alloc_fftw_data (conv); - } - - res = TRUE; - } - - out: - gst_object_unref (conv); - - return res; -} - - -/* The only thing that can constrain the caps is the rate. */ -static GstCaps * -gst_fftwspectrum_getcaps (GstPad *pad) -{ - GstFFTWSpectrum *conv; - GstCaps *tmplcaps; - - conv = GST_FFTWSPECTRUM (gst_pad_get_parent (pad)); - tmplcaps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); - - if(conv->rate != 0) - { - /* Assumes the template caps are simple */ - gst_caps_set_simple (tmplcaps, "rate", G_TYPE_INT, conv->rate, NULL); - } - - gst_object_unref (conv); - - return tmplcaps; -} - - -/* This is called when the source pad needs to choose its capabilities - * when it has a choice and nobody's forcing its hand. In this case - * we take our hint from the def_size and def_step properties. - */ -static void -gst_fftwspectrum_fixatecaps (GstPad *pad, GstCaps *caps) -{ - GstFFTWSpectrum *conv; - GstStructure *s; - const GValue *val; - - conv = GST_FFTWSPECTRUM (gst_pad_get_parent (pad)); - s = gst_caps_get_structure (caps, 0); - - val = gst_structure_get_value (s, "size"); - if (val == NULL) - gst_caps_set_simple (caps, "size", G_TYPE_INT, conv->def_size, NULL); - else if (G_VALUE_TYPE (val) == GST_TYPE_INT_RANGE) - { - gint sizemin, sizemax; - sizemin = gst_value_get_int_range_min (val); - sizemax = gst_value_get_int_range_max (val); - gst_caps_set_simple (caps, "size", G_TYPE_INT, - CLAMP (conv->def_size, sizemin, sizemax), NULL); - } - /* else it should be already fixed */ - - val = gst_structure_get_value (s, "step"); - if (val == NULL) - gst_caps_set_simple (caps, "step", G_TYPE_INT, conv->def_step, NULL); - else if (G_VALUE_TYPE (val) == GST_TYPE_INT_RANGE) - { - gint stepmin, stepmax; - stepmin = gst_value_get_int_range_min (val); - stepmax = gst_value_get_int_range_max (val); - gst_caps_set_simple (caps, "step", G_TYPE_INT, - CLAMP (conv->def_step, stepmin, stepmax), NULL); - } - /* else it should be already fixed */ - - /* Assume rate is already fixed (if not it'll be fixed by default) */ - - gst_object_unref (conv); -} - - -/***************************************************************/ -/* Actual conversion */ -/***************************************************************/ - - -static GstStateChangeReturn -gst_fftwspectrum_change_state (GstElement * element, - GstStateChange transition) -{ - GstFFTWSpectrum *conv = GST_FFTWSPECTRUM (element); - GstStateChangeReturn res; - - switch (transition) - { - case GST_STATE_CHANGE_NULL_TO_READY: - alloc_fftw_data (conv); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - conv->samples = (gdouble *) g_malloc (sizeof(gdouble)); - conv->numsamples = 0; - conv->timestamp = 0; - conv->offset = 0; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - res = parent_class->change_state (element, transition); - - switch (transition) - { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - g_free(conv->samples); - conv->samples = NULL; - conv->numsamples = 0; - conv->timestamp = 0; - conv->offset = 0; - break; - case GST_STATE_CHANGE_READY_TO_NULL: - free_fftw_data (conv); - break; - default: - break; - } - - return res; -} - - -/* Adds the samples contained in buf to the end of conv->samples, - * updating conv->numsamples. - */ -static void -push_samples (GstFFTWSpectrum *conv, GstBuffer *buf) -{ - gint newsamples = GST_BUFFER_SIZE (buf) / sizeof (gdouble); - gint oldsamples = conv->numsamples; - - conv->numsamples += newsamples; - conv->samples = g_realloc (conv->samples, conv->numsamples * sizeof (gdouble)); - memcpy (&conv->samples[oldsamples], GST_BUFFER_DATA (buf), - newsamples * sizeof (gdouble)); - - /* GST_LOG ("Added %d samples", newsamples); */ -} - -/* This basically does the opposite of push_samples, but takes samples - * off the front. - */ -static void -shift_samples (GstFFTWSpectrum *conv, gint toshift) -{ - gdouble *oldsamples = conv->samples; - - conv->numsamples -= toshift; - conv->samples = g_malloc (MAX (conv->numsamples, 1) * sizeof (double)); - memcpy (conv->samples, &oldsamples[toshift], - conv->numsamples * sizeof (gdouble)); - g_free (oldsamples); - - /* Fix the timestamp and offset */ - conv->timestamp - += gst_util_uint64_scale_int (GST_SECOND, toshift, conv->rate); - conv->offset += toshift; - - /* GST_LOG ("Disposed of %d samples (time: %" GST_TIME_FORMAT " offset: %llu)", - toshift, GST_TIME_ARGS(conv->timestamp), conv->offset); */ -} - - -/* This function queues samples until there are at least - * max (conv->size, conv->step) samples to process. We - * then process samples in chunks of conv->size and increment - * by conv->step. - */ -static GstFlowReturn -gst_fftwspectrum_chain (GstPad * pad, GstBuffer * buf) -{ - GstFFTWSpectrum *conv; - GstBuffer *outbuf; - GstFlowReturn res = GST_FLOW_OK; - - conv = GST_FFTWSPECTRUM (gst_pad_get_parent (pad)); - - push_samples (conv, buf); - gst_buffer_unref (buf); - - while (conv->numsamples >= MAX (conv->size, conv->step)) - { - res = gst_pad_alloc_buffer_and_set_caps - (conv->srcpad, conv->offset, OUTPUT_SIZE (conv), - GST_PAD_CAPS(conv->srcpad), &outbuf); - if (res != GST_FLOW_OK) - break; - - GST_BUFFER_SIZE (outbuf) = OUTPUT_SIZE (conv); - GST_BUFFER_OFFSET (outbuf) = conv->offset; - GST_BUFFER_OFFSET_END (outbuf) = conv->offset + conv->step; - GST_BUFFER_TIMESTAMP (outbuf) = conv->timestamp; - GST_BUFFER_DURATION (outbuf) - = gst_util_uint64_scale_int (GST_SECOND, conv->step, conv->rate); - - /* Do the Fourier transform */ - memcpy (conv->fftw_in, conv->samples, conv->size * sizeof (double)); - fftw_execute (conv->fftw_plan); - { /* Normalize */ - gint i; - gfloat root = sqrtf (conv->size); - for (i = 0; i < 2*(conv->size/2+1); ++i) - conv->fftw_out[i] /= root; - } - memcpy (GST_BUFFER_DATA (outbuf), conv->fftw_out, OUTPUT_SIZE (conv)); - - res = gst_pad_push (conv->srcpad, outbuf); - - shift_samples (conv, conv->step); - - if (res != GST_FLOW_OK) - break; - } - - gst_object_unref (conv); - - return res; -} - diff --git a/gst/moodbar/gstfftwspectrum.h b/gst/moodbar/gstfftwspectrum.h deleted file mode 100644 index f06d2b0e2..000000000 --- a/gst/moodbar/gstfftwspectrum.h +++ /dev/null @@ -1,72 +0,0 @@ -/* GStreamer FFTW-based signal-to-spectrum converter - * Copyright (C) 2006 Joseph Rabinoff - */ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - - -#ifndef __GST_FFTWSPECTRUM_H__ -#define __GST_FFTWSPECTRUM_H__ - -#include -#include - -G_BEGIN_DECLS - -/* #defines don't like whitespacey bits */ -#define GST_TYPE_FFTWSPECTRUM \ - (gst_fftwspectrum_get_type()) -#define GST_FFTWSPECTRUM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFTWSPECTRUM,GstFFTWSpectrum)) -#define GST_FFTWSPECTRUM_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFTWSPECTRUM,GstFFTWSpectrumClass)) - -typedef struct _GstFFTWSpectrum GstFFTWSpectrum; -typedef struct _GstFFTWSpectrumClass GstFFTWSpectrumClass; - -struct _GstFFTWSpectrum -{ - GstElement element; - - GstPad *sinkpad, *srcpad; - - /* Stream data */ - gint rate, size, step; - - /* Actual queued (incoming) stream */ - gdouble *samples; - gint numsamples; - GstClockTime timestamp; /* Timestamp of the first sample */ - guint64 offset; /* Offset of the first sample */ - - /* State data for fftw */ - double *fftw_in; - double *fftw_out; - fftw_plan fftw_plan; - - /* Properties */ - gint32 def_size, def_step; - gboolean hi_q; - - GMutex* mutex; -}; - -struct _GstFFTWSpectrumClass -{ - GstElementClass parent_class; - - GMutex mutex; -}; - -GType gst_fftwspectrum_get_type (void); - -G_END_DECLS - -#endif /* __GST_FFTWSPECTRUM_H__ */ diff --git a/gst/moodbar/gstmoodbar.c b/gst/moodbar/gstmoodbar.c deleted file mode 100644 index 9b42aeed0..000000000 --- a/gst/moodbar/gstmoodbar.c +++ /dev/null @@ -1,673 +0,0 @@ -/* GStreamer spectrum analysis toy - * Copyright (C) 2006 Joseph Rabinoff - * Some code copyright (C) 2005 Gav Wood - */ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -/** - * SECTION:element-moodbar - * - * - * Example launch line - * - * - * gst-launch filesrc location=test.mp3 ! mad ! audioconvert ! fftwspectrum ! moodbar height=50 ! pngenc ! filesink location=test.png - * - * - * - */ - -/* This plugin is based on the Moodbar code in Amarok version 1.4.0a, - * written by Gav Wood. The algorithm is basically the same as the - * one applied there, and the normalizing code below is taken directly - * from Gav Wood's Exscalibar package. - */ - -/* This plugin takes a frequency-domain stream, does some simple - * analysis, and returns a string of (unsigned char) rgb triples - * that represent the magnitude of various sections of the stream. - * Since we have to perform some normalization, we queue up all - * of our analysis until we get an EOS event, at which point we - * normalize and do the output. If a max-width is specified, the - * output is scaled down to the desired width if necessary. - */ - -/* More precisely, the analysis performed is as follows: - * (1) the spectrum is broken into 24 parts, called "bark bands" - * (Gav's terminology), as given in bark_bands below - * (2) we compute the size of the first 8 bark bands and store - * that as the "red" component; similarly for blue and green - * (3) after receiving an EOS, we normalize all of the analysis - * done in (1) and (2) and return a stream of rgb triples - * (application/x-raw-rgb) - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include - -#include "gstmoodbar.h" -#include "spectrum.h" - -GST_DEBUG_CATEGORY (gst_moodbar_debug); -#define GST_CAT_DEFAULT gst_moodbar_debug - -/* Filter signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - ARG_0, - ARG_HEIGHT, - ARG_MAX_WIDTH -}; - -static GstStaticPadTemplate sink_factory - = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS - ( SPECTRUM_FREQ_CAPS ) - ); - -static GstStaticPadTemplate src_factory - = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS - ( "video/x-raw-rgb, " - "bpp = (int) 24, " - "depth = (int) 24, " - "height = (int) [ 1, MAX ], " - "width = (int) [ 1, MAX ], " - "framerate = (fraction) 0/1" - ) - ); - -GST_BOILERPLATE (GstMoodbar, gst_moodbar, GstElement, - GST_TYPE_ELEMENT); - -static void gst_moodbar_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec); -static void gst_moodbar_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec); - -static gboolean gst_moodbar_set_sink_caps (GstPad *pad, GstCaps *caps); -static gboolean gst_moodbar_sink_event (GstPad *pad, GstEvent *event); - -static GstFlowReturn gst_moodbar_chain (GstPad *pad, GstBuffer *buf); -static GstStateChangeReturn gst_moodbar_change_state (GstElement *element, - GstStateChange transition); - -static void gst_moodbar_finish (GstMoodbar *mood); - -/* This is a failsafe so we don't eat up all of a computer's memory - * if we hit an endless stream. */ -#define MAX_TRIPLES (1024*1024*4) - -#define NUMFREQS(mood) ((mood)->size/2+1) - -/* Allocate mood->r, mood->g, and mood->b in chunks of this many */ -#define FRAME_CHUNK 1000 - -/* Default height of the output image */ -#define HEIGHT_DEFAULT 1 - -/* Default max-width of the output image, or 0 for no rescaling */ -#define MAX_WIDTH_DEFAULT 0 - -/* We use this table to break up the incoming spectrum into segments */ -static const guint bark_bands[24] - = { 100, 200, 300, 400, 510, 630, 770, 920, - 1080, 1270, 1480, 1720, 2000, 2320, 2700, 3150, - 3700, 4400, 5300, 6400, 7700, 9500, 12000, 15500 }; - - -/***************************************************************/ -/* GObject boilerplate stuff */ -/***************************************************************/ - - -static void -gst_moodbar_base_init (gpointer gclass) -{ - static GstElementDetails element_details = - { - "Moodbar analyzer", - "Filter/Converter/Moodbar", - "Convert a spectrum into a stream of (uchar) rgb triples representing its \"mood\"", - "Joe Rabinoff " - }; - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_factory)); - gst_element_class_set_details (element_class, &element_details); -} - -/* initialize the plugin's class */ -static void -gst_moodbar_class_init (GstMoodbarClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - gobject_class->set_property = gst_moodbar_set_property; - gobject_class->get_property = gst_moodbar_get_property; - - g_object_class_install_property (gobject_class, ARG_HEIGHT, - g_param_spec_int ("height", "Image height", - "The height of the resulting raw image", - 1, G_MAXINT32, HEIGHT_DEFAULT, G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, ARG_MAX_WIDTH, - g_param_spec_int ("max-width", "Image maximum width", - "The maximum width of the resulting raw image, or 0 for no rescaling", - 0, G_MAXINT32, MAX_WIDTH_DEFAULT, G_PARAM_READWRITE)); - - gstelement_class->change_state - = GST_DEBUG_FUNCPTR (gst_moodbar_change_state); -} - -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ -static void -gst_moodbar_init (GstMoodbar *mood, GstMoodbarClass *gclass) -{ - GstElementClass *klass = GST_ELEMENT_GET_CLASS (mood); - - mood->sinkpad = - gst_pad_new_from_template - (gst_element_class_get_pad_template (klass, "sink"), "sink"); - gst_pad_set_setcaps_function (mood->sinkpad, - GST_DEBUG_FUNCPTR (gst_moodbar_set_sink_caps)); - gst_pad_set_event_function (mood->sinkpad, - GST_DEBUG_FUNCPTR (gst_moodbar_sink_event)); - gst_pad_set_chain_function (mood->sinkpad, - GST_DEBUG_FUNCPTR (gst_moodbar_chain)); - - mood->srcpad = - gst_pad_new_from_template - (gst_element_class_get_pad_template (klass, "src"), "src"); - - - gst_element_add_pad (GST_ELEMENT (mood), mood->sinkpad); - gst_element_add_pad (GST_ELEMENT (mood), mood->srcpad); - - /* These are set once the (sink) capabilities are determined */ - mood->rate = 0; - mood->size = 0; - mood->barkband_table = NULL; - - /* These are allocated when we change to PAUSED */ - mood->r = NULL; - mood->g = NULL; - mood->b = NULL; - mood->numframes = 0; - - /* Property */ - mood->height = HEIGHT_DEFAULT; - mood->max_width = MAX_WIDTH_DEFAULT; -} - - -static void gst_moodbar_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - GstMoodbar *mood = GST_MOODBAR (object); - - switch (prop_id) - { - case ARG_HEIGHT: - mood->height = (guint) g_value_get_int (value); - break; - case ARG_MAX_WIDTH: - mood->max_width = (guint) g_value_get_int (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - -} - - -static void gst_moodbar_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - GstMoodbar *mood = GST_MOODBAR (object); - - switch (prop_id) - { - case ARG_HEIGHT: - g_value_set_int (value, (int) mood->height); - break; - case ARG_MAX_WIDTH: - g_value_set_int (value, (int) mood->max_width); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - - - -/***************************************************************/ -/* Pad handling */ -/***************************************************************/ - - -/* This calculates a table that caches which bark band slot each - * incoming band is supposed to go in. */ -static void -calc_barkband_table (GstMoodbar *mood) -{ - guint i; - guint barkband = 0; - - /* Avoid divide-by-zero */ - if (!mood->size || !mood->rate) - return; - - if (mood->barkband_table) - g_free (mood->barkband_table); - - mood->barkband_table = g_malloc (NUMFREQS (mood) * sizeof (guint)); - - for (i = 0; i < NUMFREQS (mood); ++i) - { - if (barkband < 23 && - (guint) GST_SPECTRUM_BAND_FREQ (i, mood->size, mood->rate) - >= bark_bands[barkband]) - barkband++; - - mood->barkband_table[i] = barkband; - - /* - GST_LOG ("Band %d (frequency %f) -> barkband %d (frequency %d)", - i, GST_SPECTRUM_BAND_FREQ (i, mood->size, mood->rate), - barkband, bark_bands[barkband]); - */ - } -} - - -/* Setting the sink caps just gets the rate and size parameters. - * Note that we do not support upstream caps renegotiation, since - * we could only possibly scale the height anyway. - */ - -static gboolean -gst_moodbar_set_sink_caps (GstPad *pad, GstCaps *caps) -{ - GstMoodbar *mood; - GstStructure *newstruct; - gint rate, size; - gboolean res = FALSE; - - mood = GST_MOODBAR (gst_pad_get_parent (pad)); - - newstruct = gst_caps_get_structure (caps, 0); - if (!gst_structure_get_int (newstruct, "rate", &rate) || - !gst_structure_get_int (newstruct, "size", &size)) - goto out; - - res = TRUE; - - mood->rate = rate; - mood->size = (guint) size; - calc_barkband_table (mood); - - out: - gst_object_unref (mood); - - return res; -} - - -static gboolean -gst_moodbar_sink_event (GstPad *pad, GstEvent *event) -{ - GstMoodbar *mood; - gboolean res = TRUE; - - mood = GST_MOODBAR (gst_pad_get_parent (pad)); - - if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) - gst_moodbar_finish (mood); - - res = gst_pad_push_event (mood->srcpad, event); - gst_object_unref (mood); - - return res; -} - - -/***************************************************************/ -/* Actual analysis */ -/***************************************************************/ - - -static GstStateChangeReturn -gst_moodbar_change_state (GstElement *element, GstStateChange transition) -{ - GstMoodbar *mood = GST_MOODBAR (element); - GstStateChangeReturn res; - - switch (transition) - { - case GST_STATE_CHANGE_NULL_TO_READY: - calc_barkband_table (mood); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - mood->r = (gdouble *) g_malloc (FRAME_CHUNK * sizeof(gdouble)); - mood->g = (gdouble *) g_malloc (FRAME_CHUNK * sizeof(gdouble)); - mood->b = (gdouble *) g_malloc (FRAME_CHUNK * sizeof(gdouble)); - mood->numframes = 0; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - res = parent_class->change_state (element, transition); - - switch (transition) - { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - g_free (mood->r); - g_free (mood->g); - g_free (mood->b); - mood->r = NULL; - mood->g = NULL; - mood->b = NULL; - mood->numframes = 0; - break; - case GST_STATE_CHANGE_READY_TO_NULL: - g_free (mood->barkband_table); - mood->barkband_table = NULL; - break; - default: - break; - } - - return res; -} - - -/* We allocate r, g, b frames in chunks of FRAME_CHUNK so we don't - * have to realloc every time a buffer comes in. - */ -static gboolean -allocate_another_frame (GstMoodbar *mood) -{ - mood->numframes++; - - /* Failsafe */ - if (mood->numframes == MAX_TRIPLES) - return FALSE; - - if(mood->numframes % FRAME_CHUNK == 0) - { - guint size = (mood->numframes + FRAME_CHUNK) * sizeof (gdouble); - - mood->r = (gdouble *) g_realloc (mood->r, size); - mood->g = (gdouble *) g_realloc (mood->g, size); - mood->b = (gdouble *) g_realloc (mood->b, size); - - if (mood->r == NULL || mood->g == NULL || mood->b == NULL) - return FALSE; - } - - return TRUE; -} - - -/* This function does most of the analysis on the spectra we - * get as input and caches them. We actually push buffers - * once we receive an EOS signal. - */ -static GstFlowReturn -gst_moodbar_chain (GstPad *pad, GstBuffer *buf) -{ - GstMoodbar *mood = GST_MOODBAR (gst_pad_get_parent (pad)); - guint i; - gdouble amplitudes[24], rgb[3] = {0.f, 0.f, 0.f}; - gdouble *out, real, imag; - guint numfreqs = NUMFREQS (mood); - - if (GST_BUFFER_SIZE (buf) != numfreqs * sizeof (gdouble) * 2) - { - gst_object_unref (mood); - return GST_FLOW_ERROR; - } - - out = (gdouble *) GST_BUFFER_DATA (buf); - - if (!allocate_another_frame (mood)) - return GST_FLOW_ERROR; - - /* Calculate total amplitudes for the different bark bands */ - - for (i = 0; i < 24; ++i) - amplitudes[i] = 0.f; - - for (i = 0; i < numfreqs; ++i) - { - real = out[2*i]; imag = out[2*i + 1]; - amplitudes[mood->barkband_table[i]] += sqrtf (real*real + imag*imag); - } - - /* Now divide the bark bands into thirds and compute their total - * amplitudes */ - - for (i = 0; i < 24; ++i) - rgb[i/8] += amplitudes[i] * amplitudes[i]; - - rgb[0] = sqrtf (rgb[0]); - rgb[1] = sqrtf (rgb[1]); - rgb[2] = sqrtf (rgb[2]); - - mood->r[mood->numframes] = rgb[0]; - mood->g[mood->numframes] = rgb[1]; - mood->b[mood->numframes] = rgb[2]; - - gst_buffer_unref (buf); - gst_object_unref (mood); - - return GST_FLOW_OK; -} - - -/* The normalization code was copied from Gav Wood's Exscalibar - * library, normalise.cpp - */ -static void -normalize (gdouble *vals, guint numvals) -{ - gdouble mini, maxi, tu = 0.f, tb = 0.f; - gdouble avgu = 0.f, avgb = 0.f, delta, avg = 0.f; - gdouble avguu = 0.f, avgbb = 0.f; - guint i; - gint t = 0; - - if (!numvals) - return; - - mini = maxi = vals[0]; - - for (i = 1; i < numvals; i++) - { - if (vals[i] > maxi) - maxi = vals[i]; - else if (vals[i] < mini) - mini = vals[i]; - } - - for (i = 0; i < numvals; i++) - { - if(vals[i] != mini && vals[i] != maxi) - { - avg += vals[i] / ((gdouble) numvals); - t++; - } - } - - for (i = 0; i < numvals; i++) - { - if (vals[i] != mini && vals[i] != maxi) - { - if (vals[i] > avg) - { - avgu += vals[i]; - tu++; - } - else - { - avgb += vals[i]; - tb++; - } - } - } - - avgu /= (gdouble) tu; - avgb /= (gdouble) tb; - - tu = 0.f; - tb = 0.f; - for (i = 0; i < numvals; i++) - { - if (vals[i] != mini && vals[i] != maxi) - { - if (vals[i] > avgu) - { - avguu += vals[i]; - tu++; - } - - else if (vals[i] < avgb) - { - avgbb += vals[i]; - tb++; - } - } - } - - avguu /= (gdouble) tu; - avgbb /= (gdouble) tb; - - mini = MAX (avg + (avgb - avg) * 2.f, avgbb); - maxi = MIN (avg + (avgu - avg) * 2.f, avguu); - delta = maxi - mini; - - if (delta == 0.f) - delta = 1.f; - - for (i = 0; i < numvals; i++) - vals[i] = isfinite (vals[i]) ? MIN(1.f, MAX(0.f, (vals[i] - mini) / delta)) - : 0.f; -} - - - -/* This function normalizes all of the cached r,g,b data and - * finally pushes a monster buffer with all of our output. - */ -static void -gst_moodbar_finish (GstMoodbar *mood) -{ - GstBuffer *buf; - guchar *data; - guint line; - guint output_width; - - if (mood->max_width == 0 - || mood->numframes <= mood->max_width) - output_width = mood->numframes; - else - output_width = mood->max_width; - - normalize (mood->r, mood->numframes); - normalize (mood->g, mood->numframes); - normalize (mood->b, mood->numframes); - - buf = gst_buffer_new_and_alloc - (output_width * mood->height * 3 * sizeof (guchar)); - if (!buf) - return; - /* Don't set the timestamp, duration, etc. since it's irrelevant */ - GST_BUFFER_OFFSET (buf) = 0; - data = (guchar *) GST_BUFFER_DATA (buf); - - gdouble r, g, b; - guint i, j, n; - guint start, end; - for (line = 0; line < mood->height; ++line) - { - for (i = 0; i < output_width; ++i) - { - r = 0.f; g = 0.f; b = 0.f; - start = i * mood->numframes / output_width; - end = (i + 1) * mood->numframes / output_width; - if ( start == end ) - end = start + 1; - - for( j = start; j < end; j++ ) - { - r += mood->r[j] * 255.f; - g += mood->g[j] * 255.f; - b += mood->b[j] * 255.f; - } - - n = end - start; - - *(data++) = (guchar) (r / ((gdouble) n)); - *(data++) = (guchar) (g / ((gdouble) n)); - *(data++) = (guchar) (b / ((gdouble) n)); - } - } - - { /* Now we (finally) know the width of the image we're pushing */ - GstCaps *caps = gst_caps_copy (gst_pad_get_caps (mood->srcpad)); - gboolean res; - gst_caps_set_simple (caps, "width", G_TYPE_INT, output_width, NULL); - gst_caps_set_simple (caps, "height", G_TYPE_INT, mood->height, NULL); - res = gst_pad_set_caps (mood->srcpad, caps); - if (res) - gst_buffer_set_caps (buf, caps); - gst_caps_unref (caps); - if (!res) - return; - } - - gst_pad_push (mood->srcpad, buf); -} diff --git a/gst/moodbar/gstmoodbar.h b/gst/moodbar/gstmoodbar.h deleted file mode 100644 index 6ce2f799c..000000000 --- a/gst/moodbar/gstmoodbar.h +++ /dev/null @@ -1,63 +0,0 @@ -/* GStreamer spectrum analysis toy - * Copyright (C) 2006 Joseph Rabinoff - * Some code copyright (C) 2005 Gav Wood - */ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef __GST_MOODBAR_H__ -#define __GST_MOODBAR_H__ - -#include - -G_BEGIN_DECLS - -/* #defines don't like whitespacey bits */ -#define GST_TYPE_MOODBAR \ - (gst_moodbar_get_type()) -#define GST_MOODBAR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MOODBAR,GstMoodbar)) -#define GST_MOODBAR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MOODBAR,GstMoodbarClass)) - -typedef struct _GstMoodbar GstMoodbar; -typedef struct _GstMoodbarClass GstMoodbarClass; - -struct _GstMoodbar -{ - GstElement element; - - GstPad *sinkpad, *srcpad; - - /* Stream data */ - gint rate, size; - - /* Cached band -> bark band table */ - guint *barkband_table; - - /* Queued moodbar data */ - gdouble *r, *g, *b; - guint numframes; - - /* Property */ - guint height; - guint max_width; -}; - -struct _GstMoodbarClass -{ - GstElementClass parent_class; -}; - -GType gst_moodbar_get_type (void); - -G_END_DECLS - -#endif /* __GST_MOODBAR_H__ */ diff --git a/gst/moodbar/plugin.cpp b/gst/moodbar/plugin.cpp new file mode 100644 index 000000000..b70e53b99 --- /dev/null +++ b/gst/moodbar/plugin.cpp @@ -0,0 +1,48 @@ +/* This file is part of Clementine. + Copyright 2014, David Sansome + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#include + +#include "gstfastspectrum.h" +#include "plugin.h" + +namespace { + +static gboolean plugin_init(GstPlugin* plugin) { + if (!gst_element_register(plugin, "fastspectrum", + GST_RANK_NONE, GST_TYPE_FASTSPECTRUM)) { + return FALSE; + } + + return TRUE; +} + +} // namespace + +int gstfastspectrum_register_static() { + return gst_plugin_register_static( + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "fastspectrum", + "Fast spectrum analyzer for generating Moodbars", + plugin_init, + "0.1", + "GPL", + "FastSpectrum", + "FastSpectrum", + "https://www.clementine-player.org"); +} diff --git a/gst/moodbar/plugin.h b/gst/moodbar/plugin.h new file mode 100644 index 000000000..456ee2f94 --- /dev/null +++ b/gst/moodbar/plugin.h @@ -0,0 +1,25 @@ +/* This file is part of Clementine. + Copyright 2014, David Sansome + + Clementine 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. + + Clementine 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 Clementine. 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/gst/moodbar/spectrum.c b/gst/moodbar/spectrum.c deleted file mode 100644 index f82f3932f..000000000 --- a/gst/moodbar/spectrum.c +++ /dev/null @@ -1,71 +0,0 @@ -/* GStreamer moodbar plugin globals - * Copyright (C) 2006 Joseph Rabinoff - */ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include - -#include "gstfftwspectrum.h" -#include "gstmoodbar.h" -#include "spectrum.h" - - -/***************************************************************/ -/* Plugin managing */ -/***************************************************************/ - -GST_DEBUG_CATEGORY_EXTERN (gst_fftwspectrum_debug); -GST_DEBUG_CATEGORY_EXTERN (gst_moodbar_debug); - - -/* entry point to initialize the plug-in - * initialize the plug-in itself - * register the element factories and pad templates - * register the features - * - * exchange the string 'plugin' with your elemnt name - */ -static gboolean -plugin_init (GstPlugin * plugin) -{ - if (!gst_element_register (plugin, "fftwspectrum", - GST_RANK_NONE, GST_TYPE_FFTWSPECTRUM)) - return FALSE; - - if (!gst_element_register (plugin, "moodbar", - GST_RANK_NONE, GST_TYPE_MOODBAR)) - return FALSE; - - GST_DEBUG_CATEGORY_INIT (gst_fftwspectrum_debug, "fftwspectrum", - 0, "FFTW Sample-to-Spectrum Converter Plugin"); - GST_DEBUG_CATEGORY_INIT (gst_moodbar_debug, "moodbar", - 0, "Moodbar analyzer"); - - return TRUE; -} - -void gstmoodbar_register_static() { - gst_plugin_register_static( - GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "moodbar", - "Frequency analyzer and converter plugin", - plugin_init, - "0.1.2", - "GPL", - "Moodbar", - "Moodbar", - "http://amarok.kde.org/wiki/Moodbar"); -} diff --git a/gst/moodbar/spectrum.h b/gst/moodbar/spectrum.h deleted file mode 100644 index 65f271338..000000000 --- a/gst/moodbar/spectrum.h +++ /dev/null @@ -1,67 +0,0 @@ -/* GStreamer moodbar plugin globals - * Copyright (C) 2006 Joseph Rabinoff - */ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef __SPECTRUM_H__ -#define __SPECTRUM_H__ - - -/* Since fftwspectrum and fftwunspectrum are supposed to be - * opposites, they'll be using the same caps: */ - -#define SPECTRUM_SIGNAL_CAPS "audio/x-raw-float, " \ - "rate = (int) [ 1, MAX ], " \ - "channels = (int) 1, " \ - "endianness = (int) BYTE_ORDER, " \ - "width = (int) 64, " \ - "signed = (boolean) true" - -/* audio/x-spectrum-complex-float is an array of complex floats. A - * complex float is just a pair (r, i) of a real float and an - * imaginary float, each with the specified width. The properties - * are as follows: - * rate: the rate of the original signal - * size: the number of signals processed to make the current buffer - * step: the number of signals advanced after the current buffer - * width: the size of the real & imaginary parts of the data - * endianness: ditto - * - * Each audio/x-spectrum-complex-float buffer represents the Fourier - * transform of size samples, and hence _must_ have exactly - * floor(size/2) + 1 complex floats in it; in other words, its - * buffer size must be (floor(size/2) + 1) * 2 * sizeof(gfloat) - */ - -#define SPECTRUM_FREQ_CAPS "audio/x-spectrum-complex-float, " \ - "rate = (int) [ 1, MAX ], " \ - "endianness = (int) BYTE_ORDER, " \ - "width = (int) 64, " \ - "size = (int) [ 1, MAX ], " \ - "step = (int) [ 1, MAX ]" - - -/* Given a band number from a spectrum made from size audio - * samples at the given rate, return the frequency that band - * corresponds to. - */ -#define GST_SPECTRUM_BAND_FREQ(band, size, rate) \ - (((gfloat)(band))*((gfloat)(rate))/((gfloat)(size))) - -#ifdef __cplusplus -extern "C" { -#endif - void gstmoodbar_register_static(); -#ifdef __cplusplus -} -#endif - -#endif /* __SPECTRUM_H__ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 775b0a522..3a5df15a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1055,6 +1055,7 @@ optional_source(HAVE_LIBMTP # Moodbar support optional_source(HAVE_MOODBAR SOURCES + moodbar/moodbarbuilder.cpp moodbar/moodbarcontroller.cpp moodbar/moodbaritemdelegate.cpp moodbar/moodbarloader.cpp @@ -1268,7 +1269,6 @@ endif(HAVE_GIO) if(HAVE_AUDIOCD) target_link_libraries(clementine_lib ${CDIO_LIBRARIES}) - target_link_libraries(clementine_lib ${GSTREAMER_CDDA_LIBRARIES}) endif(HAVE_AUDIOCD) if(HAVE_MOODBAR) diff --git a/src/core/songloader.cpp b/src/core/songloader.cpp index 00e3b0284..f5ac565e1 100644 --- a/src/core/songloader.cpp +++ b/src/core/songloader.cpp @@ -27,7 +27,7 @@ #include #ifdef HAVE_AUDIOCD -#include +#include #endif #include "config.h" @@ -138,7 +138,8 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString& filename) { SongLoader::Result SongLoader::LoadAudioCD() { #ifdef HAVE_AUDIOCD // Create gstreamer cdda element - GstElement* cdda = gst_element_make_from_uri(GST_URI_SRC, "cdda://", nullptr); + GstElement* cdda = gst_element_make_from_uri( + GST_URI_SRC, "cdda://", nullptr, nullptr); if (cdda == nullptr) { qLog(Error) << "Error while creating CDDA GstElement"; return Error; @@ -157,10 +158,8 @@ SongLoader::Result SongLoader::LoadAudioCD() { // Get number of tracks GstFormat fmt = gst_format_get_by_nick("track"); - GstFormat out_fmt = fmt; gint64 num_tracks = 0; - if (!gst_element_query_duration(cdda, &out_fmt, &num_tracks) || - out_fmt != fmt) { + if (!gst_element_query_duration(cdda, fmt, &num_tracks)) { qLog(Error) << "Error while querying cdda GstElement"; gst_object_unref(GST_OBJECT(cdda)); return Error; @@ -461,7 +460,7 @@ void SongLoader::LoadRemote() { // Create the source element automatically based on the URL GstElement* source = gst_element_make_from_uri( - GST_URI_SRC, url_.toEncoded().constData(), nullptr); + GST_URI_SRC, url_.toEncoded().constData(), nullptr, nullptr); if (!source) { qLog(Warning) << "Couldn't create gstreamer source element for" << url_.toString(); @@ -479,12 +478,13 @@ void SongLoader::LoadRemote() { // Connect callbacks GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline.get())); CHECKED_GCONNECT(typefind, "have-type", &TypeFound, this); - gst_bus_set_sync_handler(bus, BusCallbackSync, this); + gst_bus_set_sync_handler(bus, BusCallbackSync, this, NULL); gst_bus_add_watch(bus, BusCallback, this); // Add a probe to the sink so we can capture the data if it's a playlist GstPad* pad = gst_element_get_static_pad(fakesink, "sink"); - gst_pad_add_buffer_probe(pad, G_CALLBACK(DataReady), this); + gst_pad_add_probe( + pad, GST_PAD_PROBE_TYPE_BUFFER, &DataReady, this, NULL); gst_object_unref(pad); QEventLoop loop; @@ -520,15 +520,21 @@ void SongLoader::TypeFound(GstElement*, uint, GstCaps* caps, void* self) { instance->StopTypefindAsync(true); } -gboolean SongLoader::DataReady(GstPad*, GstBuffer* buf, void* self) { - SongLoader* instance = static_cast(self); +GstPadProbeReturn SongLoader::DataReady( + GstPad*, GstPadProbeInfo* info, gpointer self) { + SongLoader* instance = reinterpret_cast(self); - if (instance->state_ == Finished) return true; + if (instance->state_ == Finished) + return GST_PAD_PROBE_OK; + + GstBuffer* buffer = gst_pad_probe_info_get_buffer(info); + GstMapInfo map; + gst_buffer_map(buffer, &map, GST_MAP_READ); // Append the data to the buffer - instance->buffer_.append(reinterpret_cast(GST_BUFFER_DATA(buf)), - GST_BUFFER_SIZE(buf)); + instance->buffer_.append(reinterpret_cast(map.data), map.size); qLog(Debug) << "Received total" << instance->buffer_.size() << "bytes"; + gst_buffer_unmap(buffer, &map); if (instance->state_ == WaitingForMagic && (instance->buffer_.size() >= PlaylistParser::kMagicSize || @@ -537,7 +543,7 @@ gboolean SongLoader::DataReady(GstPad*, GstBuffer* buf, void* self) { instance->MagicReady(); } - return true; + return GST_PAD_PROBE_OK; } gboolean SongLoader::BusCallback(GstBus*, GstMessage* msg, gpointer self) { diff --git a/src/core/songloader.h b/src/core/songloader.h index 22499699e..24889e12f 100644 --- a/src/core/songloader.h +++ b/src/core/songloader.h @@ -99,7 +99,8 @@ signals: // GStreamer callbacks static void TypeFound(GstElement* typefind, uint probability, GstCaps* caps, void* self); - static gboolean DataReady(GstPad*, GstBuffer* buf, void* self); + static GstPadProbeReturn DataReady(GstPad*, GstPadProbeInfo* buf, + gpointer self); static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage*, gpointer); static gboolean BusCallback(GstBus*, GstMessage*, gpointer); diff --git a/src/devices/cddadevice.h b/src/devices/cddadevice.h index 4e5b11604..d89221f07 100644 --- a/src/devices/cddadevice.h +++ b/src/devices/cddadevice.h @@ -22,7 +22,7 @@ // These must come after Qt includes (issue 3247) #include -#include +#include #include "connecteddevice.h" #include "core/song.h" diff --git a/src/devices/cddalister.h b/src/devices/cddalister.h index 47718826a..373ce4f2c 100644 --- a/src/devices/cddalister.h +++ b/src/devices/cddalister.h @@ -18,8 +18,6 @@ #ifndef CDDALISTER_H #define CDDALISTER_H -#include - #include #include "devicelister.h" diff --git a/src/engines/gstengine.cpp b/src/engines/gstengine.cpp index 90be58ca7..2c04e23a9 100755 --- a/src/engines/gstengine.cpp +++ b/src/engines/gstengine.cpp @@ -49,7 +49,7 @@ #include "core/utilities.h" #ifdef HAVE_MOODBAR -#include "gst/moodbar/spectrum.h" +#include "gst/moodbar/plugin.h" #endif #ifdef HAVE_LIBPULSE @@ -128,7 +128,7 @@ void GstEngine::InitialiseGstreamer() { gst_init(nullptr, nullptr); #ifdef HAVE_MOODBAR - gstmoodbar_register_static(); + gstfastspectrum_register_static(); #endif QSet plugin_names; @@ -281,32 +281,24 @@ void GstEngine::UpdateScope(int chunk_length) { if (!GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(latest_buffer_))) return; if (GST_BUFFER_DURATION(latest_buffer_) == 0) return; + GstMapInfo map; + gst_buffer_map(latest_buffer_, &map, GST_MAP_READ); + // determine where to split the buffer - int chunk_density = (GST_BUFFER_SIZE(latest_buffer_) * kNsecPerMsec) / + int chunk_density = (map.size * kNsecPerMsec) / GST_BUFFER_DURATION(latest_buffer_); int chunk_size = chunk_length * chunk_density; - // determine the number of channels - GstStructure* structure = - gst_caps_get_structure(GST_BUFFER_CAPS(latest_buffer_), 0); - int channels = 2; - gst_structure_get_int(structure, "channels", &channels); - - // scope does not support >2 channels - if (channels > 2) return; - // in case a buffer doesn't arrive in time if (scope_chunk_ >= scope_chunks_) { scope_chunk_ = 0; return; } - // set the starting point in the buffer to take data from - const sample_type* source = - reinterpret_cast(GST_BUFFER_DATA(latest_buffer_)); - source += (chunk_size / sizeof(sample_type)) * scope_chunk_; + const sample_type* source = reinterpret_cast(map.data); sample_type* dest = scope_.data(); + source += (chunk_size / sizeof(sample_type)) * scope_chunk_; int bytes = 0; @@ -314,7 +306,7 @@ void GstEngine::UpdateScope(int chunk_length) { if (scope_chunk_ == scope_chunks_ - 1) { bytes = qMin(static_cast( - GST_BUFFER_SIZE(latest_buffer_) - (chunk_size * scope_chunk_)), + map.size - (chunk_size * scope_chunk_)), scope_.size() * sizeof(sample_type)); } else { bytes = qMin(static_cast(chunk_size), @@ -322,8 +314,11 @@ void GstEngine::UpdateScope(int chunk_length) { } scope_chunk_++; - memcpy(dest, source, bytes); + + gst_buffer_unmap(latest_buffer_, &map); + gst_buffer_unref(latest_buffer_); + latest_buffer_ = NULL; } void GstEngine::StartPreloading(const QUrl& url, bool force_stop_at_end, @@ -731,17 +726,19 @@ GstEngine::PluginDetailsList GstEngine::GetPluginList( const QString& classname) const { PluginDetailsList ret; - GstRegistry* registry = gst_registry_get_default(); + GstRegistry* registry = gst_registry_get(); GList* const features = gst_registry_get_feature_list(registry, GST_TYPE_ELEMENT_FACTORY); GList* p = features; while (p) { GstElementFactory* factory = GST_ELEMENT_FACTORY(p->data); - if (QString(factory->details.klass).contains(classname)) { + if (QString(gst_element_factory_get_klass(factory)).contains(classname)) { PluginDetails details; - details.name = QString::fromUtf8(GST_PLUGIN_FEATURE_NAME(p->data)); - details.description = QString::fromUtf8(factory->details.longname); + details.name = QString::fromUtf8(gst_plugin_feature_get_name(p->data)); + details.description = QString::fromUtf8( + gst_element_factory_get_metadata(factory, + GST_ELEMENT_METADATA_DESCRIPTION)); ret << details; } p = g_list_next(p); diff --git a/src/engines/gstenginepipeline.cpp b/src/engines/gstenginepipeline.cpp index 275fd2715..f7f732faa 100644 --- a/src/engines/gstenginepipeline.cpp +++ b/src/engines/gstenginepipeline.cpp @@ -324,7 +324,8 @@ bool GstEnginePipeline::Init() { // We do it here because we want pre-equalized and pre-volume samples // so that our visualization are not be affected by them. pad = gst_element_get_static_pad(event_probe, "src"); - gst_pad_add_event_probe(pad, G_CALLBACK(EventHandoffCallback), this); + gst_pad_add_probe( + pad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, &EventHandoffCallback, this, NULL); gst_object_unref(pad); // Configure the fakesink properly @@ -335,8 +336,8 @@ bool GstEnginePipeline::Init() { int last_band_frequency = 0; for (int i = 0; i < kEqBandCount; ++i) { - GstObject* band = - gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), i); + GstObject* band = GST_OBJECT(gst_child_proxy_get_child_by_index( + GST_CHILD_PROXY(equalizer_), i)); const float frequency = kEqBandFrequencies[i]; const float bandwidth = frequency - last_band_frequency; @@ -366,29 +367,20 @@ bool GstEnginePipeline::Init() { g_object_set(G_OBJECT(queue_), "use-buffering", true, nullptr); } - gst_element_link(queue_, audioconvert_); - - // Create the caps to put in each path in the tee. The scope path gets 16-bit - // ints and the audiosink path gets float32. - GstCaps* caps16 = - gst_caps_new_simple("audio/x-raw-int", "width", G_TYPE_INT, 16, "signed", - G_TYPE_BOOLEAN, true, nullptr); - GstCaps* caps32 = gst_caps_new_simple("audio/x-raw-float", "width", - G_TYPE_INT, 32, nullptr); - if (mono_playback_) { - gst_caps_set_simple(caps32, "channels", G_TYPE_INT, 1, nullptr); - } + gst_element_link_many(queue_, audioconvert_, convert_sink, nullptr); // Link the elements with special caps + // The scope path through the tee gets 16-bit ints. + GstCaps* caps16 = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, "S16LE", + NULL); gst_element_link_filtered(probe_converter, probe_sink, caps16); - gst_element_link_filtered(audioconvert_, convert_sink, caps32); gst_caps_unref(caps16); - gst_caps_unref(caps32); // Link the outputs of tee to the queues on each path. - gst_pad_link(gst_element_get_request_pad(tee, "src%d"), + gst_pad_link(gst_element_get_request_pad(tee, "src_%u"), gst_element_get_static_pad(probe_queue, "sink")); - gst_pad_link(gst_element_get_request_pad(tee, "src%d"), + gst_pad_link(gst_element_get_request_pad(tee, "src_%u"), gst_element_get_static_pad(audio_queue, "sink")); // Link replaygain elements if enabled. @@ -403,10 +395,11 @@ bool GstEnginePipeline::Init() { audiosink_, nullptr); // Add probes and handlers. - gst_pad_add_buffer_probe(gst_element_get_static_pad(probe_converter, "src"), - G_CALLBACK(HandoffCallback), this); + gst_pad_add_probe(gst_element_get_static_pad(probe_converter, "src"), + GST_PAD_PROBE_TYPE_BUFFER, + HandoffCallback, this, nullptr); gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), - BusCallbackSync, this); + BusCallbackSync, this, nullptr); bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this); @@ -465,8 +458,9 @@ bool GstEnginePipeline::InitFromUrl(const QUrl& url, qint64 end_nanosec) { GstEnginePipeline::~GstEnginePipeline() { if (pipeline_) { - gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), - nullptr, nullptr); + gst_bus_set_sync_handler( + gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), + nullptr, nullptr, nullptr); g_source_remove(bus_cb_id_); gst_element_set_state(pipeline_, GST_STATE_NULL); gst_object_unref(GST_OBJECT(pipeline_)); @@ -551,12 +545,7 @@ void GstEnginePipeline::StreamStatusMessageReceived(GstMessage* msg) { const GValue* val = gst_message_get_stream_status_object(msg); if (G_VALUE_TYPE(val) == GST_TYPE_TASK) { GstTask* task = static_cast(g_value_get_object(val)); - - GstTaskThreadCallbacks callbacks; - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.enter_thread = TaskEnterCallback; - - gst_task_set_thread_callbacks(task, &callbacks, this, nullptr); + gst_task_set_enter_callback(task, &TaskEnterCallback, this, NULL); } } } @@ -719,9 +708,11 @@ void GstEnginePipeline::NewPadCallback(GstElement*, GstPad* pad, } } -bool GstEnginePipeline::HandoffCallback(GstPad*, GstBuffer* buf, - gpointer self) { +GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, + GstPadProbeInfo* info, + gpointer self) { GstEnginePipeline* instance = reinterpret_cast(self); + GstBuffer* buf = gst_pad_probe_info_get_buffer(info); QList consumers; { @@ -779,23 +770,24 @@ bool GstEnginePipeline::HandoffCallback(GstPad*, GstBuffer* buf, instance->last_buffer_offset_ = GST_BUFFER_OFFSET(buf); - return true; + return GST_PAD_PROBE_OK; } -bool GstEnginePipeline::EventHandoffCallback(GstPad*, GstEvent* e, - gpointer self) { +GstPadProbeReturn GstEnginePipeline::EventHandoffCallback(GstPad*, + GstPadProbeInfo* info, + gpointer self) { GstEnginePipeline* instance = reinterpret_cast(self); + GstEvent* e = gst_pad_probe_info_get_event(info); qLog(Debug) << instance->id() << "event" << GST_EVENT_TYPE_NAME(e); - if (GST_EVENT_TYPE(e) == GST_EVENT_NEWSEGMENT && + if (GST_EVENT_TYPE(e) == GST_EVENT_SEGMENT && !instance->segment_start_received_) { // The segment start time is used to calculate the proper offset of data // buffers from the start of the stream - gint64 start = 0; - gst_event_parse_new_segment(e, nullptr, nullptr, nullptr, &start, nullptr, - nullptr); - instance->segment_start_ = start; + const GstSegment* segment = nullptr; + gst_event_parse_segment(e, &segment); + instance->segment_start_ = segment->start; instance->segment_start_received_ = true; if (instance->emit_track_ended_on_segment_start_) { @@ -806,7 +798,7 @@ bool GstEnginePipeline::EventHandoffCallback(GstPad*, GstEvent* e, } } - return true; + return GST_PAD_PROBE_OK; } void GstEnginePipeline::SourceDrainedCallback(GstURIDecodeBin* bin, @@ -887,9 +879,8 @@ void GstEnginePipeline::TransitionToNext() { } qint64 GstEnginePipeline::position() const { - GstFormat fmt = GST_FORMAT_TIME; gint64 value = 0; - gst_element_query_position(pipeline_, &fmt, &value); + gst_element_query_position(pipeline_, GST_FORMAT_TIME, &value); if (url_.scheme() == "spotify") { value += spotify_offset_; @@ -899,9 +890,8 @@ qint64 GstEnginePipeline::position() const { } qint64 GstEnginePipeline::length() const { - GstFormat fmt = GST_FORMAT_TIME; gint64 value = 0; - gst_element_query_duration(pipeline_, &fmt, &value); + gst_element_query_duration(pipeline_, GST_FORMAT_TIME, &value); return value; } @@ -999,8 +989,8 @@ void GstEnginePipeline::UpdateEqualizer() { else gain *= 0.12; - GstObject* band = - gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), i); + GstObject* band = GST_OBJECT( + gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), i)); g_object_set(G_OBJECT(band), "gain", gain, nullptr); g_object_unref(G_OBJECT(band)); } diff --git a/src/engines/gstenginepipeline.h b/src/engines/gstenginepipeline.h index 3aa3fe0ee..ab4ea6d99 100644 --- a/src/engines/gstenginepipeline.h +++ b/src/engines/gstenginepipeline.h @@ -130,8 +130,8 @@ signals: static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage*, gpointer); static gboolean BusCallback(GstBus*, GstMessage*, gpointer); static void NewPadCallback(GstElement*, GstPad*, gpointer); - static bool HandoffCallback(GstPad*, GstBuffer*, gpointer); - static bool EventHandoffCallback(GstPad*, GstEvent*, gpointer); + static GstPadProbeReturn HandoffCallback(GstPad*, GstPadProbeInfo*, gpointer); + static GstPadProbeReturn EventHandoffCallback(GstPad*, GstPadProbeInfo*, gpointer); static void SourceDrainedCallback(GstURIDecodeBin*, gpointer); static void SourceSetupCallback(GstURIDecodeBin*, GParamSpec* pspec, gpointer); diff --git a/src/moodbar/moodbarbuilder.cpp b/src/moodbar/moodbarbuilder.cpp new file mode 100644 index 000000000..f19386e42 --- /dev/null +++ b/src/moodbar/moodbarbuilder.cpp @@ -0,0 +1,191 @@ +/* This file is part of Clementine. + Copyright 2014, David Sansome + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#include "moodbarbuilder.h" +#include "core/arraysize.h" + +#include + +namespace { + +static const int sBarkBands[] = { + 100, 200, 300, 400, 510, 630, 770, 920, + 1080, 1270, 1480, 1720, 2000, 2320, 2700, 3150, + 3700, 4400, 5300, 6400, 7700, 9500, 12000, 15500 }; + +static const int sBarkBandCount = arraysize(sBarkBands); + +} // namespace + +MoodbarBuilder::MoodbarBuilder() + : bands_(0), + rate_hz_(0) { +} + +int MoodbarBuilder::BandFrequency(int band) const { + return ((rate_hz_ / 2) * band + rate_hz_ / 4) / bands_; +} + +void MoodbarBuilder::Init(int bands, int rate_hz) { + bands_ = bands; + rate_hz_ = rate_hz; + + barkband_table_.clear(); + barkband_table_.reserve(bands + 1); + + int barkband = 0; + for (int i = 0; i < bands + 1; ++i) { + if (barkband < sBarkBandCount - 1 && + BandFrequency(i) >= sBarkBands[barkband]) { + barkband++; + } + + barkband_table_.append(barkband); + } +} + +void MoodbarBuilder::AddFrame(const double* magnitudes, int size) { + if (size > barkband_table_.length()) { + return; + } + + // Calculate total magnitudes for different bark bands. + double bands[sBarkBandCount]; + for (int i = 0; i < sBarkBandCount; ++i) { + bands[i] = 0.0; + } + + for (int i = 0; i < size; ++i) { + bands[barkband_table_[i]] += magnitudes[i]; + } + + // Now divide the bark bands into thirds and compute their total amplitudes. + double rgb[] = {0, 0, 0}; + for (int i = 0; i < sBarkBandCount; ++i) { + rgb[(i * 3) / sBarkBandCount] += bands[i] * bands[i]; + } + + frames_.append(Rgb(sqrt(rgb[0]), sqrt(rgb[1]), sqrt(rgb[2]))); +} + +void MoodbarBuilder::Normalize(QList* vals, double Rgb::*member) { + double mini = vals->at(0).*member; + double maxi = vals->at(0).*member; + for (int i = 1; i < vals->count(); i++) { + const double value = vals->at(i).*member; + if (value > maxi) { + maxi = value; + } else if (value < mini) { + mini = value; + } + } + + double avg = 0; + int t = 0; + for (const Rgb& rgb : *vals) { + const double value = rgb.*member; + if (value != mini && value != maxi) { + avg += value / vals->count(); + t++; + } + } + + double tu = 0; + double tb = 0; + double avgu = 0; + double avgb = 0; + for (const Rgb& rgb : *vals) { + const double value = rgb.*member; + if (value != mini && value != maxi) { + if (value > avg) { + avgu += value; + tu++; + } else { + avgb += value; + tb++; + } + } + } + avgu /= tu; + avgb /= tb; + + tu = 0; + tb = 0; + double avguu = 0; + double avgbb = 0; + for (const Rgb& rgb : *vals) { + const double value = rgb.*member; + if (value != mini && value != maxi) { + if (value > avgu) { + avguu += value; + tu++; + } else if (value < avgb) { + avgbb += value; + tb++; + } + } + } + avguu /= tu; + avgbb /= tb; + + mini = std::max(avg + (avgb - avg) * 2, avgbb); + maxi = std::min(avg + (avgu - avg) * 2, avguu); + double delta = maxi - mini; + if (delta == 0) { + delta = 1; + } + + for (auto it = vals->begin(); it != vals->end(); ++it) { + double* value = &((*it).*member); + *value = std::isfinite(*value) + ? qBound(0.0, (*value - mini) / delta, 1.0) + : 0; + } +} + +QByteArray MoodbarBuilder::Finish(int width) { + Normalize(&frames_, &Rgb::r); + Normalize(&frames_, &Rgb::g); + Normalize(&frames_, &Rgb::b); + + QByteArray ret; + ret.resize(width * 3); + char* data = ret.data(); + + for (int i = 0; i < width; ++i) { + Rgb rgb; + int start = i * frames_.count() / width; + int end = (i + 1) * frames_.count() / width; + if (start == end) { + end = start + 1; + } + + for (int j = start; j < end; j++) { + const Rgb& frame = frames_[j]; + rgb.r += frame.r * 255; + rgb.g += frame.g * 255; + rgb.b += frame.b * 255; + } + + const int n = end - start; + + *(data++) = rgb.r / n; + *(data++) = rgb.g / n; + *(data++) = rgb.b / n; + } + return ret; +} diff --git a/src/moodbar/moodbarbuilder.h b/src/moodbar/moodbarbuilder.h new file mode 100644 index 000000000..c718ff6cc --- /dev/null +++ b/src/moodbar/moodbarbuilder.h @@ -0,0 +1,50 @@ +/* This file is part of Clementine. + Copyright 2014, David Sansome + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef MOODBARBUILDER_H +#define MOODBARBUILDER_H + +#include +#include + +class MoodbarBuilder { + public: + MoodbarBuilder(); + + void Init(int bands, int rate_hz); + void AddFrame(const double* magnitudes, int size); + QByteArray Finish(int width); + + private: + struct Rgb { + Rgb() : r(0), g(0), b(0) {} + Rgb(double r_, double g_, double b_) : r(r_), g(g_), b(b_) {} + + double r, g, b; + }; + + int BandFrequency(int band) const; + static void Normalize(QList* vals, double Rgb::*member); + + QList barkband_table_; + int bands_; + int rate_hz_; + + QList frames_; +}; + +#endif // MOODBARBUILDER_H diff --git a/src/moodbar/moodbarloader.cpp b/src/moodbar/moodbarloader.cpp index dfc905e44..578e8b4d6 100644 --- a/src/moodbar/moodbarloader.cpp +++ b/src/moodbar/moodbarloader.cpp @@ -156,12 +156,10 @@ void MoodbarLoader::RequestFinished(MoodbarPipeline* request, const QUrl& url) { metadata.setUrl(url); QIODevice* cache_file = cache_->prepare(metadata); - if (!cache_file) { - qLog(Warning) << "Error writing to moodbar cache"; - return; + if (cache_file) { + cache_file->write(request->data()); + cache_->insert(cache_file); } - cache_file->write(request->data()); - cache_->insert(cache_file); // Save the data alongside the original as well if we're configured to. if (save_alongside_originals_) { diff --git a/src/moodbar/moodbarpipeline.cpp b/src/moodbar/moodbarpipeline.cpp index 1de26e6c6..9f3bee927 100644 --- a/src/moodbar/moodbarpipeline.cpp +++ b/src/moodbar/moodbarpipeline.cpp @@ -23,9 +23,14 @@ #include "core/logging.h" #include "core/signalchecker.h" +#include "core/timeconstants.h" #include "core/utilities.h" +#include "moodbar/moodbarbuilder.h" + +#include "gst/moodbar/gstfastspectrum.h" bool MoodbarPipeline::sIsAvailable = false; +const int MoodbarPipeline::kBands = 128; MoodbarPipeline::MoodbarPipeline(const QUrl& local_filename) : QObject(nullptr), @@ -44,12 +49,6 @@ bool MoodbarPipeline::IsAvailable() { } gst_object_unref(factory); - factory = gst_element_factory_find("moodbar"); - if (!factory) { - return false; - } - gst_object_unref(factory); - sIsAvailable = true; } @@ -82,40 +81,44 @@ void MoodbarPipeline::Start() { GstElement* decodebin = CreateElement("uridecodebin"); convert_element_ = CreateElement("audioconvert"); - GstElement* fftwspectrum = CreateElement("fftwspectrum"); - GstElement* moodbar = CreateElement("moodbar"); - GstElement* appsink = CreateElement("appsink"); + GstElement* spectrum = CreateElement("fastspectrum"); + GstElement* fakesink = CreateElement("fakesink"); - if (!decodebin || !convert_element_ || !fftwspectrum || !moodbar || - !appsink) { + if (!decodebin || !convert_element_ || !spectrum || !fakesink) { pipeline_ = nullptr; emit Finished(false); return; } // Join them together - gst_element_link_many(convert_element_, fftwspectrum, moodbar, appsink, - nullptr); + if (!gst_element_link(convert_element_, spectrum) || + !gst_element_link(spectrum, fakesink)) { + qLog(Error) << "Failed to link elements"; + pipeline_ = nullptr; + emit Finished(false); + return; + } + + builder_.reset(new MoodbarBuilder); // Set properties - g_object_set(decodebin, "uri", local_filename_.toEncoded().constData(), + g_object_set(decodebin, + "uri", local_filename_.toEncoded().constData(), nullptr); - g_object_set(fftwspectrum, "def-size", 2048, "def-step", 1024, "hiquality", - true, nullptr); - g_object_set(moodbar, "height", 1, "max-width", 1000, nullptr); + g_object_set(spectrum, + "bands", kBands, + nullptr); + + GstFastSpectrum* fast_spectrum = GST_FASTSPECTRUM(spectrum); + fast_spectrum->output_callback = [this](double* magnitudes, int size) { + builder_->AddFrame(magnitudes, size); + }; // Connect signals CHECKED_GCONNECT(decodebin, "pad-added", &NewPadCallback, this); - gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), - BusCallbackSync, this); - - // Set appsink callbacks - GstAppSinkCallbacks callbacks; - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.new_buffer = NewBufferCallback; - - gst_app_sink_set_callbacks(reinterpret_cast(appsink), &callbacks, - this, nullptr); + GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_)); + gst_bus_set_sync_handler(bus, BusCallbackSync, this, nullptr); + gst_object_unref(bus); // Start playing gst_element_set_state(pipeline_, GST_STATE_PLAYING); @@ -146,17 +149,14 @@ void MoodbarPipeline::NewPadCallback(GstElement*, GstPad* pad, gpointer data) { gst_pad_link(pad, audiopad); gst_object_unref(audiopad); -} -GstFlowReturn MoodbarPipeline::NewBufferCallback(GstAppSink* app_sink, - gpointer data) { - MoodbarPipeline* self = reinterpret_cast(data); + int rate = 0; + GstCaps* caps = gst_pad_get_current_caps(pad); + GstStructure* structure = gst_caps_get_structure(caps, 0); + gst_structure_get_int(structure, "rate", &rate); + gst_caps_unref(caps); - GstBuffer* buffer = gst_app_sink_pull_buffer(app_sink); - self->data_.append(reinterpret_cast(buffer->data), buffer->size); - gst_buffer_unref(buffer); - - return GST_FLOW_OK; + self->builder_->Init(kBands, rate); } GstBusSyncReply MoodbarPipeline::BusCallbackSync(GstBus*, GstMessage* msg, @@ -181,6 +181,9 @@ GstBusSyncReply MoodbarPipeline::BusCallbackSync(GstBus*, GstMessage* msg, void MoodbarPipeline::Stop(bool success) { success_ = success; + data_ = builder_->Finish(1000); + builder_.reset(); + emit Finished(success); } @@ -190,7 +193,7 @@ void MoodbarPipeline::Cleanup() { if (pipeline_) { gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), - nullptr, nullptr); + nullptr, nullptr, nullptr); gst_element_set_state(pipeline_, GST_STATE_NULL); gst_object_unref(pipeline_); pipeline_ = nullptr; diff --git a/src/moodbar/moodbarpipeline.h b/src/moodbar/moodbarpipeline.h index 63374176e..c7acad8e5 100644 --- a/src/moodbar/moodbarpipeline.h +++ b/src/moodbar/moodbarpipeline.h @@ -24,6 +24,10 @@ #include #include +#include + +class MoodbarBuilder; + // Creates moodbar data for a single local music file. class MoodbarPipeline : public QObject { Q_OBJECT @@ -40,7 +44,7 @@ class MoodbarPipeline : public QObject { public slots: void Start(); -signals: + signals: void Finished(bool success); private: @@ -58,11 +62,14 @@ signals: private: static bool sIsAvailable; + static const int kBands; QUrl local_filename_; GstElement* pipeline_; GstElement* convert_element_; + std::unique_ptr builder_; + bool success_; QByteArray data_; }; diff --git a/src/musicbrainz/chromaprinter.cpp b/src/musicbrainz/chromaprinter.cpp index ebb2ef882..21f8dfc93 100644 --- a/src/musicbrainz/chromaprinter.cpp +++ b/src/musicbrainz/chromaprinter.cpp @@ -66,8 +66,8 @@ QString Chromaprinter::CreateFingerprint() { pipeline_ = gst_pipeline_new("pipeline"); GstElement* src = CreateElement("filesrc", pipeline_); - GstElement* decode = CreateElement("decodebin2", pipeline_); - GstElement* convert = CreateElement("audioconvert", pipeline_); + GstElement* decode = CreateElement("decodebin", pipeline_); + GstElement* convert = CreateElement("audioconvert", pipeline_); GstElement* resample = CreateElement("audioresample", pipeline_); GstElement* sink = CreateElement("appsink", pipeline_); @@ -81,16 +81,19 @@ QString Chromaprinter::CreateFingerprint() { gst_element_link_many(src, decode, nullptr); gst_element_link_many(convert, resample, nullptr); - // Chromaprint expects mono floats at a sample rate of 11025Hz. + // Chromaprint expects mono 16-bit ints at a sample rate of 11025Hz. GstCaps* caps = gst_caps_new_simple( - "audio/x-raw-int", "width", G_TYPE_INT, 16, "channels", G_TYPE_INT, - kDecodeChannels, "rate", G_TYPE_INT, kDecodeRate, nullptr); + "audio/x-raw", + "format", G_TYPE_STRING, "S16LE", + "channels", G_TYPE_INT, kDecodeChannels, + "rate", G_TYPE_INT, kDecodeRate, + NULL); gst_element_link_filtered(resample, sink, caps); gst_caps_unref(caps); GstAppSinkCallbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); - callbacks.new_buffer = NewBufferCallback; + callbacks.new_sample = NewBufferCallback; gst_app_sink_set_callbacks(reinterpret_cast(sink), &callbacks, this, nullptr); g_object_set(G_OBJECT(sink), "sync", FALSE, nullptr); @@ -100,11 +103,11 @@ QString Chromaprinter::CreateFingerprint() { g_object_set(src, "location", filename_.toUtf8().constData(), nullptr); // Connect signals - CHECKED_GCONNECT(decode, "new-decoded-pad", &NewPadCallback, this); + CHECKED_GCONNECT(decode, "pad-added", &NewPadCallback, this); gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), - BusCallbackSync, this); + BusCallbackSync, this, nullptr); guint bus_callback_id = gst_bus_add_watch( - gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this); + gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this); QTime time; time.start(); @@ -150,11 +153,11 @@ QString Chromaprinter::CreateFingerprint() { << "Codegen time:" << codegen_time; // Cleanup - callbacks.new_buffer = nullptr; + callbacks.new_sample = nullptr; gst_app_sink_set_callbacks(reinterpret_cast(sink), &callbacks, this, nullptr); gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), - nullptr, nullptr); + nullptr, nullptr, nullptr); g_source_remove(bus_callback_id); gst_element_set_state(pipeline_, GST_STATE_NULL); gst_object_unref(pipeline_); @@ -162,8 +165,7 @@ QString Chromaprinter::CreateFingerprint() { return fingerprint; } -void Chromaprinter::NewPadCallback(GstElement*, GstPad* pad, gboolean, - gpointer data) { +void Chromaprinter::NewPadCallback(GstElement*, GstPad* pad, gpointer data) { Chromaprinter* instance = reinterpret_cast(data); GstPad* const audiopad = gst_element_get_static_pad(instance->convert_element_, "sink"); @@ -238,13 +240,16 @@ GstFlowReturn Chromaprinter::NewBufferCallback(GstAppSink* app_sink, return GST_FLOW_OK; } - GstBuffer* buffer = gst_app_sink_pull_buffer(app_sink); - me->buffer_.write(reinterpret_cast(buffer->data), buffer->size); + GstSample* sample = gst_app_sink_pull_sample(app_sink); + GstBuffer* buffer = gst_sample_get_buffer(sample); + GstMapInfo map; + gst_buffer_map(buffer, &map, GST_MAP_READ); + me->buffer_.write(reinterpret_cast(map.data), map.size); + gst_buffer_unmap(buffer, &map); gst_buffer_unref(buffer); gint64 pos = 0; - GstFormat format = GST_FORMAT_TIME; - gboolean ret = gst_element_query_position(me->pipeline_, &format, &pos); + gboolean ret = gst_element_query_position(me->pipeline_, GST_FORMAT_TIME, &pos); if (ret && pos > 30 * kNsecPerSec) { me->finishing_ = true; g_main_loop_quit(me->event_loop_); diff --git a/src/musicbrainz/chromaprinter.h b/src/musicbrainz/chromaprinter.h index 7174cfc0f..f596c676a 100644 --- a/src/musicbrainz/chromaprinter.h +++ b/src/musicbrainz/chromaprinter.h @@ -50,7 +50,7 @@ class Chromaprinter { void ReportError(GstMessage* message); - static void NewPadCallback(GstElement*, GstPad* pad, gboolean, gpointer data); + static void NewPadCallback(GstElement*, GstPad* pad, gpointer data); static gboolean BusCallback(GstBus*, GstMessage* msg, gpointer data); static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage* msg, gpointer data); diff --git a/src/transcoder/transcoder.cpp b/src/transcoder/transcoder.cpp index 1c2ed0625..7457e6a76 100644 --- a/src/transcoder/transcoder.cpp +++ b/src/transcoder/transcoder.cpp @@ -94,7 +94,7 @@ GstElement* Transcoder::CreateElementForMimeType(const QString& element_type, // The caps we're trying to find GstCaps* target_caps = gst_caps_from_string(mime_type.toUtf8().constData()); - GstRegistry* registry = gst_registry_get_default(); + GstRegistry* registry = gst_registry_get(); GList* const features = gst_registry_get_feature_list(registry, GST_TYPE_ELEMENT_FACTORY); @@ -102,7 +102,7 @@ GstElement* Transcoder::CreateElementForMimeType(const QString& element_type, GstElementFactory* factory = GST_ELEMENT_FACTORY(p->data); // Is this the right type of plugin? - if (QString(factory->details.klass).contains(element_type)) { + if (QString(gst_element_factory_get_klass(factory)).contains(element_type)) { const GList* const templates = gst_element_factory_get_static_pad_templates(factory); for (const GList* p = templates; p; p = g_list_next(p)) { @@ -118,7 +118,7 @@ GstElement* Transcoder::CreateElementForMimeType(const QString& element_type, if (intersection) { if (!gst_caps_is_empty(intersection)) { int rank = gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(factory)); - QString name = GST_PLUGIN_FEATURE_NAME(factory); + QString name = GST_OBJECT_NAME(factory); if (name.startsWith("ffmux") || name.startsWith("ffenc")) rank = -1; // ffmpeg usually sucks @@ -448,10 +448,10 @@ bool Transcoder::StartJob(const Job& job) { CHECKED_GCONNECT(decode, "new-decoded-pad", &NewPadCallback, state.get()); gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(state->pipeline_)), - BusCallbackSync, state.get()); - state->bus_callback_id_ = - gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(state->pipeline_)), - BusCallback, state.get()); + BusCallbackSync, state.get(), nullptr); + state->bus_callback_id_ = gst_bus_add_watch( + gst_pipeline_get_bus(GST_PIPELINE(state->pipeline_)), + BusCallback, state.get()); // Start the pipeline gst_element_set_state(state->pipeline_, GST_STATE_PLAYING); @@ -493,7 +493,7 @@ bool Transcoder::event(QEvent* e) { // called after the pipeline is shutting down gst_bus_set_sync_handler( gst_pipeline_get_bus(GST_PIPELINE(finished_event->state_->pipeline_)), - nullptr, nullptr); + nullptr, nullptr, nullptr); g_source_remove(finished_event->state_->bus_callback_id_); // Remove it from the list - this will also destroy the GStreamer pipeline @@ -522,8 +522,8 @@ void Transcoder::Cancel() { // Remove event handlers from the gstreamer pipeline so they don't get // called after the pipeline is shutting down - gst_bus_set_sync_handler( - gst_pipeline_get_bus(GST_PIPELINE(state->pipeline_)), nullptr, nullptr); + gst_bus_set_sync_handler(gst_pipeline_get_bus( + GST_PIPELINE(state->pipeline_)), nullptr, nullptr, nullptr); g_source_remove(state->bus_callback_id_); // Stop the pipeline @@ -547,10 +547,9 @@ QMap Transcoder::GetProgress() const { gint64 position = 0; gint64 duration = 0; - GstFormat format = GST_FORMAT_TIME; - gst_element_query_position(state->pipeline_, &format, &position); - gst_element_query_duration(state->pipeline_, &format, &duration); + gst_element_query_position(state->pipeline_, GST_FORMAT_TIME, &position); + gst_element_query_duration(state->pipeline_, GST_FORMAT_TIME, &duration); ret[state->job_.input] = float(position) / duration; } diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index 714e46ace..57a50639a 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -41,7 +41,9 @@ #include #endif +#ifdef HAVE_AUDIOCD #include +#endif #include "core/appearance.h" #include "core/application.h" @@ -152,6 +154,8 @@ #include "internet/vkservice.h" #endif +#include + #ifdef Q_OS_DARWIN // Non exported mac-specific function. void qt_mac_set_dock_menu(QMenu*); diff --git a/src/visualisations/projectmvisualisation.cpp b/src/visualisations/projectmvisualisation.cpp index 4f45c5d38..742efd0a8 100644 --- a/src/visualisations/projectmvisualisation.cpp +++ b/src/visualisations/projectmvisualisation.cpp @@ -166,11 +166,16 @@ void ProjectMVisualisation::SetDuration(int seconds) { } void ProjectMVisualisation::ConsumeBuffer(GstBuffer* buffer, int) { - const int samples_per_channel = GST_BUFFER_SIZE(buffer) / sizeof(short) / 2; - const short* data = reinterpret_cast(GST_BUFFER_DATA(buffer)); + GstMapInfo map; + gst_buffer_map(buffer, &map, GST_MAP_READ); + const int samples_per_channel = map.size / sizeof(short) / 2; + const short* data = reinterpret_cast(map.data); - if (projectm_) projectm_->pcm()->addPCM16Data(data, samples_per_channel); - gst_buffer_unref(buffer); + if (projectm_) { + projectm_->pcm()->addPCM16Data(data, samples_per_channel); + } + + gst_buffer_unmap(buffer, &map); } void ProjectMVisualisation::SetSelected(const QStringList& paths,