# HG changeset patch # User Chris Cannam # Date 1471618737 -3600 # Node ID 659372323b45f19c5707942ef1c7cffb391efe82 # Parent e94719f941ba86d53732cd872904ab6193b2c5b2# Parent 35387a99c236f91031de4a331593810889b210c0 Merge latest SV 3.0 branch code diff -r e94719f941ba -r 659372323b45 acinclude.m4 --- a/acinclude.m4 Tue Oct 20 12:54:06 2015 +0100 +++ b/acinclude.m4 Fri Aug 19 15:58:57 2016 +0100 @@ -69,6 +69,9 @@ AC_CHECK_PROG(QMAKE, qmake-qt5, $QTDIR/bin/qmake-qt5,,$QTDIR/bin/) fi if test x$QMAKE = x ; then + AC_CHECK_PROG(QMAKE, qt5-qmake, $QTDIR/bin/qt5-qmake,,$QTDIR/bin/) +fi +if test x$QMAKE = x ; then AC_CHECK_PROG(QMAKE, qmake, $QTDIR/bin/qmake,,$QTDIR/bin/) fi if test x$QMAKE = x ; then @@ -78,6 +81,9 @@ AC_CHECK_PROG(QMAKE, qmake-qt5, qmake-qt5,,$PATH) fi if test x$QMAKE = x ; then + AC_CHECK_PROG(QMAKE, qt5-qmake, qt5-qmake,,$PATH) +fi +if test x$QMAKE = x ; then AC_CHECK_PROG(QMAKE, qmake, qmake,,$PATH) fi if test x$QMAKE = x ; then diff -r e94719f941ba -r 659372323b45 base/ColumnOp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/ColumnOp.h Fri Aug 19 15:58:57 2016 +0100 @@ -0,0 +1,216 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2016 Chris Cannam and QMUL. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef COLUMN_OP_H +#define COLUMN_OP_H + +#include "BaseTypes.h" + +#include + +/** + * Display normalization types for columns in e.g. grid plots. + * + * Max1 means to normalize to max value = 1.0. + * Sum1 means to normalize to sum of values = 1.0. + * + * Hybrid means normalize to max = 1.0 and then multiply by + * log10 of the max value, to retain some difference between + * levels of neighbouring columns. + * + * Area normalization is handled separately. + */ +enum class ColumnNormalization { + None, + Max1, + Sum1, + Hybrid +}; + +/** + * Class containing static functions for simple operations on data + * columns, for use by display layers. + */ +class ColumnOp +{ +public: + /** + * Column type. + */ + typedef std::vector Column; + + /** + * Scale the given column using the given gain multiplier. + */ + static Column applyGain(const Column &in, double gain) { + + if (gain == 1.0) { + return in; + } + Column out; + out.reserve(in.size()); + for (auto v: in) { + out.push_back(float(v * gain)); + } + return out; + } + + /** + * Scale an FFT output by half the FFT size. + */ + static Column fftScale(const Column &in, int fftSize) { + return applyGain(in, 2.0 / fftSize); + } + + /** + * Determine whether an index points to a local peak. + */ + static bool isPeak(const Column &in, int ix) { + + if (!in_range_for(in, ix-1)) return false; + if (!in_range_for(in, ix+1)) return false; + if (in[ix] < in[ix+1]) return false; + if (in[ix] < in[ix-1]) return false; + + return true; + } + + /** + * Return a column containing only the local peak values (all + * others zero). + */ + static Column peakPick(const Column &in) { + + std::vector out(in.size(), 0.f); + for (int i = 0; in_range_for(in, i); ++i) { + if (isPeak(in, i)) { + out[i] = in[i]; + } + } + + return out; + } + + /** + * Return a column normalized from the input column according to + * the given normalization scheme. + */ + static Column normalize(const Column &in, ColumnNormalization n) { + + if (n == ColumnNormalization::None) { + return in; + } + + float scale = 1.f; + + if (n == ColumnNormalization::Sum1) { + + float sum = 0.f; + + for (auto v: in) { + sum += v; + } + + if (sum != 0.f) { + scale = 1.f / sum; + } + } else { + + float max = *max_element(in.begin(), in.end()); + + if (n == ColumnNormalization::Max1) { + if (max != 0.f) { + scale = 1.f / max; + } + } else if (n == ColumnNormalization::Hybrid) { + if (max > 0.f) { + scale = log10f(max + 1.f) / max; + } + } + } + + return applyGain(in, scale); + } + + /** + * Distribute the given column into a target vector of a different + * size, optionally using linear interpolation. The binfory vector + * contains a mapping from y coordinate (i.e. index into the + * target vector) to bin (i.e. index into the source column). + */ + static Column distribute(const Column &in, + int h, + const std::vector &binfory, + int minbin, + bool interpolate) { + + std::vector out(h, 0.f); + int bins = int(in.size()); + + for (int y = 0; y < h; ++y) { + + double sy0 = binfory[y] - minbin; + double sy1 = sy0 + 1; + if (y+1 < h) { + sy1 = binfory[y+1] - minbin; + } + + if (interpolate && fabs(sy1 - sy0) < 1.0) { + + double centre = (sy0 + sy1) / 2; + double dist = (centre - 0.5) - rint(centre - 0.5); + int bin = int(centre); + + int other = (dist < 0 ? (bin-1) : (bin+1)); + + if (bin < 0) bin = 0; + if (bin >= bins) bin = bins-1; + + if (other < 0 || other >= bins) { + other = bin; + } + + double prop = 1.0 - fabs(dist); + + double v0 = in[bin]; + double v1 = in[other]; + + out[y] = float(prop * v0 + (1.0 - prop) * v1); + + } else { // not interpolating this one + + int by0 = int(sy0 + 0.0001); + int by1 = int(sy1 + 0.0001); + if (by1 < by0 + 1) by1 = by0 + 1; + if (by1 >= bins) by1 = by1 - 1; + + for (int bin = by0; bin < by1; ++bin) { + + float value = in[bin]; + + if (bin == by0 || value > out[y]) { + out[y] = value; + } + } + } + } + + return out; + } + +}; + +#endif + diff -r e94719f941ba -r 659372323b45 base/MagnitudeRange.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/MagnitudeRange.h Fri Aug 19 15:58:57 2016 +0100 @@ -0,0 +1,83 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam and QMUL. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef MAGNITUDE_RANGE_H +#define MAGNITUDE_RANGE_H + +#include + +/** + * Maintain a min and max value, and update them when supplied a new + * data point. + */ +class MagnitudeRange +{ +public: + MagnitudeRange() : m_min(0), m_max(0) { } + MagnitudeRange(float min, float max) : m_min(min), m_max(max) { } + + bool operator==(const MagnitudeRange &r) { + return r.m_min == m_min && r.m_max == m_max; + } + bool operator!=(const MagnitudeRange &r) { + return !(*this == r); + } + + bool isSet() const { return (m_min != 0.f || m_max != 0.f); } + void set(float min, float max) { + m_min = min; + m_max = max; + if (m_max < m_min) m_max = m_min; + } + bool sample(float f) { + bool changed = false; + if (isSet()) { + if (f < m_min) { m_min = f; changed = true; } + if (f > m_max) { m_max = f; changed = true; } + } else { + m_max = m_min = f; + changed = true; + } + return changed; + } + bool sample(const std::vector &ff) { + bool changed = false; + for (auto f: ff) { + if (sample(f)) { + changed = true; + } + } + return changed; + } + bool sample(const MagnitudeRange &r) { + bool changed = false; + if (isSet()) { + if (r.m_min < m_min) { m_min = r.m_min; changed = true; } + if (r.m_max > m_max) { m_max = r.m_max; changed = true; } + } else { + m_min = r.m_min; + m_max = r.m_max; + changed = true; + } + return changed; + } + float getMin() const { return m_min; } + float getMax() const { return m_max; } +private: + float m_min; + float m_max; +}; + +#endif diff -r e94719f941ba -r 659372323b45 base/RangeMapper.cpp --- a/base/RangeMapper.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/base/RangeMapper.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -23,13 +23,15 @@ LinearRangeMapper::LinearRangeMapper(int minpos, int maxpos, double minval, double maxval, - QString unit, bool inverted) : + QString unit, bool inverted, + std::map labels) : m_minpos(minpos), m_maxpos(maxpos), m_minval(minval), m_maxval(maxval), m_unit(unit), - m_inverted(inverted) + m_inverted(inverted), + m_labels(labels) { assert(m_maxval != m_minval); assert(m_maxpos != m_minpos); @@ -70,9 +72,20 @@ double value = m_minval + ((double(position - m_minpos) / double(m_maxpos - m_minpos)) * (m_maxval - m_minval)); +// cerr << "getValueForPositionUnclamped(" << position << "): minval " << m_minval << ", maxval " << m_maxval << ", value " << value << endl; return value; } +QString +LinearRangeMapper::getLabel(int position) const +{ + if (m_labels.find(position) != m_labels.end()) { + return m_labels.at(position); + } else { + return ""; + } +} + LogRangeMapper::LogRangeMapper(int minpos, int maxpos, double minval, double maxval, QString unit, bool inverted) : diff -r e94719f941ba -r 659372323b45 base/RangeMapper.h --- a/base/RangeMapper.h Tue Oct 20 12:54:06 2015 +0100 +++ b/base/RangeMapper.h Fri Aug 19 15:58:57 2016 +0100 @@ -50,7 +50,7 @@ virtual double getValueForPosition(int position) const = 0; /** - * Return the value mapped from the given positionq, without + * Return the value mapped from the given position, without * clamping. That is, whatever mapping function is in use will be * projected even outside the minimum and maximum extents of the * mapper's value range. (The mapping outside that range is not @@ -62,6 +62,16 @@ * Get the unit of the mapper's value range. */ virtual QString getUnit() const { return ""; } + + /** + * The mapper may optionally provide special labels for one or + * more individual positions (such as the minimum position, the + * default, or indeed all positions). These should be used in any + * display context in preference to just showing the numerical + * value for the position. If a position has such a label, return + * it here. + */ + virtual QString getLabel(int /* position */) const { return ""; } }; @@ -76,7 +86,8 @@ */ LinearRangeMapper(int minpos, int maxpos, double minval, double maxval, - QString unit = "", bool inverted = false); + QString unit = "", bool inverted = false, + std::map labels = {}); virtual int getPositionForValue(double value) const; virtual int getPositionForValueUnclamped(double value) const; @@ -85,6 +96,7 @@ virtual double getValueForPositionUnclamped(int position) const; virtual QString getUnit() const { return m_unit; } + virtual QString getLabel(int position) const; protected: int m_minpos; @@ -93,6 +105,7 @@ double m_maxval; QString m_unit; bool m_inverted; + std::map m_labels; }; class LogRangeMapper : public RangeMapper diff -r e94719f941ba -r 659372323b45 base/ResizeableBitset.h --- a/base/ResizeableBitset.h Tue Oct 20 12:54:06 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _RESIZEABLE_BITMAP_H_ -#define _RESIZEABLE_BITMAP_H_ - -#include -#include -#include -#include - -class ResizeableBitset { - -public: - ResizeableBitset() : m_bits(0), m_size(0) { - } - ResizeableBitset(size_t size) : m_bits(new std::vector), m_size(size) { - m_bits->assign((size >> 3) + 1, 0); - } - ResizeableBitset(const ResizeableBitset &b) { - m_bits = new std::vector(*b.m_bits); - } - ResizeableBitset &operator=(const ResizeableBitset &b) { - if (&b != this) return *this; - delete m_bits; - m_bits = new std::vector(*b.m_bits); - return *this; - } - ~ResizeableBitset() { - delete m_bits; - } - - void resize(size_t size) { // retaining existing data; not thread safe - size_t bytes = (size >> 3) + 1; - if (m_bits && bytes == m_bits->size()) return; - std::vector *newbits = new std::vector(bytes); - newbits->assign(bytes, 0); - if (m_bits) { - for (size_t i = 0; i < bytes && i < m_bits->size(); ++i) { - (*newbits)[i] = (*m_bits)[i]; - } - delete m_bits; - } - m_bits = newbits; - m_size = size; - } - - bool get(size_t column) const { - return ((*m_bits)[column >> 3]) & (1u << (column & 0x07)); - } - - void set(size_t column) { - size_t ix = (column >> 3); - uint8_t prior = (*m_bits)[ix]; - uint8_t extra = ((1u << (column & 0x07)) & 0xff); - (*m_bits)[ix] = uint8_t(prior | extra); - } - - void reset(size_t column) { - ((*m_bits)[column >> 3]) &= uint8_t((~(1u << (column & 0x07))) & 0xff); - } - - void copy(size_t source, size_t dest) { - get(source) ? set(dest) : reset(dest); - } - - bool isAllOff() const { - for (size_t i = 0; i < m_bits->size(); ++i) { - if ((*m_bits)[i]) return false; - } - return true; - } - - bool isAllOn() const { - for (size_t i = 0; i + 1 < m_bits->size(); ++i) { - if ((*m_bits)[i] != 0xff) return false; - } - for (size_t i = (m_size / 8) * 8; i < m_size; ++i) { - if (!get(i)) return false; - } - return true; - } - - size_t size() const { - return m_size; - } - -private: - std::vector *m_bits; - size_t m_size; -}; - - -#endif - diff -r e94719f941ba -r 659372323b45 base/Strings.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/Strings.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -0,0 +1,28 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#include "Strings.h" + +QString +Strings::pi = QChar(0x3c0); + +QString +Strings::minus_pi = "-" + pi; + +QString +Strings::infinity = QChar(0x221e); + +QString +Strings::minus_infinity = "-" + infinity; + diff -r e94719f941ba -r 659372323b45 base/Strings.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/Strings.h Fri Aug 19 15:58:57 2016 +0100 @@ -0,0 +1,30 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_STRINGS_H +#define SV_STRINGS_H + +#include + +class Strings +{ +public: + static QString pi; + static QString minus_pi; + + static QString infinity; + static QString minus_infinity; +}; + +#endif diff -r e94719f941ba -r 659372323b45 base/test/test.pro --- a/base/test/test.pro Tue Oct 20 12:54:06 2015 +0100 +++ b/base/test/test.pro Fri Aug 19 15:58:57 2016 +0100 @@ -1,6 +1,8 @@ TEMPLATE = app +INCLUDEPATH += ../../../vamp-plugin-sdk + LIBS += -L../.. -L../../../dataquay -L../../release -L../../../dataquay/release -lsvcore -ldataquay win32-g++ { @@ -27,7 +29,7 @@ DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_VAMP HAVE_VAMPHOSTSDK HAVE_DATAQUAY HAVE_LIBLO HAVE_MAD HAVE_ID3TAG HAVE_PORTAUDIO - LIBS += -lbz2 -lvamp-hostsdk -lfftw3 -lfftw3f -lsndfile -lFLAC -logg -lvorbis -lvorbisenc -lvorbisfile -logg -lmad -lid3tag -lportaudio -lsamplerate -lz -lsord-0 -lserd-0 + LIBS += -lbz2 -lfftw3 -lfftw3f -lsndfile -lFLAC -logg -lvorbis -lvorbisenc -lvorbisfile -logg -lmad -lid3tag -lportaudio -lsamplerate -lz -lsord-0 -lserd-0 win* { LIBS += -llo -lwinmm -lws2_32 diff -r e94719f941ba -r 659372323b45 config.pri.in --- a/config.pri.in Tue Oct 20 12:54:06 2015 +0100 +++ b/config.pri.in Fri Aug 19 15:58:57 2016 +0100 @@ -10,7 +10,7 @@ QMAKE_CXXFLAGS += @CXXFLAGS@ QMAKE_LFLAGS += @LDFLAGS@ -linux*:LIBS += -lasound +linux*:LIBS += -lasound -ldl macx*:DEFINES += HAVE_COREAUDIO MACOSX_DEPLOYMENT_TARGET=1060 diff -r e94719f941ba -r 659372323b45 configure --- a/configure Tue Oct 20 12:54:06 2015 +0100 +++ b/configure Fri Aug 19 15:58:57 2016 +0100 @@ -650,10 +650,6 @@ portaudio_2_0_CFLAGS liblo_LIBS liblo_CFLAGS -vamphostsdk_LIBS -vamphostsdk_CFLAGS -vamp_LIBS -vamp_CFLAGS samplerate_LIBS samplerate_CFLAGS sndfile_LIBS @@ -754,10 +750,6 @@ sndfile_LIBS samplerate_CFLAGS samplerate_LIBS -vamp_CFLAGS -vamp_LIBS -vamphostsdk_CFLAGS -vamphostsdk_LIBS liblo_CFLAGS liblo_LIBS portaudio_2_0_CFLAGS @@ -1419,12 +1411,6 @@ C compiler flags for samplerate, overriding pkg-config samplerate_LIBS linker flags for samplerate, overriding pkg-config - vamp_CFLAGS C compiler flags for vamp, overriding pkg-config - vamp_LIBS linker flags for vamp, overriding pkg-config - vamphostsdk_CFLAGS - C compiler flags for vamphostsdk, overriding pkg-config - vamphostsdk_LIBS - linker flags for vamphostsdk, overriding pkg-config liblo_CFLAGS C compiler flags for liblo, overriding pkg-config liblo_LIBS linker flags for liblo, overriding pkg-config @@ -4129,6 +4115,45 @@ fi if test x$QMAKE = x ; then + # Extract the first word of "qt5-qmake", so it can be a program name with args. +set dummy qt5-qmake; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_QMAKE+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$QMAKE"; then + ac_cv_prog_QMAKE="$QMAKE" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $QTDIR/bin/ +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_QMAKE="$QTDIR/bin/qt5-qmake" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +QMAKE=$ac_cv_prog_QMAKE +if test -n "$QMAKE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QMAKE" >&5 +$as_echo "$QMAKE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test x$QMAKE = x ; then # Extract the first word of "qmake", so it can be a program name with args. set dummy qmake; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 @@ -4246,6 +4271,45 @@ fi if test x$QMAKE = x ; then + # Extract the first word of "qt5-qmake", so it can be a program name with args. +set dummy qt5-qmake; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_QMAKE+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$QMAKE"; then + ac_cv_prog_QMAKE="$QMAKE" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_QMAKE="qt5-qmake" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +QMAKE=$ac_cv_prog_QMAKE +if test -n "$QMAKE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QMAKE" >&5 +$as_echo "$QMAKE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test x$QMAKE = x ; then # Extract the first word of "qmake", so it can be a program name with args. set dummy qmake; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 @@ -5140,308 +5204,6 @@ fi -SV_MODULE_MODULE=vamp -SV_MODULE_VERSION_TEST="vamp >= 2.1" -SV_MODULE_HEADER=vamp/vamp.h -SV_MODULE_LIB= -SV_MODULE_FUNC= -SV_MODULE_HAVE=HAVE_$(echo vamp | tr 'a-z' 'A-Z') -SV_MODULE_FAILED=1 -if test -n "$vamp_LIBS" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5 -$as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;} - CXXFLAGS="$CXXFLAGS $vamp_CFLAGS" - LIBS="$LIBS $vamp_LIBS" - SV_MODULE_FAILED="" -fi -if test -z "$SV_MODULE_VERSION_TEST" ; then - SV_MODULE_VERSION_TEST=$SV_MODULE_MODULE -fi -if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for vamp" >&5 -$as_echo_n "checking for vamp... " >&6; } - -if test -n "$vamp_CFLAGS"; then - pkg_cv_vamp_CFLAGS="$vamp_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_vamp_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$vamp_LIBS"; then - pkg_cv_vamp_LIBS="$vamp_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_vamp_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - vamp_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` - else - vamp_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$vamp_PKG_ERRORS" >&5 - - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} -else - vamp_CFLAGS=$pkg_cv_vamp_CFLAGS - vamp_LIBS=$pkg_cv_vamp_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $vamp_CFLAGS";LIBS="$LIBS $vamp_LIBS";SV_MODULE_FAILED="" -fi -fi -if test -n "$SV_MODULE_FAILED"; then - as_ac_Header=`$as_echo "ac_cv_header_$SV_MODULE_HEADER" | $as_tr_sh` -ac_fn_cxx_check_header_mongrel "$LINENO" "$SV_MODULE_HEADER" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - HAVES="$HAVES $SV_MODULE_HAVE" -else - as_fn_error $? "Failed to find header $SV_MODULE_HEADER for required module $SV_MODULE_MODULE" "$LINENO" 5 -fi - - - if test -n "$SV_MODULE_LIB"; then - as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB" >&5 -$as_echo_n "checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$SV_MODULE_LIB $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $SV_MODULE_FUNC (); -int -main () -{ -return $SV_MODULE_FUNC (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - LIBS="$LIBS -l$SV_MODULE_LIB" -else - as_fn_error $? "Failed to find library $SV_MODULE_LIB for required module $SV_MODULE_MODULE" "$LINENO" 5 -fi - - fi -fi - - -SV_MODULE_MODULE=vamphostsdk -SV_MODULE_VERSION_TEST="vamp-hostsdk >= 2.5" -SV_MODULE_HEADER=vamp-hostsdk/PluginLoader.h -SV_MODULE_LIB=vamp-hostsdk -SV_MODULE_FUNC=libvamphostsdk_v_2_5_present -SV_MODULE_HAVE=HAVE_$(echo vamphostsdk | tr 'a-z' 'A-Z') -SV_MODULE_FAILED=1 -if test -n "$vamphostsdk_LIBS" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5 -$as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;} - CXXFLAGS="$CXXFLAGS $vamphostsdk_CFLAGS" - LIBS="$LIBS $vamphostsdk_LIBS" - SV_MODULE_FAILED="" -fi -if test -z "$SV_MODULE_VERSION_TEST" ; then - SV_MODULE_VERSION_TEST=$SV_MODULE_MODULE -fi -if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for vamphostsdk" >&5 -$as_echo_n "checking for vamphostsdk... " >&6; } - -if test -n "$vamphostsdk_CFLAGS"; then - pkg_cv_vamphostsdk_CFLAGS="$vamphostsdk_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_vamphostsdk_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$vamphostsdk_LIBS"; then - pkg_cv_vamphostsdk_LIBS="$vamphostsdk_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_vamphostsdk_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - vamphostsdk_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` - else - vamphostsdk_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$vamphostsdk_PKG_ERRORS" >&5 - - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} -else - vamphostsdk_CFLAGS=$pkg_cv_vamphostsdk_CFLAGS - vamphostsdk_LIBS=$pkg_cv_vamphostsdk_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $vamphostsdk_CFLAGS";LIBS="$LIBS $vamphostsdk_LIBS";SV_MODULE_FAILED="" -fi -fi -if test -n "$SV_MODULE_FAILED"; then - as_ac_Header=`$as_echo "ac_cv_header_$SV_MODULE_HEADER" | $as_tr_sh` -ac_fn_cxx_check_header_mongrel "$LINENO" "$SV_MODULE_HEADER" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - HAVES="$HAVES $SV_MODULE_HAVE" -else - as_fn_error $? "Failed to find header $SV_MODULE_HEADER for required module $SV_MODULE_MODULE" "$LINENO" 5 -fi - - - if test -n "$SV_MODULE_LIB"; then - as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB" >&5 -$as_echo_n "checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$SV_MODULE_LIB $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $SV_MODULE_FUNC (); -int -main () -{ -return $SV_MODULE_FUNC (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - LIBS="$LIBS -l$SV_MODULE_LIB" -else - as_fn_error $? "Failed to find library $SV_MODULE_LIB for required module $SV_MODULE_MODULE" "$LINENO" 5 -fi - - fi -fi - - SV_MODULE_MODULE=liblo SV_MODULE_VERSION_TEST="" diff -r e94719f941ba -r 659372323b45 configure.ac --- a/configure.ac Tue Oct 20 12:54:06 2015 +0100 +++ b/configure.ac Fri Aug 19 15:58:57 2016 +0100 @@ -83,8 +83,6 @@ SV_MODULE_REQUIRED([fftw3f],[fftw3f >= 3.0.0],[fftw3.h],[fftw3f],[fftwf_execute]) SV_MODULE_REQUIRED([sndfile],[sndfile >= 1.0.16],[sndfile.h],[sndfile],[sf_open]) SV_MODULE_REQUIRED([samplerate],[samplerate >= 0.1.2],[samplerate.h],[samplerate],[src_new]) -SV_MODULE_REQUIRED([vamp],[vamp >= 2.1],[vamp/vamp.h],[],[]) -SV_MODULE_REQUIRED([vamphostsdk],[vamp-hostsdk >= 2.5],[vamp-hostsdk/PluginLoader.h],[vamp-hostsdk],[libvamphostsdk_v_2_5_present]) SV_MODULE_OPTIONAL([liblo],[],[lo/lo.h],[lo],[lo_address_new]) SV_MODULE_OPTIONAL([portaudio],[portaudio-2.0 >= 19],[portaudio.h],[portaudio],[Pa_IsFormatSupported]) diff -r e94719f941ba -r 659372323b45 data/fileio/AudioFileReaderFactory.cpp --- a/data/fileio/AudioFileReaderFactory.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/AudioFileReaderFactory.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -29,6 +29,8 @@ #include #include +//#define DEBUG_AUDIO_FILE_READER_FACTORY 1 + QString AudioFileReaderFactory::getKnownExtensions() { @@ -87,7 +89,9 @@ { QString err; - SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\"): Requested rate: " << targetRate << endl; +#ifdef DEBUG_AUDIO_FILE_READER_FACTORY + cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\"): Requested rate: " << targetRate << endl; +#endif if (!source.isOK()) { cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Failed to retrieve source (transmission error?): " << source.getErrorString() << endl; @@ -95,7 +99,7 @@ } if (!source.isAvailable()) { - SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Source not found" << endl; + cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Source not found" << endl; return 0; } @@ -139,8 +143,10 @@ (cacheMode == CodedAudioFileReader::CacheInMemory) || (targetRate != 0 && fileRate != targetRate))) { - SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", in memory " << (cacheMode == CodedAudioFileReader::CacheInMemory) << ", creating decoding reader" << endl; - +#ifdef DEBUG_AUDIO_FILE_READER_FACTORY + cerr << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", in memory " << (cacheMode == CodedAudioFileReader::CacheInMemory) << ", creating decoding reader" << endl; +#endif + delete reader; reader = new DecodingWavFileReader (source, @@ -208,7 +214,9 @@ (cacheMode == CodedAudioFileReader::CacheInMemory) || (targetRate != 0 && fileRate != targetRate))) { - SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", in memory " << (cacheMode == CodedAudioFileReader::CacheInMemory) << ", creating decoding reader" << endl; +#ifdef DEBUG_AUDIO_FILE_READER_FACTORY + cerr << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", in memory " << (cacheMode == CodedAudioFileReader::CacheInMemory) << ", creating decoding reader" << endl; +#endif delete reader; reader = new DecodingWavFileReader diff -r e94719f941ba -r 659372323b45 data/fileio/CodedAudioFileReader.cpp --- a/data/fileio/CodedAudioFileReader.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/CodedAudioFileReader.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -146,11 +146,27 @@ } fileInfo.samplerate = fileRate; fileInfo.channels = m_channelCount; - - // No point in writing 24-bit or float; generally this - // class is used for decoding files that have come from a - // 16 bit source or that decode to only 16 bits anyway. - fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + + // Previously we were writing SF_FORMAT_PCM_16 and in a + // comment I wrote: "No point in writing 24-bit or float; + // generally this class is used for decoding files that + // have come from a 16 bit source or that decode to only + // 16 bits anyway." That was naive -- we want to preserve + // the original values to the same float precision that we + // use internally. Saving PCM_16 obviously doesn't + // preserve values for sources at bit depths greater than + // 16, but it also doesn't always do so for sources at bit + // depths less than 16. + // + // (This came to light with a bug in libsndfile 1.0.26, + // which always reports every file as non-seekable, so + // that coded readers were being used even for WAV + // files. This changed the values that came from PCM_8 WAV + // sources, breaking Sonic Annotator's output comparison + // tests.) + // + // So: now we write floats. + fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(), SFM_WRITE, &fileInfo); diff -r e94719f941ba -r 659372323b45 data/fileio/CoreAudioFileReader.cpp --- a/data/fileio/CoreAudioFileReader.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/CoreAudioFileReader.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -57,7 +57,7 @@ } CoreAudioFileReader::CoreAudioFileReader(FileSource source, - DecodeMode decodeMode, + DecodeMode /* decodeMode */, CacheMode mode, sv_samplerate_t targetRate, bool normalised, @@ -180,7 +180,7 @@ // buffers are interleaved unless specified otherwise addSamplesToDecodeCache((float *)m_d->buffer.mBuffers[0].mData, framesRead); - if (framesRead < m_d->blockSize) break; + if ((int)framesRead < m_d->blockSize) break; } finishDecodeCache(); @@ -196,7 +196,7 @@ if (m_d->valid) { ExtAudioFileDispose(m_d->file); - delete[] m_d->buffer.mBuffers[0].mData; + delete[] (float *)(m_d->buffer.mBuffers[0].mData); } delete m_d; diff -r e94719f941ba -r 659372323b45 data/fileio/MP3FileReader.cpp --- a/data/fileio/MP3FileReader.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/MP3FileReader.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -32,6 +32,7 @@ #ifdef HAVE_ID3TAG #include #endif + //#define DEBUG_ID3TAG 1 #include @@ -178,7 +179,7 @@ id3_tag *tag = id3_file_tag(file); if (!tag) { #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: No ID3 tag found" << endl; + cerr << "MP3FileReader::loadTags: No ID3 tag found" << endl; #endif id3_file_close(file); return; @@ -201,7 +202,7 @@ #else #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in" + cerr << "MP3FileReader::loadTags: ID3 tag support not compiled in" << endl; #endif #endif @@ -216,20 +217,20 @@ id3_frame *frame = id3_tag_findframe(tag, name, 0); if (!frame) { #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: No \"" << name << "\" in ID3 tag" << endl; + cerr << "MP3FileReader::loadTags: No \"" << name << "\" in ID3 tag" << endl; #endif return ""; } if (frame->nfields < 2) { - SVDEBUG << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for \"" << name << "\" in ID3 tag" << endl; + cerr << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for \"" << name << "\" in ID3 tag" << endl; return ""; } unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]); if (nstrings == 0) { #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: No strings for \"" << name << "\" in ID3 tag" << endl; + cerr << "MP3FileReader::loadTags: No strings for \"" << name << "\" in ID3 tag" << endl; #endif return ""; } @@ -237,7 +238,7 @@ id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0); if (!ustr) { #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: Invalid or absent data for \"" << name << "\" in ID3 tag" << endl; + cerr << "MP3FileReader::loadTags: Invalid or absent data for \"" << name << "\" in ID3 tag" << endl; #endif return ""; } @@ -252,7 +253,7 @@ free(u8str); #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: tag \"" << name << "\" -> \"" + cerr << "MP3FileReader::loadTags: tag \"" << name << "\" -> \"" << rv << "\"" << endl; #endif diff -r e94719f941ba -r 659372323b45 data/fileio/MatrixFile.cpp --- a/data/fileio/MatrixFile.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/MatrixFile.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -176,8 +176,6 @@ QMutexLocker locker(&m_createMutex); - delete m_setColumns; - if (m_fileName != "") { if (--m_refcount[m_fileName] == 0) { @@ -208,7 +206,7 @@ assert(m_mode == WriteOnly); - m_setColumns = new ResizeableBitset(m_width); + m_setColumns.resize(m_width, false); off_t off = m_headerSize + (m_width * m_height * m_cellSize) + m_width; @@ -316,7 +314,7 @@ MatrixFile::haveSetColumnAt(int x) const { if (m_mode == WriteOnly) { - return m_setColumns->get(x); + return m_setColumns[x]; } if (m_readyToReadColumn >= 0 && @@ -398,9 +396,10 @@ throw FileOperationFailed(m_fileName, "write"); } - m_setColumns->set(x); + m_setColumns[x] = true; if (m_autoClose) { - if (m_setColumns->isAllOn()) { + if (std::all_of(m_setColumns.begin(), m_setColumns.end(), + [](bool c) { return c; })) { #ifdef DEBUG_MATRIX_FILE cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << "): All columns set: auto-closing" << endl; #endif diff -r e94719f941ba -r 659372323b45 data/fileio/MatrixFile.h --- a/data/fileio/MatrixFile.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/MatrixFile.h Fri Aug 19 15:58:57 2016 +0100 @@ -13,10 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _MATRIX_FILE_CACHE_H_ -#define _MATRIX_FILE_CACHE_H_ - -#include "base/ResizeableBitset.h" +#ifndef MATRIX_FILE_H +#define MATRIX_FILE_H #include "FileReadThread.h" @@ -91,7 +89,7 @@ int m_headerSize; QString m_fileName; - ResizeableBitset *m_setColumns; // only in writer + std::vector m_setColumns; // only populated in writer bool m_autoClose; // In reader: if this is >= 0, we can read that column directly diff -r e94719f941ba -r 659372323b45 data/fileio/WavFileReader.cpp --- a/data/fileio/WavFileReader.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/WavFileReader.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -62,20 +62,26 @@ m_seekable = (m_fileInfo.seekable != 0); - // Our m_seekable reports whether a file is rapidly seekable, - // so things like Ogg don't qualify. We cautiously report - // every file type of "at least" the historical period of Ogg - // or FLAC as non-seekable. int type = m_fileInfo.format & SF_FORMAT_TYPEMASK; -// cerr << "WavFileReader: format type is " << type << " (flac, ogg are " << SF_FORMAT_FLAC << ", " << SF_FORMAT_OGG << ")" << endl; + int subtype = m_fileInfo.format & SF_FORMAT_SUBMASK; + if (type >= SF_FORMAT_FLAC || type >= SF_FORMAT_OGG) { -// cerr << "WavFileReader: Recording as non-seekable" << endl; + // Our m_seekable reports whether a file is rapidly + // seekable, so things like Ogg don't qualify. We + // cautiously report every file type of "at least" the + // historical period of Ogg or FLAC as non-seekable. m_seekable = false; + } else if (type == SF_FORMAT_WAV && subtype <= SF_FORMAT_DOUBLE) { + // libsndfile 1.0.26 has a bug (subsequently fixed in the + // repo) that causes all files to be reported as + // non-seekable. We know that certain common file types + // are definitely seekable so, again cautiously, identify + // and mark those (basically only non-adaptive WAVs). + m_seekable = true; } } -// cerr << "WavFileReader: Frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", seekable " << m_seekable << endl; - +// cerr << "WavFileReader: Filename " << m_path << ", frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", format " << m_fileInfo.format << ", seekable " << m_fileInfo.seekable << " adjusted to " << m_seekable << endl; } WavFileReader::~WavFileReader() diff -r e94719f941ba -r 659372323b45 data/fileio/test/test.pro --- a/data/fileio/test/test.pro Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/test/test.pro Fri Aug 19 15:58:57 2016 +0100 @@ -1,6 +1,8 @@ TEMPLATE = app +INCLUDEPATH += ../../../../vamp-plugin-sdk + LIBS += -L../../.. -L../../../../dataquay -L../../../release -L../../../../dataquay/release -lsvcore -ldataquay win32-g++ { @@ -27,7 +29,7 @@ DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_VAMP HAVE_VAMPHOSTSDK HAVE_DATAQUAY HAVE_LIBLO HAVE_MAD HAVE_ID3TAG HAVE_PORTAUDIO - LIBS += -lbz2 -lvamp-hostsdk -lfftw3 -lfftw3f -lsndfile -lFLAC -logg -lvorbis -lvorbisenc -lvorbisfile -logg -lmad -lid3tag -lportaudio -lsamplerate -lz -lsord-0 -lserd-0 + LIBS += -lbz2 -lfftw3 -lfftw3f -lsndfile -lFLAC -logg -lvorbis -lvorbisenc -lvorbisfile -logg -lmad -lid3tag -lportaudio -lsamplerate -lz -lsord-0 -lserd-0 win* { LIBS += -llo -lwinmm -lws2_32 @@ -36,6 +38,9 @@ DEFINES += HAVE_COREAUDIO LIBS += -framework CoreAudio -framework CoreMidi -framework AudioUnit -framework AudioToolbox -framework CoreFoundation -framework CoreServices -framework Accelerate } + linux* { + LIBS += -ldl + } } CONFIG += qt thread warn_on stl rtti exceptions console c++11 diff -r e94719f941ba -r 659372323b45 data/model/Dense3DModelPeakCache.cpp --- a/data/model/Dense3DModelPeakCache.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/Dense3DModelPeakCache.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -17,13 +17,11 @@ #include "base/Profiler.h" -Dense3DModelPeakCache::Dense3DModelPeakCache(DenseThreeDimensionalModel *source, +Dense3DModelPeakCache::Dense3DModelPeakCache(const DenseThreeDimensionalModel *source, int columnsPerPeak) : m_source(source), - m_resolution(columnsPerPeak) + m_columnsPerPeak(columnsPerPeak) { - m_coverage.resize(1); // otherwise it is simply invalid - m_cache = new EditableDenseThreeDimensionalModel (source->getSampleRate(), getResolution(), @@ -43,20 +41,6 @@ delete m_cache; } -bool -Dense3DModelPeakCache::isColumnAvailable(int column) const -{ - if (!m_source) return false; - if (haveColumn(column)) return true; - for (int i = m_resolution; i > 0; ) { - --i; - if (!m_source->isColumnAvailable(column * m_resolution + i)) { - return false; - } - } - return true; -} - Dense3DModelPeakCache::Column Dense3DModelPeakCache::getColumn(int column) const { @@ -81,9 +65,9 @@ if (m_coverage.size() > 0) { // The last peak may have come from an incomplete read, which // may since have been filled, so reset it - m_coverage.reset(m_coverage.size()-1); + m_coverage[m_coverage.size()-1] = false; } - m_coverage.resize(getWidth()); // retaining data + m_coverage.resize(getWidth(), false); // retaining data } void @@ -95,7 +79,7 @@ bool Dense3DModelPeakCache::haveColumn(int column) const { - return column < (int)m_coverage.size() && m_coverage.get(column); + return in_range_for(m_coverage, column) && m_coverage[column]; } void @@ -103,26 +87,43 @@ { Profiler profiler("Dense3DModelPeakCache::fillColumn"); - if (column >= (int)m_coverage.size()) { - // see note in sourceModelChanged - if (m_coverage.size() > 0) m_coverage.reset(m_coverage.size()-1); - m_coverage.resize(column + 1); + if (!in_range_for(m_coverage, column)) { + if (m_coverage.size() > 0) { + // The last peak may have come from an incomplete read, which + // may since have been filled, so reset it + m_coverage[m_coverage.size()-1] = false; + } + m_coverage.resize(column + 1, false); } + int sourceWidth = m_source->getWidth(); + Column peak; - for (int i = 0; i < int(m_resolution); ++i) { - Column here = m_source->getColumn(column * m_resolution + i); + int n = 0; + for (int i = 0; i < m_columnsPerPeak; ++i) { + + int sourceColumn = column * m_columnsPerPeak + i; + if (sourceColumn >= sourceWidth) break; + + Column here = m_source->getColumn(sourceColumn); + +// cerr << "Dense3DModelPeakCache::fillColumn(" << column << "): source col " +// << sourceColumn << " of " << sourceWidth +// << " returned " << here.size() << " elts" << endl; + if (i == 0) { peak = here; + n = int(peak.size()); } else { - for (int j = 0; j < (int)peak.size() && j < (int)here.size(); ++j) { - if (here[j] > peak[j]) peak[j] = here[j]; + int m = std::min(n, int(here.size())); + for (int j = 0; j < m; ++j) { + peak[j] = std::max(here[j], peak[j]); } } } m_cache->setColumn(column, peak); - m_coverage.set(column); + m_coverage[column] = true; } diff -r e94719f941ba -r 659372323b45 data/model/Dense3DModelPeakCache.h --- a/data/model/Dense3DModelPeakCache.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/Dense3DModelPeakCache.h Fri Aug 19 15:58:57 2016 +0100 @@ -13,19 +13,18 @@ COPYING included with this distribution for more information. */ -#ifndef _DENSE_3D_MODEL_PEAK_CACHE_H_ -#define _DENSE_3D_MODEL_PEAK_CACHE_H_ +#ifndef DENSE_3D_MODEL_PEAK_CACHE_H +#define DENSE_3D_MODEL_PEAK_CACHE_H #include "DenseThreeDimensionalModel.h" #include "EditableDenseThreeDimensionalModel.h" -#include "base/ResizeableBitset.h" class Dense3DModelPeakCache : public DenseThreeDimensionalModel { Q_OBJECT public: - Dense3DModelPeakCache(DenseThreeDimensionalModel *source, + Dense3DModelPeakCache(const DenseThreeDimensionalModel *source, int columnsPerPeak); ~Dense3DModelPeakCache(); @@ -46,11 +45,20 @@ } virtual int getResolution() const { - return m_source->getResolution() * m_resolution; + return m_source->getResolution() * m_columnsPerPeak; } + virtual int getColumnsPerPeak() const { + return m_columnsPerPeak; + } + virtual int getWidth() const { - return m_source->getWidth() / m_resolution + 1; + int sourceWidth = m_source->getWidth(); + if ((sourceWidth % m_columnsPerPeak) == 0) { + return sourceWidth / m_columnsPerPeak; + } else { + return sourceWidth / m_columnsPerPeak + 1; + } } virtual int getHeight() const { @@ -65,8 +73,6 @@ return m_source->getMaximumLevel(); } - virtual bool isColumnAvailable(int column) const; - virtual Column getColumn(int column) const; virtual float getValueAt(int column, int n) const; @@ -90,10 +96,11 @@ void sourceModelAboutToBeDeleted(); private: - DenseThreeDimensionalModel *m_source; + const DenseThreeDimensionalModel *m_source; mutable EditableDenseThreeDimensionalModel *m_cache; - mutable ResizeableBitset m_coverage; - int m_resolution; + mutable std::vector m_coverage; // must be bool, for space efficiency + // (vector of bool uses 1-bit elements) + int m_columnsPerPeak; bool haveColumn(int column) const; void fillColumn(int column) const; diff -r e94719f941ba -r 659372323b45 data/model/DenseThreeDimensionalModel.h --- a/data/model/DenseThreeDimensionalModel.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/DenseThreeDimensionalModel.h Fri Aug 19 15:58:57 2016 +0100 @@ -18,6 +18,7 @@ #include "Model.h" #include "TabularModel.h" +#include "base/ColumnOp.h" #include "base/ZoomConstraint.h" #include "base/RealTime.h" @@ -55,16 +56,7 @@ */ virtual float getMaximumLevel() const = 0; - /** - * Return true if there are data available for the given column. - * This should return true only if getColumn(column) would not - * have to do any substantial work to calculate its return values. - * If this function returns false, it may still be possible to - * retrieve the column, but its values may have to be calculated. - */ - virtual bool isColumnAvailable(int column) const = 0; - - typedef QVector Column; + typedef ColumnOp::Column Column; /** * Get data from the given column of bin values. @@ -153,12 +145,12 @@ { switch (column) { case 0: { - RealTime rt = RealTime::frame2RealTime(row * getResolution(), - getSampleRate()); + RealTime rt = RealTime::frame2RealTime + (row * getResolution() + getStartFrame(), getSampleRate()); return rt.toText().c_str(); } case 1: - return int(row * getResolution()); + return int(row * getResolution() + getStartFrame()); default: return getValueAt(row, column - 2); } @@ -172,10 +164,10 @@ } virtual sv_frame_t getFrameForRow(int row) const { - return sv_frame_t(row) * getResolution(); + return sv_frame_t(row) * getResolution() + getStartFrame(); } virtual int getRowForFrame(sv_frame_t frame) const { - return int(frame / getResolution()); + return int((frame - getStartFrame()) / getResolution()); } protected: diff -r e94719f941ba -r 659372323b45 data/model/EditableDenseThreeDimensionalModel.cpp --- a/data/model/EditableDenseThreeDimensionalModel.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/EditableDenseThreeDimensionalModel.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -96,7 +96,7 @@ int EditableDenseThreeDimensionalModel::getWidth() const { - return m_data.size(); + return int(m_data.size()); } int @@ -139,15 +139,15 @@ EditableDenseThreeDimensionalModel::getColumn(int index) const { QReadLocker locker(&m_lock); - if (index < 0 || index >= m_data.size()) return Column(); - return expandAndRetrieve(index); + if (in_range_for(m_data, index)) return expandAndRetrieve(index); + else return Column(); } float EditableDenseThreeDimensionalModel::getValueAt(int index, int n) const { Column c = getColumn(index); - if (int(n) < c.size()) return c.at(n); + if (in_range_for(c, n)) return c.at(n); return m_minimum; } @@ -157,7 +157,7 @@ EditableDenseThreeDimensionalModel::truncateAndStore(int index, const Column &values) { - assert(int(index) < m_data.size()); + assert(in_range_for(m_data, index)); //cout << "truncateAndStore(" << index << ", " << values.size() << ")" << endl; @@ -169,7 +169,7 @@ m_trunc[index] = 0; if (index == 0 || m_compression == NoCompression || - values.size() != int(m_yBinCount)) { + int(values.size()) != m_yBinCount) { // given += values.size(); // stored += values.size(); m_data[index] = values; @@ -206,7 +206,7 @@ Column p = expandAndRetrieve(index - tdist); int h = m_yBinCount; - if (p.size() == h && tdist <= maxdist) { + if (int(p.size()) == h && tdist <= maxdist) { int bcount = 0, tcount = 0; if (!known || !top) { @@ -282,7 +282,7 @@ int tdist = trunc; if (trunc < 0) { top = false; tdist = -trunc; } Column p = expandAndRetrieve(index - tdist); - int psize = p.size(), csize = c.size(); + int psize = int(p.size()), csize = int(c.size()); if (psize != m_yBinCount) { cerr << "WARNING: EditableDenseThreeDimensionalModel::expandAndRetrieve: Trying to expand from incorrectly sized column" << endl; } @@ -291,10 +291,6 @@ c.push_back(p.at(i)); } } else { - // push_front is very slow on QVector -- but not enough to - // make it desirable to choose a different container, since - // QVector has all the other advantages for us. easier to - // write the whole array out to a new vector Column cc(psize); for (int i = 0; i < psize - csize; ++i) { cc[i] = p.at(i); @@ -313,7 +309,7 @@ { QWriteLocker locker(&m_lock); - while (int(index) >= m_data.size()) { + while (index >= int(m_data.size())) { m_data.push_back(Column()); m_trunc.push_back(0); } @@ -322,7 +318,7 @@ // if (values.size() > m_yBinCount) m_yBinCount = values.size(); - for (int i = 0; i < values.size(); ++i) { + for (int i = 0; in_range_for(values, i); ++i) { float value = values[i]; if (ISNAN(value) || ISINF(value)) { continue; @@ -432,13 +428,13 @@ for (int i = 0; i < 10; ++i) { int index = i * 10; - if (index < m_data.size()) { + if (in_range_for(m_data, index)) { const Column &c = m_data.at(index); - while (c.size() > int(sample.size())) { + while (c.size() > sample.size()) { sample.push_back(0.0); n.push_back(0); } - for (int j = 0; j < c.size(); ++j) { + for (int j = 0; in_range_for(c, j); ++j) { sample[j] += c.at(j); ++n[j]; } @@ -486,9 +482,9 @@ { QReadLocker locker(&m_lock); QString s; - for (int i = 0; i < m_data.size(); ++i) { + for (int i = 0; in_range_for(m_data, i); ++i) { QStringList list; - for (int j = 0; j < m_data.at(i).size(); ++j) { + for (int j = 0; in_range_for(m_data.at(i), j); ++j) { list << QString("%1").arg(m_data.at(i).at(j)); } s += list.join(delimiter) + "\n"; @@ -501,11 +497,11 @@ { QReadLocker locker(&m_lock); QString s; - for (int i = 0; i < m_data.size(); ++i) { + for (int i = 0; in_range_for(m_data, i); ++i) { sv_frame_t fr = m_startFrame + i * m_resolution; if (fr >= f0 && fr < f1) { QStringList list; - for (int j = 0; j < m_data.at(i).size(); ++j) { + for (int j = 0; in_range_for(m_data.at(i), j); ++j) { list << QString("%1").arg(m_data.at(i).at(j)); } s += list.join(delimiter) + "\n"; diff -r e94719f941ba -r 659372323b45 data/model/EditableDenseThreeDimensionalModel.h --- a/data/model/EditableDenseThreeDimensionalModel.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/EditableDenseThreeDimensionalModel.h Fri Aug 19 15:58:57 2016 +0100 @@ -105,11 +105,6 @@ virtual void setMaximumLevel(float sz); /** - * Return true if there are data available for the given column. - */ - virtual bool isColumnAvailable(int x) const { return x < getWidth(); } - - /** * Get the set of bin values at the given column. */ virtual Column getColumn(int x) const; @@ -194,7 +189,7 @@ QString extraAttributes = "") const; protected: - typedef QVector ValueMatrix; + typedef std::vector ValueMatrix; ValueMatrix m_data; // m_trunc is used for simple compression. If at least the top N diff -r e94719f941ba -r 659372323b45 data/model/FFTModel.cpp --- a/data/model/FFTModel.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/FFTModel.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -98,9 +98,21 @@ { auto cplx = getFFTColumn(x); Column col; - col.reserve(int(cplx.size())); + col.reserve(cplx.size()); for (auto c: cplx) col.push_back(abs(c)); - return col; + return move(col); +} + +FFTModel::Column +FFTModel::getPhases(int x) const +{ + auto cplx = getFFTColumn(x); + Column col; + col.reserve(cplx.size()); + for (auto c: cplx) { + col.push_back(arg(c)); + } + return move(col); } float @@ -116,7 +128,8 @@ { Column col(getColumn(x)); float max = 0.f; - for (int i = 0; i < col.size(); ++i) { + int n = int(col.size()); + for (int i = 0; i < n; ++i) { if (col[i] > max) max = col[i]; } return max; @@ -138,13 +151,6 @@ } bool -FFTModel::isColumnAvailable(int) const -{ - //!!! - return true; -} - -bool FFTModel::getMagnitudesAt(int x, float *values, int minbin, int count) const { if (count == 0) count = getHeight(); @@ -155,23 +161,6 @@ return true; } -float -FFTModel::getNormalizedMagnitudesAt(int x, float *values, int minbin, int count) const -{ - if (!getMagnitudesAt(x, values, minbin, count)) return false; - if (count == 0) count = getHeight(); - float max = 0.f; - for (int i = 0; i < count; ++i) { - if (values[i] > max) max = values[i]; - } - if (max > 0.f) { - for (int i = 0; i < count; ++i) { - values[i] /= max; - } - } - return max; -} - bool FFTModel::getPhasesAt(int x, float *values, int minbin, int count) const { @@ -313,7 +302,7 @@ } m_cached.push_back(sc); - return col; + return move(col); } bool @@ -354,7 +343,7 @@ } FFTModel::PeakLocationSet -FFTModel::getPeaks(PeakPickType type, int x, int ymin, int ymax) +FFTModel::getPeaks(PeakPickType type, int x, int ymin, int ymax) const { Profiler profiler("FFTModel::getPeaks"); @@ -388,10 +377,11 @@ } Column values = getColumn(x); + int nv = int(values.size()); float mean = 0.f; - for (int i = 0; i < values.size(); ++i) mean += values[i]; - if (values.size() > 0) mean = mean / float(values.size()); + for (int i = 0; i < nv; ++i) mean += values[i]; + if (nv > 0) mean = mean / float(values.size()); // For peak picking we use a moving median window, picking the // highest value within each continuous region of values that @@ -412,8 +402,8 @@ else binmin = 0; int binmax; - if (ymax + halfWin < values.size()) binmax = ymax + halfWin; - else binmax = values.size()-1; + if (ymax + halfWin < nv) binmax = ymax + halfWin; + else binmax = nv - 1; int prevcentre = 0; @@ -434,8 +424,8 @@ int actualSize = int(window.size()); if (type == MajorPitchAdaptivePeaks) { - if (ymax + halfWin < values.size()) binmax = ymax + halfWin; - else binmax = values.size()-1; + if (ymax + halfWin < nv) binmax = ymax + halfWin; + else binmax = nv - 1; } deque sorted(window); @@ -455,7 +445,7 @@ inrange.push_back(centrebin); } - if (centre <= median || centrebin+1 == values.size()) { + if (centre <= median || centrebin+1 == nv) { if (!inrange.empty()) { int peakbin = 0; float peakval = 0.f; @@ -501,7 +491,7 @@ FFTModel::PeakSet FFTModel::getPeakFrequencies(PeakPickType type, int x, - int ymin, int ymax) + int ymin, int ymax) const { Profiler profiler("FFTModel::getPeakFrequencies"); diff -r e94719f941ba -r 659372323b45 data/model/FFTModel.h --- a/data/model/FFTModel.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/FFTModel.h Fri Aug 19 15:58:57 2016 +0100 @@ -76,6 +76,7 @@ virtual float getMinimumLevel() const { return 0.f; } // Can't provide virtual float getMaximumLevel() const { return 1.f; } // Can't provide virtual Column getColumn(int x) const; // magnitudes + virtual Column getPhases(int x) const; virtual QString getBinName(int n) const; virtual bool shouldUseLogValueScale() const { return true; } virtual int getCompletion() const { @@ -95,14 +96,14 @@ int getWindowSize() const { return m_windowSize; } int getWindowIncrement() const { return m_windowIncrement; } int getFFTSize() const { return m_fftSize; } + +//!!! review which of these are ever actually called float getMagnitudeAt(int x, int y) const; float getMaximumMagnitudeAt(int x) const; float getPhaseAt(int x, int y) const; void getValuesAt(int x, int y, float &real, float &imaginary) const; - bool isColumnAvailable(int x) const; bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) const; - float getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) const; // returns maximum of unnormalized magnitudes bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0) const; bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0) const; @@ -128,13 +129,13 @@ * ymax is zero, getHeight()-1 will be used. */ virtual PeakLocationSet getPeaks(PeakPickType type, int x, - int ymin = 0, int ymax = 0); + int ymin = 0, int ymax = 0) const; /** * Return locations and estimated stable frequencies of peak bins. */ virtual PeakSet getPeakFrequencies(PeakPickType type, int x, - int ymin = 0, int ymax = 0); + int ymin = 0, int ymax = 0) const; QString getTypeName() const { return tr("FFT"); } diff -r e94719f941ba -r 659372323b45 data/model/ReadOnlyWaveFileModel.cpp --- a/data/model/ReadOnlyWaveFileModel.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/ReadOnlyWaveFileModel.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -193,7 +193,7 @@ // playback or input to transforms. #ifdef DEBUG_WAVE_FILE_MODEL - cout << "ReadOnlyWaveFileModel::getData[" << this << "]: " << channel << ", " << start << ", " << count << ", " << buffer << endl; + cout << "ReadOnlyWaveFileModel::getData[" << this << "]: " << channel << ", " << start << ", " << count << endl; #endif int channels = getChannelCount(); @@ -252,7 +252,7 @@ // playback or input to transforms. #ifdef DEBUG_WAVE_FILE_MODEL - cout << "ReadOnlyWaveFileModel::getData[" << this << "]: " << fromchannel << "," << tochannel << ", " << start << ", " << count << ", " << buffer << endl; + cout << "ReadOnlyWaveFileModel::getData[" << this << "]: " << fromchannel << "," << tochannel << ", " << start << ", " << count << endl; #endif int channels = getChannelCount(); @@ -322,7 +322,7 @@ void ReadOnlyWaveFileModel::getSummaries(int channel, sv_frame_t start, sv_frame_t count, - RangeBlock &ranges, int &blockSize) const + RangeBlock &ranges, int &blockSize) const { ranges.clear(); if (!isOK()) return; @@ -403,14 +403,12 @@ blockSize = roundedBlockSize; sv_frame_t cacheBlock, div; - - if (cacheType == 0) { - cacheBlock = (1 << m_zoomConstraint.getMinCachePower()); - div = (1 << power) / cacheBlock; - } else { - cacheBlock = sv_frame_t((1 << m_zoomConstraint.getMinCachePower()) * sqrt(2.) + 0.01); - div = sv_frame_t(((1 << power) * sqrt(2.) + 0.01) / double(cacheBlock)); + + cacheBlock = (sv_frame_t(1) << m_zoomConstraint.getMinCachePower()); + if (cacheType == 1) { + cacheBlock = sv_frame_t(double(cacheBlock) * sqrt(2.) + 0.01); } + div = blockSize / cacheBlock; sv_frame_t startIndex = start / cacheBlock; sv_frame_t endIndex = (start + count) / cacheBlock; @@ -425,7 +423,7 @@ for (i = 0; i <= endIndex - startIndex; ) { sv_frame_t index = (i + startIndex) * channels + channel; - if (index >= (sv_frame_t)cache.size()) break; + if (!in_range_for(cache, index)) break; const Range &range = cache[index]; if (range.max() > max || got == 0) max = range.max(); @@ -448,7 +446,7 @@ } #ifdef DEBUG_WAVE_FILE_MODEL - SVDEBUG << "returning " << ranges.size() << " ranges" << endl; + cerr << "returning " << ranges.size() << " ranges" << endl; #endif return; } @@ -573,7 +571,7 @@ sqrt(2.) + 0.01)); sv_frame_t frame = 0; - const sv_frame_t readBlockSize = 16384; + const sv_frame_t readBlockSize = 32768; vector block; if (!m_model.isOK()) return; @@ -583,7 +581,9 @@ if (updating) { while (channels == 0 && !m_model.m_exiting) { -// SVDEBUG << "ReadOnlyWaveFileModel::fill: Waiting for channels..." << endl; +#ifdef DEBUG_WAVE_FILE_MODEL + cerr << "ReadOnlyWaveFileModel::fill: Waiting for channels..." << endl; +#endif sleep(1); channels = m_model.getChannelCount(); } @@ -604,38 +604,38 @@ updating = m_model.m_reader->isUpdating(); m_frameCount = m_model.getFrameCount(); -// SVDEBUG << "ReadOnlyWaveFileModel::fill: frame = " << frame << ", count = " << m_frameCount << endl; + m_model.m_mutex.lock(); while (frame < m_frameCount) { -// SVDEBUG << "ReadOnlyWaveFileModel::fill inner loop: frame = " << frame << ", count = " << m_frameCount << ", blocksize " << readBlockSize << endl; + m_model.m_mutex.unlock(); + +#ifdef DEBUG_WAVE_FILE_MODEL + cerr << "ReadOnlyWaveFileModel::fill inner loop: frame = " << frame << ", count = " << m_frameCount << ", blocksize " << readBlockSize << endl; +#endif if (updating && (frame + readBlockSize > m_frameCount)) break; block = m_model.m_reader->getInterleavedFrames(frame, readBlockSize); -// cerr << "block is " << block.size() << endl; + sv_frame_t gotBlockSize = block.size() / channels; - for (sv_frame_t i = 0; i < readBlockSize; ++i) { + m_model.m_mutex.lock(); + + for (sv_frame_t i = 0; i < gotBlockSize; ++i) { - if (channels * i + channels > (int)block.size()) break; - for (int ch = 0; ch < channels; ++ch) { sv_frame_t index = channels * i + ch; float sample = block[index]; - for (int cacheType = 0; cacheType < 2; ++cacheType) { // cache type - + for (int cacheType = 0; cacheType < 2; ++cacheType) { sv_frame_t rangeIndex = ch * 2 + cacheType; range[rangeIndex].sample(sample); means[rangeIndex] += fabsf(sample); } } - //!!! this looks like a ludicrous way to do synchronisation - QMutexLocker locker(&m_model.m_mutex); - for (int cacheType = 0; cacheType < 2; ++cacheType) { if (++count[cacheType] == cacheBlockSize[cacheType]) { @@ -655,18 +655,16 @@ ++frame; } - + if (m_model.m_exiting) break; - m_fillExtent = frame; } -// cerr << "ReadOnlyWaveFileModel: inner loop ended" << endl; - + m_model.m_mutex.unlock(); + first = false; if (m_model.m_exiting) break; if (updating) { -// cerr << "sleeping..." << endl; sleep(1); } } diff -r e94719f941ba -r 659372323b45 data/model/test/test.pro --- a/data/model/test/test.pro Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/test/test.pro Fri Aug 19 15:58:57 2016 +0100 @@ -1,6 +1,8 @@ TEMPLATE = app +INCLUDEPATH += ../../../../vamp-plugin-sdk + LIBS += -L../../.. -L../../../../dataquay -L../../../release -L../../../../dataquay/release -lsvcore -ldataquay win32-g++ { @@ -27,7 +29,7 @@ DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_VAMP HAVE_VAMPHOSTSDK HAVE_DATAQUAY HAVE_LIBLO HAVE_MAD HAVE_ID3TAG HAVE_PORTAUDIO - LIBS += -lbz2 -lvamp-hostsdk -lfftw3 -lfftw3f -lsndfile -lFLAC -logg -lvorbis -lvorbisenc -lvorbisfile -logg -lmad -lid3tag -lportaudio -lsamplerate -lz -lsord-0 -lserd-0 + LIBS += -lbz2 -lfftw3 -lfftw3f -lsndfile -lFLAC -logg -lvorbis -lvorbisenc -lvorbisfile -logg -lmad -lid3tag -lportaudio -lsamplerate -lz -lsord-0 -lserd-0 win* { LIBS += -llo -lwinmm -lws2_32 diff -r e94719f941ba -r 659372323b45 plugin/DSSIPluginFactory.h --- a/plugin/DSSIPluginFactory.h Tue Oct 20 12:54:06 2015 +0100 +++ b/plugin/DSSIPluginFactory.h Fri Aug 19 15:58:57 2016 +0100 @@ -48,6 +48,10 @@ DSSIPluginFactory(); friend class RealTimePluginFactory; + virtual PluginScan::PluginType getPluginType() const { + return PluginScan::DSSIPlugin; + } + virtual std::vector getPluginPath(); virtual std::vector getLRDFPath(QString &baseUri); diff -r e94719f941ba -r 659372323b45 plugin/FeatureExtractionPluginFactory.cpp --- a/plugin/FeatureExtractionPluginFactory.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/plugin/FeatureExtractionPluginFactory.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -21,6 +21,8 @@ #include "system/System.h" +#include "PluginScan.h" + #include #include #include @@ -30,6 +32,8 @@ #include "base/Profiler.h" +using namespace std; + //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1 class PluginDeletionNotifyAdapter : public Vamp::HostExt::PluginWrapper { @@ -77,25 +81,25 @@ return instance(type); } -std::vector +vector FeatureExtractionPluginFactory::getPluginPath() { if (!m_pluginPath.empty()) return m_pluginPath; - std::vector p = Vamp::PluginHostAdapter::getPluginPath(); + vector p = Vamp::PluginHostAdapter::getPluginPath(); for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str()); return m_pluginPath; } -std::vector +vector FeatureExtractionPluginFactory::getAllPluginIdentifiers() { FeatureExtractionPluginFactory *factory; - std::vector rv; + vector rv; factory = instance("vamp"); if (factory) { - std::vector tmp = factory->getPluginIdentifiers(); + vector tmp = factory->getPluginIdentifiers(); for (size_t i = 0; i < tmp.size(); ++i) { // cerr << "identifier: " << tmp[i] << endl; rv.push_back(tmp[i]); @@ -108,103 +112,84 @@ return rv; } -std::vector +vector FeatureExtractionPluginFactory::getPluginIdentifiers() { Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers"); - std::vector rv; - std::vector path = getPluginPath(); + vector rv; + + QStringList candidates = PluginScan::getInstance()->getCandidateLibrariesFor + (PluginScan::VampPlugin); - for (std::vector::iterator i = path.begin(); i != path.end(); ++i) { + for (QString soname : candidates) { + + void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); + + if (!libraryHandle) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl; + continue; + } + + VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) + DLSYM(libraryHandle, "vampGetPluginDescriptor"); + + if (!fn) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl; + if (DLCLOSE(libraryHandle) != 0) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; + } + continue; + } #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i-<< endl; + cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl; #endif - QDir pluginDir(*i, PLUGIN_GLOB, - QDir::Name | QDir::IgnoreCase, - QDir::Files | QDir::Readable); + const VampPluginDescriptor *descriptor = 0; + int index = 0; - for (unsigned int j = 0; j < pluginDir.count(); ++j) { + map known; + bool ok = true; - QString soname = pluginDir.filePath(pluginDir[j]); + while ((descriptor = fn(VAMP_API_VERSION, index))) { -#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname << endl; -#endif - - void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); - - if (!libraryHandle) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl; - continue; + if (known.find(descriptor->identifier) != known.end()) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Plugin library " + << soname + << " returns the same plugin identifier \"" + << descriptor->identifier << "\" at indices " + << known[descriptor->identifier] << " and " + << index << endl; + cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl; + ok = false; + break; + } else { + known[descriptor->identifier] = index; } -#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << endl; -#endif + ++index; + } - VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) - DLSYM(libraryHandle, "vampGetPluginDescriptor"); + if (ok) { - if (!fn) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl; - if (DLCLOSE(libraryHandle) != 0) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; - } - continue; - } - -#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl; -#endif - - const VampPluginDescriptor *descriptor = 0; - int index = 0; - - std::map known; - bool ok = true; + index = 0; while ((descriptor = fn(VAMP_API_VERSION, index))) { - if (known.find(descriptor->identifier) != known.end()) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Plugin library " - << soname - << " returns the same plugin identifier \"" - << descriptor->identifier << "\" at indices " - << known[descriptor->identifier] << " and " - << index << endl; - SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl; - ok = false; - break; - } else { - known[descriptor->identifier] = index; - } - + QString id = PluginIdentifier::createIdentifier + ("vamp", soname, descriptor->identifier); + rv.push_back(id); +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl; +#endif ++index; } - - if (ok) { - - index = 0; - - while ((descriptor = fn(VAMP_API_VERSION, index))) { - - QString id = PluginIdentifier::createIdentifier - ("vamp", soname, descriptor->identifier); - rv.push_back(id); -#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl; -#endif - ++index; - } - } + } - if (DLCLOSE(libraryHandle) != 0) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; - } - } + if (DLCLOSE(libraryHandle) != 0) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; + } } generateTaxonomy(); @@ -218,7 +203,7 @@ QString file = ""; #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile(\"" + cerr << "FeatureExtractionPluginFactory::findPluginFile(\"" << soname << "\", \"" << inDir << "\")" << endl; #endif @@ -235,7 +220,7 @@ if (QFileInfo(file).exists() && QFileInfo(file).isFile()) { #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: " + cerr << "FeatureExtractionPluginFactory::findPluginFile: " << "found trivially at " << file << endl; #endif @@ -247,7 +232,7 @@ if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) { #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: " + cerr << "FeatureExtractionPluginFactory::findPluginFile: " << "found \"" << soname << "\" at " << file << endl; #endif @@ -256,7 +241,7 @@ } #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile (with dir): " + cerr << "FeatureExtractionPluginFactory::findPluginFile (with dir): " << "not found" << endl; #endif @@ -268,7 +253,7 @@ if (fi.isAbsolute() && fi.exists() && fi.isFile()) { #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: " + cerr << "FeatureExtractionPluginFactory::findPluginFile: " << "found trivially at " << soname << endl; #endif return soname; @@ -279,8 +264,8 @@ if (file != "") return file; } - std::vector path = getPluginPath(); - for (std::vector::iterator i = path.begin(); + vector path = getPluginPath(); + for (vector::iterator i = path.begin(); i != path.end(); ++i) { if (*i != "") { file = findPluginFile(soname, *i); @@ -289,7 +274,7 @@ } #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::findPluginFile: " + cerr << "FeatureExtractionPluginFactory::findPluginFile: " << "not found" << endl; #endif @@ -312,7 +297,9 @@ QString type, soname, label; PluginIdentifier::parseIdentifier(identifier, type, soname, label); if (type != "vamp") { - SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl; +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl; +#endif return 0; } @@ -324,7 +311,7 @@ } else if (found != soname) { #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Given library name was " << soname << ", found at " << found << endl; + cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Given library name was " << soname << ", found at " << found << endl; cerr << soname << " -> " << found << endl; #endif @@ -343,7 +330,7 @@ DLSYM(libraryHandle, "vampGetPluginDescriptor"); if (!fn) { - SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname << endl; + cerr << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname << endl; goto done; } @@ -375,7 +362,9 @@ } } -// SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl; +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl; +#endif return rv; } @@ -385,7 +374,9 @@ { void *handle = m_handleMap[plugin]; if (handle) { -// SVDEBUG << "unloading library " << handle << " for plugin " << plugin << endl; +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "unloading library " << handle << " for plugin " << plugin << endl; +#endif DLCLOSE(handle); } m_handleMap.erase(plugin); @@ -400,8 +391,8 @@ void FeatureExtractionPluginFactory::generateTaxonomy() { - std::vector pluginPath = getPluginPath(); - std::vector path; + vector pluginPath = getPluginPath(); + vector path; for (size_t i = 0; i < pluginPath.size(); ++i) { if (pluginPath[i].contains("/lib/")) { diff -r e94719f941ba -r 659372323b45 plugin/FeatureExtractionPluginFactory.h --- a/plugin/FeatureExtractionPluginFactory.h Tue Oct 20 12:54:06 2015 +0100 +++ b/plugin/FeatureExtractionPluginFactory.h Fri Aug 19 15:58:57 2016 +0100 @@ -37,7 +37,7 @@ virtual std::vector getPluginPath(); virtual std::vector getPluginIdentifiers(); - + virtual QString findPluginFile(QString soname, QString inDir = ""); // We don't set blockSize or channels on this -- they're @@ -57,7 +57,7 @@ friend class PluginDeletionNotifyAdapter; void pluginDeleted(Vamp::Plugin *); std::map m_handleMap; - + void generateTaxonomy(); }; diff -r e94719f941ba -r 659372323b45 plugin/LADSPAPluginFactory.cpp --- a/plugin/LADSPAPluginFactory.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/plugin/LADSPAPluginFactory.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -668,14 +668,11 @@ generateFallbackCategories(); - for (std::vector::iterator i = pathList.begin(); - i != pathList.end(); ++i) { + QStringList candidates = + PluginScan::getInstance()->getCandidateLibrariesFor(getPluginType()); - QDir pluginDir(*i, PLUGIN_GLOB); - - for (unsigned int j = 0; j < pluginDir.count(); ++j) { - discoverPluginsFrom(QString("%1/%2").arg(*i).arg(pluginDir[j])); - } + for (QString c: candidates) { + discoverPluginsFrom(c); } } diff -r e94719f941ba -r 659372323b45 plugin/LADSPAPluginFactory.h --- a/plugin/LADSPAPluginFactory.h Tue Oct 20 12:54:06 2015 +0100 +++ b/plugin/LADSPAPluginFactory.h Fri Aug 19 15:58:57 2016 +0100 @@ -24,6 +24,8 @@ #include "RealTimePluginFactory.h" #include "api/ladspa.h" +#include "PluginScan.h" + #include #include #include @@ -63,6 +65,10 @@ LADSPAPluginFactory(); friend class RealTimePluginFactory; + virtual PluginScan::PluginType getPluginType() const { + return PluginScan::LADSPAPlugin; + } + virtual std::vector getPluginPath(); virtual std::vector getLRDFPath(QString &baseUri); diff -r e94719f941ba -r 659372323b45 plugin/PluginScan.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/PluginScan.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -0,0 +1,112 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#include "PluginScan.h" + +#include "base/Debug.h" + +#include "checker/knownplugins.h" + +#include +#include + +using std::string; + +class PluginScan::Logger : public PluginCandidates::LogCallback +{ +protected: + void log(std::string message) { + SVDEBUG << "PluginScan: " << message; + } +}; + +PluginScan *PluginScan::getInstance() +{ + static QMutex mutex; + static PluginScan *m_instance = 0; + mutex.lock(); + if (!m_instance) m_instance = new PluginScan(); + mutex.unlock(); + return m_instance; +} + +PluginScan::PluginScan() : m_kp(0), m_succeeded(false), m_logger(new Logger) { +} + +PluginScan::~PluginScan() { + delete m_kp; + delete m_logger; +} + +void +PluginScan::scan(QString helperExecutablePath) +{ + delete m_kp; + m_succeeded = false; + try { + m_kp = new KnownPlugins(helperExecutablePath.toStdString(), m_logger); + m_succeeded = true; + } catch (const std::exception &e) { + cerr << "ERROR: PluginScan::scan: " << e.what() << endl; + m_kp = 0; + } +} + +QStringList +PluginScan::getCandidateLibrariesFor(PluginType type) const +{ + KnownPlugins::PluginType kpt; + switch (type) { + case VampPlugin: kpt = KnownPlugins::VampPlugin; break; + case LADSPAPlugin: kpt = KnownPlugins::LADSPAPlugin; break; + case DSSIPlugin: kpt = KnownPlugins::DSSIPlugin; break; + default: throw std::logic_error("Inconsistency in plugin type enums"); + } + + QStringList candidates; + if (!m_kp) return candidates; + auto c = m_kp->getCandidateLibrariesFor(kpt); + for (auto s: c) candidates.push_back(s.c_str()); + return candidates; +} + +QString +PluginScan::getStartupFailureReport() const +{ + if (!m_succeeded) { + return QObject::tr("Failed to scan for plugins" + "

