Mercurial > hg > svcore
changeset 1159:444d133b5ab7 3.0-integration
Merge from branch "tony-2.0-integration"
author | Chris Cannam |
---|---|
date | Thu, 04 Feb 2016 11:13:39 +0000 |
parents | 2dc27f0f97ad (diff) e94719f941ba (current diff) |
children | ea636412f9fe |
files | data/model/FFTModel.cpp data/model/FFTModel.h |
diffstat | 19 files changed, 355 insertions(+), 326 deletions(-) [+] |
line wrap: on
line diff
--- 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 <vector> -#include <stdint.h> -#include <stddef.h> -#include <stdlib.h> - -class ResizeableBitset { - -public: - ResizeableBitset() : m_bits(0), m_size(0) { - } - ResizeableBitset(size_t size) : m_bits(new std::vector<uint8_t>), m_size(size) { - m_bits->assign((size >> 3) + 1, 0); - } - ResizeableBitset(const ResizeableBitset &b) { - m_bits = new std::vector<uint8_t>(*b.m_bits); - } - ResizeableBitset &operator=(const ResizeableBitset &b) { - if (&b != this) return *this; - delete m_bits; - m_bits = new std::vector<uint8_t>(*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<uint8_t> *newbits = new std::vector<uint8_t>(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<uint8_t> *m_bits; - size_t m_size; -}; - - -#endif -
--- a/data/fileio/MatrixFile.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/MatrixFile.cpp Thu Feb 04 11:13:39 2016 +0000 @@ -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
--- a/data/fileio/MatrixFile.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/fileio/MatrixFile.h Thu Feb 04 11:13:39 2016 +0000 @@ -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<bool> m_setColumns; // only populated in writer bool m_autoClose; // In reader: if this is >= 0, we can read that column directly
--- a/data/model/Dense3DModelPeakCache.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/Dense3DModelPeakCache.cpp Thu Feb 04 11:13:39 2016 +0000 @@ -22,8 +22,6 @@ m_source(source), m_resolution(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,29 @@ { Profiler profiler("Dense3DModelPeakCache::fillColumn"); - if (column >= (int)m_coverage.size()) { + if (!in_range_for(m_coverage, column)) { // see note in sourceModelChanged - if (m_coverage.size() > 0) m_coverage.reset(m_coverage.size()-1); - m_coverage.resize(column + 1); + if (m_coverage.size() > 0) m_coverage[m_coverage.size()-1] = false; + m_coverage.resize(column + 1, false); } Column peak; - for (int i = 0; i < int(m_resolution); ++i) { + int n = 0; + for (int i = 0; i < m_resolution; ++i) { Column here = m_source->getColumn(column * m_resolution + i); 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; }
--- a/data/model/Dense3DModelPeakCache.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/Dense3DModelPeakCache.h Thu Feb 04 11:13:39 2016 +0000 @@ -18,7 +18,6 @@ #include "DenseThreeDimensionalModel.h" #include "EditableDenseThreeDimensionalModel.h" -#include "base/ResizeableBitset.h" class Dense3DModelPeakCache : public DenseThreeDimensionalModel { @@ -65,8 +64,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; @@ -92,7 +89,8 @@ private: DenseThreeDimensionalModel *m_source; mutable EditableDenseThreeDimensionalModel *m_cache; - mutable ResizeableBitset m_coverage; + mutable std::vector<bool> m_coverage; // must be bool, for space efficiency + // (vector of bool uses 1-bit elements) int m_resolution; bool haveColumn(int column) const;
--- a/data/model/DenseThreeDimensionalModel.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/DenseThreeDimensionalModel.h Thu Feb 04 11:13:39 2016 +0000 @@ -55,16 +55,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<float> Column; + typedef std::vector<float> Column; /** * Get data from the given column of bin values.
--- a/data/model/EditableDenseThreeDimensionalModel.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/EditableDenseThreeDimensionalModel.cpp Thu Feb 04 11:13:39 2016 +0000 @@ -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";
--- a/data/model/EditableDenseThreeDimensionalModel.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/EditableDenseThreeDimensionalModel.h Thu Feb 04 11:13:39 2016 +0000 @@ -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<Column> ValueMatrix; + typedef std::vector<Column> ValueMatrix; ValueMatrix m_data; // m_trunc is used for simple compression. If at least the top N
--- a/data/model/FFTModel.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/FFTModel.cpp Thu Feb 04 11:13:39 2016 +0000 @@ -98,9 +98,9 @@ { 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); } float @@ -116,7 +116,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 +139,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(); @@ -313,7 +307,7 @@ } m_cached.push_back(sc); - return col; + return move(col); } bool @@ -388,10 +382,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 +407,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 +429,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<float> sorted(window); @@ -455,7 +450,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;
--- a/data/model/FFTModel.h Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/FFTModel.h Thu Feb 04 11:13:39 2016 +0000 @@ -100,7 +100,6 @@ 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;
--- a/data/model/ReadOnlyWaveFileModel.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/data/model/ReadOnlyWaveFileModel.cpp Thu Feb 04 11:13:39 2016 +0000 @@ -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<float> 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); } }
--- a/plugin/FeatureExtractionPluginFactory.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/plugin/FeatureExtractionPluginFactory.cpp Thu Feb 04 11:13:39 2016 +0000 @@ -30,6 +30,8 @@ #include "base/Profiler.h" +using namespace std; + //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1 class PluginDeletionNotifyAdapter : public Vamp::HostExt::PluginWrapper { @@ -77,25 +79,25 @@ return instance(type); } -std::vector<QString> +vector<QString> FeatureExtractionPluginFactory::getPluginPath() { if (!m_pluginPath.empty()) return m_pluginPath; - std::vector<std::string> p = Vamp::PluginHostAdapter::getPluginPath(); + vector<string> 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<QString> +vector<QString> FeatureExtractionPluginFactory::getAllPluginIdentifiers() { FeatureExtractionPluginFactory *factory; - std::vector<QString> rv; + vector<QString> rv; factory = instance("vamp"); if (factory) { - std::vector<QString> tmp = factory->getPluginIdentifiers(); + vector<QString> tmp = factory->getPluginIdentifiers(); for (size_t i = 0; i < tmp.size(); ++i) { // cerr << "identifier: " << tmp[i] << endl; rv.push_back(tmp[i]); @@ -108,103 +110,165 @@ return rv; } -std::vector<QString> +vector<QString> +FeatureExtractionPluginFactory::getPluginCandidateFiles() +{ + vector<QString> path = getPluginPath(); + vector<QString> candidates; + + for (QString dirname : path) { + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << dirname << endl; +#endif + + QDir pluginDir(dirname, PLUGIN_GLOB, + QDir::Name | QDir::IgnoreCase, + QDir::Files | QDir::Readable); + + for (unsigned int j = 0; j < pluginDir.count(); ++j) { + QString soname = pluginDir.filePath(pluginDir[j]); + candidates.push_back(soname); + } + } + + return candidates; +} + +vector<QString> +FeatureExtractionPluginFactory::winnowPluginCandidates(vector<QString> candidates, + QString &warningMessage) +{ + vector<QString> good, bad; + vector<PluginLoadStatus> badStatuses; + + for (QString c: candidates) { + + PluginLoadStatus status = + TestPluginLoadability(c, "vampGetPluginDescriptor"); + + if (status == PluginLoadOK) { + good.push_back(c); + } else if (status == UnknownPluginLoadStatus) { + cerr << "WARNING: Unknown load status for plugin candidate \"" + << c << "\", continuing" << endl; + good.push_back(c); + } else { + bad.push_back(c); + badStatuses.push_back(status); + } + } + + if (!bad.empty()) { + warningMessage = + QObject::tr("<b>Failed to load plugins</b>" + "<p>Failed to load one or more plugin libraries:</p>\n"); + warningMessage += "<ul>"; + for (int i = 0; in_range_for(bad, i); ++i) { + QString m; + if (badStatuses[i] == PluginLoadFailedToLoadLibrary) { + m = QObject::tr("Failed to load library"); + } else if (badStatuses[i] == PluginLoadFailedToFindDescriptor) { + m = QObject::tr("Failed to query plugins from library after loading"); + } else if (badStatuses[i] == PluginLoadFailedElsewhere) { + m = QObject::tr("Unknown failure"); + } else { + m = QObject::tr("Success: internal error?"); + } + warningMessage += QString("<li>%1 (%2)</li>\n") + .arg(bad[i]) + .arg(m); + } + warningMessage += "</ul>"; + } + return good; +} + +vector<QString> FeatureExtractionPluginFactory::getPluginIdentifiers() { Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers"); - std::vector<QString> rv; - std::vector<QString> path = getPluginPath(); + vector<QString> rv; + vector<QString> candidates = winnowPluginCandidates(getPluginCandidateFiles(), + m_pluginScanError); - for (std::vector<QString>::iterator i = path.begin(); i != path.end(); ++i) { + for (QString soname : candidates) { #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i-<< endl; + SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname << endl; #endif - QDir pluginDir(*i, PLUGIN_GLOB, - QDir::Name | QDir::IgnoreCase, - QDir::Files | QDir::Readable); - - for (unsigned int j = 0; j < pluginDir.count(); ++j) { - - QString soname = pluginDir.filePath(pluginDir[j]); + void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); + + if (!libraryHandle) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl; + continue; + } #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname << endl; + SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << endl; #endif - 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: Vamp descriptor found" << endl; +#endif + + const VampPluginDescriptor *descriptor = 0; + int index = 0; + + map<string, int> known; + bool ok = true; + + 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; + 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<std::string, int> 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 + SVDEBUG << "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(); @@ -279,8 +343,8 @@ if (file != "") return file; } - std::vector<QString> path = getPluginPath(); - for (std::vector<QString>::iterator i = path.begin(); + vector<QString> path = getPluginPath(); + for (vector<QString>::iterator i = path.begin(); i != path.end(); ++i) { if (*i != "") { file = findPluginFile(soname, *i); @@ -312,7 +376,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 +390,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 +409,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 +441,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 +453,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 +470,8 @@ void FeatureExtractionPluginFactory::generateTaxonomy() { - std::vector<QString> pluginPath = getPluginPath(); - std::vector<QString> path; + vector<QString> pluginPath = getPluginPath(); + vector<QString> path; for (size_t i = 0; i < pluginPath.size(); ++i) { if (pluginPath[i].contains("/lib/")) {
--- a/plugin/FeatureExtractionPluginFactory.h Tue Oct 20 12:54:06 2015 +0100 +++ b/plugin/FeatureExtractionPluginFactory.h Thu Feb 04 11:13:39 2016 +0000 @@ -38,6 +38,14 @@ virtual std::vector<QString> getPluginIdentifiers(); + /** + * Return any error message arising from the initial plugin + * scan. The return value will either be an empty string (nothing + * to report) or an HTML string suitable for dropping into a + * dialog and showing the user. + */ + virtual QString getPluginPopulationWarning() { return m_pluginScanError; } + virtual QString findPluginFile(QString soname, QString inDir = ""); // We don't set blockSize or channels on this -- they're @@ -57,8 +65,14 @@ friend class PluginDeletionNotifyAdapter; void pluginDeleted(Vamp::Plugin *); std::map<Vamp::Plugin *, void *> m_handleMap; + + std::vector<QString> getPluginCandidateFiles(); + std::vector<QString> winnowPluginCandidates(std::vector<QString> candidates, + QString &warningMessage); + + void generateTaxonomy(); - void generateTaxonomy(); + QString m_pluginScanError; }; #endif
--- a/svcore.pro Tue Oct 20 12:54:06 2015 +0100 +++ b/svcore.pro Thu Feb 04 11:13:39 2016 +0000 @@ -70,7 +70,6 @@ base/RealTime.h \ base/RecentFiles.h \ base/Resampler.h \ - base/ResizeableBitset.h \ base/ResourceFinder.h \ base/RingBuffer.h \ base/Scavenger.h \
--- a/system/System.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/system/System.cpp Thu Feb 04 11:13:39 2016 +0000 @@ -325,3 +325,66 @@ double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; } float princargf(float a) { return float(princarg(a)); } +#ifndef _WIN32 + +#include <unistd.h> +#include <sys/wait.h> + +PluginLoadStatus +TestPluginLoadability(QString soname, QString descriptorFn) +{ + //!!! This is POSIX only, no equivalent on Windows, where we'll + //!!! have to do something completely different + + pid_t pid = fork(); + + if (pid < 0) { + return UnknownPluginLoadStatus; // fork failed + } + + if (pid == 0) { // the child process + + void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL); + if (!handle) { + cerr << "isPluginLibraryLoadable: Failed to open plugin library \"" + << soname << "\": " << dlerror() << "\n"; + cerr << "exiting with status 1" << endl; + exit(1); + } + + void *fn = DLSYM(handle, descriptorFn.toLocal8Bit().data()); + if (!fn) { + cerr << "isPluginLibraryLoadable: Failed to find plugin descriptor function \"" << descriptorFn << "\" in library \"" << soname << "\": " << dlerror() << "\n"; + exit(2); + } + +// cerr << "isPluginLibraryLoadable: Successfully loaded library \"" << soname << "\" and retrieved descriptor function" << endl; + + exit(0); + + } else { // the parent process + + int status = 0; + + do { + waitpid(pid, &status, 0); + } while (WIFSTOPPED(status)); + + if (WIFEXITED(status)) { + switch (WEXITSTATUS(status)) { + case 0: return PluginLoadOK; // success + case 1: return PluginLoadFailedToLoadLibrary; + case 2: return PluginLoadFailedToFindDescriptor; + default: return PluginLoadFailedElsewhere; + } + } + + if (WIFSIGNALED(status)) { + return PluginLoadFailedElsewhere; + } + + return UnknownPluginLoadStatus; + } +} + +#endif
--- a/system/System.h Tue Oct 20 12:54:06 2015 +0100 +++ b/system/System.h Thu Feb 04 11:13:39 2016 +0000 @@ -154,6 +154,21 @@ extern void StoreStartupLocale(); extern void RestoreStartupLocale(); +enum PluginLoadStatus { + UnknownPluginLoadStatus, + PluginLoadOK, + PluginLoadFailedToLoadLibrary, + PluginLoadFailedToFindDescriptor, + PluginLoadFailedElsewhere +}; + +// Check whether a plugin library is loadable without crashing (may +// need to spawn an external process to do it). Descriptor fn is the +// name of a LADSPA/DSSI/Vamp-style descriptor function to try +// calling; may be an empty string if the plugin doesn't follow that +// convention. +PluginLoadStatus TestPluginLoadability(QString soname, QString descriptorFn); + #include <cmath> #ifndef M_PI
--- a/transform/FeatureExtractionModelTransformer.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/FeatureExtractionModelTransformer.cpp Thu Feb 04 11:13:39 2016 +0000 @@ -1007,8 +1007,7 @@ } else if (isOutput<EditableDenseThreeDimensionalModel>(n)) { - DenseThreeDimensionalModel::Column values = - DenseThreeDimensionalModel::Column::fromStdVector(feature.values); + DenseThreeDimensionalModel::Column values = feature.values; EditableDenseThreeDimensionalModel *model = getConformingOutput<EditableDenseThreeDimensionalModel>(n);
--- a/transform/TransformFactory.cpp Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/TransformFactory.cpp Thu Feb 04 11:13:39 2016 +0000 @@ -399,6 +399,18 @@ m_transformsPopulated = true; } +QString +TransformFactory::getPluginPopulationWarning() +{ + FeatureExtractionPluginFactory *vfactory = + FeatureExtractionPluginFactory::instance("vamp"); + QString warningMessage; + if (vfactory) { + warningMessage = vfactory->getPluginPopulationWarning(); + } + return warningMessage; +} + void TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms) {
--- a/transform/TransformFactory.h Tue Oct 20 12:54:06 2015 +0100 +++ b/transform/TransformFactory.h Thu Feb 04 11:13:39 2016 +0000 @@ -196,6 +196,14 @@ void setParametersFromPluginConfigurationXml(Transform &transform, QString xml); + /** + * Return any error message arising from the initial plugin + * scan. The return value will either be an empty string (nothing + * to report) or an HTML string suitable for dropping into a + * dialog and showing the user. + */ + QString getPluginPopulationWarning(); + protected: typedef std::map<TransformId, TransformDescription> TransformDescriptionMap;