Failed to scan for plugins at startup. Possibly " + "the plugin checker helper program was not correctly " + "installed alongside %1?

") + .arg(QCoreApplication::applicationName()); + } + if (!m_kp) { + return QObject::tr("Did not scan for plugins" + "

Apparently no scan for plugins was attempted " + "(internal error?)

"); + } + + string report = m_kp->getFailureReport(); + if (report == "") { + return QString(report.c_str()); + } + + return QObject::tr("Failed to load plugins" + "

Failed to load one or more plugin libraries:

") + + QString(report.c_str()) + + QObject::tr("

These plugins may be incompatible with the system, " + "and will be ignored during this run of %1.

") + .arg(QCoreApplication::applicationName()); +} + diff -r e94719f941ba -r 659372323b45 plugin/PluginScan.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/PluginScan.h Fri Aug 19 15:58:57 2016 +0100 @@ -0,0 +1,50 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef PLUGIN_SCAN_H +#define PLUGIN_SCAN_H + +#include + +class KnownPlugins; + +class PluginScan +{ +public: + static PluginScan *getInstance(); + + void scan(QString helperExecutablePath); + + bool scanSucceeded() const; + + enum PluginType { + VampPlugin, + LADSPAPlugin, + DSSIPlugin + }; + QStringList getCandidateLibrariesFor(PluginType) const; + + QString getStartupFailureReport() const; + +private: + PluginScan(); + ~PluginScan(); + KnownPlugins *m_kp; + bool m_succeeded; + + class Logger; + Logger *m_logger; +}; + +#endif diff -r e94719f941ba -r 659372323b45 plugin/plugins/SamplePlayer.cpp --- a/plugin/plugins/SamplePlayer.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/plugin/plugins/SamplePlayer.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -571,7 +571,7 @@ if (m_concertA) { ratio *= *m_concertA / 440.f; } - if (m_basePitch && n != *m_basePitch) { + if (m_basePitch && float(n) != *m_basePitch) { ratio *= powf(1.059463094f, float(n) - *m_basePitch); } } diff -r e94719f941ba -r 659372323b45 rdf/RDFFeatureWriter.cpp --- a/rdf/RDFFeatureWriter.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/rdf/RDFFeatureWriter.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -352,7 +352,7 @@ bool wantTrack = (userSpecifiedTrack || (m_userMakerUri != "") || - (m_metadata.find(trackId) != m_metadata.end())); + haveTitleArtistMetadata(trackId)); // cerr << "wantTrack = " << wantTrack << " (userSpecifiedTrack = " // << userSpecifiedTrack << ", m_userMakerUri = " << m_userMakerUri << ", have metadata = " << (m_metadata.find(trackId) != m_metadata.end()) << ")" << endl; @@ -367,7 +367,7 @@ // including a Track would be to assert that this was one, // which is the one thing we wouldn't know... TrackMetadata tm; - if (m_metadata.find(trackId) != m_metadata.end()) { + if (haveTitleArtistMetadata(trackId)) { tm = m_metadata[trackId]; } stream << trackURI << " a mo:Track "; diff -r e94719f941ba -r 659372323b45 rdf/RDFFeatureWriter.h --- a/rdf/RDFFeatureWriter.h Tue Oct 20 12:54:06 2015 +0100 +++ b/rdf/RDFFeatureWriter.h Fri Aug 19 15:58:57 2016 +0100 @@ -70,6 +70,19 @@ typedef map TrackMetadataMap; TrackMetadataMap m_metadata; + bool haveTitleArtistMetadata(QString trackId) const { + // Formerly in various places we used to test whether a track + // appeared in the metadata map at all, in order to determine + // whether it had any associated metadata. That won't work any + // more because metadata now includes duration, which can + // appear even if no title/artist are given and which is not + // something whose presence indicates the involvement of a + // "publication Track". So check for artist/title explicitly. + auto mitr = m_metadata.find(trackId); + if (mitr == m_metadata.end()) return false; + return (mitr->second.title != "" || mitr->second.maker != ""); + } + QString m_fixedEventTypeURI; virtual void reviewFileForAppending(QString filename); diff -r e94719f941ba -r 659372323b45 rdf/RDFTransformFactory.cpp --- a/rdf/RDFTransformFactory.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/rdf/RDFTransformFactory.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -129,7 +129,12 @@ } m_store->import(qurl, BasicStore::ImportIgnoreDuplicates); m_isRDF = true; - } catch (...) { } + } catch (const std::exception &e) { + // The file is not RDF -- we report this by returning false + // from isRDF (because we have not reached the m_isRDF = true + // line above), but we also set the error string + m_errorString = e.what(); + } } RDFTransformFactoryImpl::~RDFTransformFactoryImpl() @@ -146,7 +151,7 @@ bool RDFTransformFactoryImpl::isOK() { - return (m_errorString == ""); + return m_isRDF && (m_errorString == ""); } QString @@ -160,6 +165,8 @@ { std::vector transforms; + if (!m_isRDF) return transforms; + std::map uriTransformMap; Nodes tnodes = m_store->match diff -r e94719f941ba -r 659372323b45 rdf/RDFTransformFactory.h --- a/rdf/RDFTransformFactory.h Tue Oct 20 12:54:06 2015 +0100 +++ b/rdf/RDFTransformFactory.h Fri Aug 19 15:58:57 2016 +0100 @@ -36,8 +36,28 @@ RDFTransformFactory(QString url); virtual ~RDFTransformFactory(); - bool isRDF(); // true if the file was parseable and had transforms in it - bool isOK(); // true if the transforms could be completely constructed + /** isRDF() may be queried at any point after construction. It + returns true if the file was parseable as RDF. + */ + bool isRDF(); + + /** isOK() may be queried at any point after getTransforms() has + been called. It is true if the file was parseable as RDF and + any transforms in it could be completely constructed. + + Note that even if isOK() returns true, it is still possible + that the file did not define any transforms; in this case, + getTransforms() would have returned an empty list. + + If isOK() is called before getTransforms() has been invoked to + query the file, it will return true iff isRDF() is true. + */ + bool isOK(); + + /** Return any error string resulting from loading or querying the + file. This will be non-empty if isRDF() or isOK() returns + false. + */ QString getErrorString() const; std::vector getTransforms(ProgressReporter *reporter); diff -r e94719f941ba -r 659372323b45 svcore.pro --- a/svcore.pro Tue Oct 20 12:54:06 2015 +0100 +++ b/svcore.pro Fri Aug 19 15:58:57 2016 +0100 @@ -1,6 +1,9 @@ TEMPLATE = lib +INCLUDEPATH += ../vamp-plugin-sdk +DEFINES += HAVE_VAMP HAVE_VAMPHOSTSDK + exists(config.pri) { include(config.pri) } @@ -22,7 +25,7 @@ LIBS += -L../sv-dependency-builds/osx/lib } - DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_VAMP HAVE_VAMPHOSTSDK HAVE_LIBLO HAVE_MAD HAVE_ID3TAG + DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_LIBLO HAVE_MAD HAVE_ID3TAG macx* { DEFINES += HAVE_COREAUDIO @@ -36,7 +39,7 @@ TARGET = svcore DEPENDPATH += . data plugin plugin/api/alsa -INCLUDEPATH += . data plugin plugin/api/alsa ../dataquay +INCLUDEPATH += . data plugin plugin/api/alsa ../dataquay ../checker OBJECTS_DIR = o MOC_DIR = o @@ -53,10 +56,12 @@ base/AudioPlaySource.h \ base/BaseTypes.h \ base/Clipboard.h \ + base/ColumnOp.h \ base/Command.h \ base/Debug.h \ base/Exceptions.h \ base/LogRange.h \ + base/MagnitudeRange.h \ base/Pitch.h \ base/Playable.h \ base/PlayParameterRepository.h \ @@ -70,7 +75,6 @@ base/RealTime.h \ base/RecentFiles.h \ base/Resampler.h \ - base/ResizeableBitset.h \ base/ResourceFinder.h \ base/RingBuffer.h \ base/Scavenger.h \ @@ -78,6 +82,7 @@ base/Serialiser.h \ base/StorageAdviser.h \ base/StringBits.h \ + base/Strings.h \ base/TempDirectory.h \ base/TempWriteFile.h \ base/TextMatcher.h \ @@ -110,6 +115,7 @@ base/Serialiser.cpp \ base/StorageAdviser.cpp \ base/StringBits.cpp \ + base/Strings.cpp \ base/TempDirectory.cpp \ base/TempWriteFile.cpp \ base/TextMatcher.cpp \ @@ -221,7 +227,8 @@ data/osc/OSCMessage.cpp \ data/osc/OSCQueue.cpp -HEADERS += plugin/DSSIPluginFactory.h \ +HEADERS += plugin/PluginScan.h \ + plugin/DSSIPluginFactory.h \ plugin/DSSIPluginInstance.h \ plugin/FeatureExtractionPluginFactory.h \ plugin/LADSPAPluginFactory.h \ @@ -241,7 +248,8 @@ plugin/api/alsa/sound/asequencer.h -SOURCES += plugin/DSSIPluginFactory.cpp \ +SOURCES += plugin/PluginScan.cpp \ + plugin/DSSIPluginFactory.cpp \ plugin/DSSIPluginInstance.cpp \ plugin/FeatureExtractionPluginFactory.cpp \ plugin/LADSPAPluginFactory.cpp \ diff -r e94719f941ba -r 659372323b45 system/System.cpp --- a/system/System.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/system/System.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -325,3 +325,4 @@ double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; } float princargf(float a) { return float(princarg(a)); } + diff -r e94719f941ba -r 659372323b45 transform/CSVFeatureWriter.cpp --- a/transform/CSVFeatureWriter.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/CSVFeatureWriter.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -37,7 +37,8 @@ m_sampleTiming(false), m_endTimes(false), m_forceEnd(false), - m_omitFilename(false) + m_omitFilename(false), + m_digits(6) { } @@ -82,6 +83,11 @@ p.hasArg = false; pl.push_back(p); + p.name = "digits"; + p.description = "Specify the number of significant digits to use when printing transform outputs. Outputs are represented internally using single-precision floating-point, so digits beyond the 8th or 9th place are usually meaningless. The default is 6."; + p.hasArg = true; + pl.push_back(p); + return pl; } @@ -93,10 +99,10 @@ SVDEBUG << "CSVFeatureWriter::setParameters" << endl; for (map::iterator i = params.begin(); i != params.end(); ++i) { - cerr << i->first << " -> " << i->second << endl; + SVDEBUG << i->first << " -> " << i->second << endl; if (i->first == "separator") { m_separator = i->second.c_str(); - cerr << "m_separator = " << m_separator << endl; + SVDEBUG << "m_separator = " << m_separator << endl; if (m_separator == "\\t") { m_separator = QChar::Tabulation; } @@ -108,6 +114,14 @@ m_forceEnd = true; } else if (i->first == "omit-filename") { m_omitFilename = true; + } else if (i->first == "digits") { + int digits = atoi(i->second.c_str()); + if (digits <= 0 || digits > 100) { + cerr << "CSVFeatureWriter: ERROR: Invalid or out-of-range value for number of significant digits: " << i->second << endl; + cerr << "CSVFeatureWriter: NOTE: Continuing with default settings" << endl; + } else { + m_digits = digits; + } } } } @@ -262,7 +276,7 @@ } for (unsigned int j = 0; j < f.values.size(); ++j) { - stream << m_separator << f.values[j]; + stream << m_separator << QString("%1").arg(f.values[j], 0, 'g', m_digits); } if (f.label != "") { diff -r e94719f941ba -r 659372323b45 transform/CSVFeatureWriter.h --- a/transform/CSVFeatureWriter.h Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/CSVFeatureWriter.h Fri Aug 19 15:58:57 2016 +0100 @@ -74,6 +74,8 @@ const Vamp::Plugin::Feature &f, const Vamp::Plugin::Feature *optionalNextFeature, std::string summaryType); + + int m_digits; }; #endif diff -r e94719f941ba -r 659372323b45 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/FeatureExtractionModelTransformer.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -1007,8 +1007,7 @@ } else if (isOutput(n)) { - DenseThreeDimensionalModel::Column values = - DenseThreeDimensionalModel::Column::fromStdVector(feature.values); + DenseThreeDimensionalModel::Column values = feature.values; EditableDenseThreeDimensionalModel *model = getConformingOutput(n); diff -r e94719f941ba -r 659372323b45 transform/FeatureWriter.h --- a/transform/FeatureWriter.h Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/FeatureWriter.h Fri Aug 19 15:58:57 2016 +0100 @@ -60,6 +60,7 @@ struct TrackMetadata { QString title; QString maker; + RealTime duration; }; virtual void setTrackMetadata(QString /* trackid */, TrackMetadata) { } diff -r e94719f941ba -r 659372323b45 transform/FileFeatureWriter.cpp --- a/transform/FileFeatureWriter.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/FileFeatureWriter.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -46,7 +46,7 @@ } else if (m_support & SupportOneFileTotal) { m_singleFileName = QString("output.%1").arg(m_extension); } else { - SVDEBUG << "FileFeatureWriter::FileFeatureWriter: ERROR: Invalid support specification " << support << endl; + cerr << "FileFeatureWriter::FileFeatureWriter: ERROR: Invalid support specification " << support << endl; } } } @@ -130,7 +130,7 @@ if (m_support & SupportOneFilePerTrackTransform && m_support & SupportOneFilePerTrack) { if (m_singleFileName != "") { - SVDEBUG << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl; + cerr << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl; } else { m_manyFiles = true; } @@ -144,7 +144,7 @@ // OneFilePerTrack), so we need to be able to // override it // if (m_manyFiles) { -// SVDEBUG << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl; +// cerr << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl; // } else { m_singleFileName = i->second.c_str(); // } @@ -153,7 +153,7 @@ } else if (i->first == "stdout") { if (m_support & SupportStdOut) { if (m_singleFileName != "") { - SVDEBUG << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl; + cerr << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl; } else { m_stdout = true; } @@ -173,7 +173,7 @@ if (m_singleFileName != "") { if (QFileInfo(m_singleFileName).exists() && !(m_force || m_append)) { cerr << endl << "FileFeatureWriter: ERROR: Specified output file \"" << m_singleFileName << "\" exists and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append flag is specified -- not overwriting" << endl; - SVDEBUG << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; + cerr << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; return ""; } return m_singleFileName; @@ -220,7 +220,7 @@ if (QFileInfo(filename).exists() && !(m_force || m_append)) { cerr << endl << "FileFeatureWriter: ERROR: Output file \"" << filename << "\" exists (for input file or URL \"" << trackId << "\" and transform \"" << transformId << "\") and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append is specified -- not overwriting" << endl; - SVDEBUG << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; + cerr << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; return ""; } diff -r e94719f941ba -r 659372323b45 transform/Transform.cpp --- a/transform/Transform.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/Transform.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -18,6 +18,7 @@ #include "plugin/PluginIdentifier.h" #include "plugin/FeatureExtractionPluginFactory.h" +#include "plugin/RealTimePluginFactory.h" #include @@ -53,12 +54,8 @@ int errorColumn; if (!doc.setContent(xml, false, &error, &errorLine, &errorColumn)) { - cerr << "Transform::Transform: Error in parsing XML: " - << error << " at line " << errorLine - << ", column " << errorColumn << endl; - cerr << "Input follows:" << endl; - cerr << xml << endl; - cerr << "Input ends." << endl; + m_errorString = QString("%1 at line %2, column %3") + .arg(error).arg(errorLine).arg(errorColumn); return; } @@ -207,10 +204,10 @@ { if (FeatureExtractionPluginFactory::instanceFor(getPluginIdentifier())) { return FeatureExtraction; + } else if (RealTimePluginFactory::instanceFor(getPluginIdentifier())) { + return RealTimeEffect; } else { - // We don't have an unknown/invalid return value, so always - // return this - return RealTimeEffect; + return UnknownType; } } diff -r e94719f941ba -r 659372323b45 transform/Transform.h --- a/transform/Transform.h Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/Transform.h Fri Aug 19 15:58:57 2016 +0100 @@ -46,7 +46,8 @@ /** * Construct a Transform by parsing the given XML data string. - * This is the inverse of toXml. + * This is the inverse of toXml. If this fails, getErrorString() + * will return a non-empty string. */ Transform(QString xml); @@ -67,7 +68,7 @@ void setIdentifier(TransformId id); TransformId getIdentifier() const; - enum Type { FeatureExtraction, RealTimeEffect }; + enum Type { FeatureExtraction, RealTimeEffect, UnknownType }; Type getType() const; QString getPluginIdentifier() const; @@ -156,6 +157,8 @@ */ void setFromXmlAttributes(const QXmlAttributes &); + QString getErrorString() const { return m_errorString; } + static SummaryType stringToSummaryType(QString); static QString summaryTypeToString(SummaryType); @@ -195,6 +198,7 @@ RealTime m_startTime; RealTime m_duration; sv_samplerate_t m_sampleRate; + QString m_errorString; }; typedef std::vector Transforms; diff -r e94719f941ba -r 659372323b45 transform/TransformFactory.cpp --- a/transform/TransformFactory.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/TransformFactory.cpp Fri Aug 19 15:58:57 2016 +0100 @@ -794,6 +794,9 @@ if (t.getType() == Transform::FeatureExtraction) { +// cerr << "TransformFactory::instantiateDefaultPluginFor: identifier \"" +// << identifier << "\" is a feature extraction transform" << endl; + FeatureExtractionPluginFactory *factory = FeatureExtractionPluginFactory::instanceFor(pluginId); @@ -801,7 +804,10 @@ plugin = factory->instantiatePlugin(pluginId, rate); } - } else { + } else if (t.getType() == Transform::RealTimeEffect) { + +// cerr << "TransformFactory::instantiateDefaultPluginFor: identifier \"" +// << identifier << "\" is a real-time transform" << endl; RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(pluginId); @@ -809,6 +815,10 @@ if (factory) { plugin = factory->instantiatePlugin(pluginId, 0, 0, rate, 1024, 1); } + + } else { + cerr << "TransformFactory: ERROR: transform id \"" + << identifier << "\" is of unknown type" << endl; } return plugin; diff -r e94719f941ba -r 659372323b45 transform/TransformFactory.h --- a/transform/TransformFactory.h Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/TransformFactory.h Fri Aug 19 15:58:57 2016 +0100 @@ -195,7 +195,7 @@ */ void setParametersFromPluginConfigurationXml(Transform &transform, QString xml); - + protected: typedef std::map TransformDescriptionMap;