# HG changeset patch # User Chris Cannam # Date 1541494737 0 # Node ID 5f9c9d8c3de68ac03a6ab469badb80086954b754 # Parent 2fec0d9bd7ac1b4b6f0890b2dbe6ac090678632c# Parent 175ef02c7864d91a22f8458d1c0ac922d2314682 Merge from default branch diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 base/Debug.cpp --- a/base/Debug.cpp Fri Oct 05 10:25:25 2018 +0100 +++ b/base/Debug.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -82,8 +83,9 @@ << "Failed to open debug log file " << fileName << " for writing"; } else { -// cerr << m_prefix << ": Log file is " << fileName << endl; m_ok = true; + (*this) << "Debug log started at " + << QDateTime::currentDateTime().toString() << endl; } } diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 base/RingBuffer.h --- a/base/RingBuffer.h Fri Oct 05 10:25:25 2018 +0100 +++ b/base/RingBuffer.h Tue Nov 06 08:58:57 2018 +0000 @@ -18,13 +18,15 @@ This file copyright 2000-2006 Chris Cannam. */ -#ifndef _RINGBUFFER_H_ -#define _RINGBUFFER_H_ +#ifndef SV_RINGBUFFER_H +#define SV_RINGBUFFER_H #include #include "system/System.h" +#include + #include // memcpy, memset &c //#define DEBUG_RINGBUFFER 1 @@ -339,7 +341,7 @@ memcpy(destination + here, m_buffer, (n - here) * sizeof(T)); } - MBARRIER(); + BQ_MBARRIER(); m_readers[R] = (m_readers[R] + n) % m_size; #ifdef DEBUG_RINGBUFFER @@ -382,7 +384,7 @@ } } - MBARRIER(); + BQ_MBARRIER(); m_readers[R] = (m_readers[R] + n) % m_size; return n; } @@ -405,7 +407,7 @@ return t; } T value = m_buffer[m_readers[R]]; - MBARRIER(); + BQ_MBARRIER(); if (++m_readers[R] == m_size) m_readers[R] = 0; return value; } @@ -512,7 +514,7 @@ memcpy(m_buffer, source + here, (n - here) * sizeof(T)); } - MBARRIER(); + BQ_MBARRIER(); m_writer = (m_writer + n) % m_size; #ifdef DEBUG_RINGBUFFER @@ -548,7 +550,7 @@ memset(m_buffer, 0, (n - here) * sizeof(T)); } - MBARRIER(); + BQ_MBARRIER(); m_writer = (m_writer + n) % m_size; return n; } diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 base/ZoomConstraint.h --- a/base/ZoomConstraint.h Fri Oct 05 10:25:25 2018 +0100 +++ b/base/ZoomConstraint.h Tue Nov 06 08:58:57 2018 +0000 @@ -54,6 +54,10 @@ RoundingDirection = RoundNearest) const { + // canonicalise + if (requestedZoomLevel.level == 1) { + requestedZoomLevel.zone = ZoomLevel::FramesPerPixel; + } if (getMaxZoomLevel() < requestedZoomLevel) return getMaxZoomLevel(); else return requestedZoomLevel; } diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 data/model/AggregateWaveModel.cpp --- a/data/model/AggregateWaveModel.cpp Fri Oct 05 10:25:25 2018 +0100 +++ b/data/model/AggregateWaveModel.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -21,6 +21,8 @@ using namespace std; +//#define DEBUG_AGGREGATE_WAVE_FILE_MODEL 1 + PowerOfSqrtTwoZoomConstraint AggregateWaveModel::m_zoomConstraint; @@ -80,11 +82,19 @@ for (ChannelSpecList::const_iterator i = m_components.begin(); i != m_components.end(); ++i) { int completionHere = 100; - if (!i->model->isReady(&completionHere)) ready = false; + if (!i->model->isReady(&completionHere)) { + ready = false; + } if (completion && completionHere < *completion) { *completion = completionHere; } } + +#ifdef DEBUG_AGGREGATE_WAVE_FILE_MODEL + SVDEBUG << "AggregateWaveModel(" << objectName() + << ")::isReady: returning " << ready << endl; +#endif + return ready; } diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 data/model/AlignmentModel.cpp --- a/data/model/AlignmentModel.cpp Fri Oct 05 10:25:25 2018 +0100 +++ b/data/model/AlignmentModel.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -101,10 +101,16 @@ { if (!m_pathBegun && m_rawPath) { if (completion) *completion = 0; +#ifdef DEBUG_ALIGNMENT_MODEL + SVDEBUG << "AlignmentModel::isReady: path not begun" << endl; +#endif return false; } if (m_pathComplete) { if (completion) *completion = 100; +#ifdef DEBUG_ALIGNMENT_MODEL + SVDEBUG << "AlignmentModel::isReady: path complete" << endl; +#endif return true; } if (!m_rawPath) { @@ -112,6 +118,9 @@ // m_pathComplete true above) or else no alignment has been // set at all yet (this case) if (completion) *completion = 0; +#ifdef DEBUG_ALIGNMENT_MODEL + SVDEBUG << "AlignmentModel::isReady: no raw path" << endl; +#endif return false; } return m_rawPath->isReady(completion); @@ -381,6 +390,7 @@ if (m_path) m_path->aboutToDelete(); delete m_path; m_path = path; + m_pathComplete = true; #ifdef DEBUG_ALIGNMENT_MODEL cerr << "AlignmentModel::setPath: path = " << m_path << endl; #endif diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 data/model/Model.cpp --- a/data/model/Model.cpp Fri Oct 05 10:25:25 2018 +0100 +++ b/data/model/Model.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -161,14 +161,15 @@ int Model::getAlignmentCompletion() const { -// SVDEBUG << "Model::getAlignmentCompletion" << endl; +// SVDEBUG << "Model::getAlignmentCompletion: m_alignment = " +// << m_alignment << endl; if (!m_alignment) { if (m_sourceModel) return m_sourceModel->getAlignmentCompletion(); else return 100; } int completion = 0; (void)m_alignment->isReady(&completion); -// cerr << " -> " << completion << endl; +// SVDEBUG << " -> " << completion << endl; return completion; } diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 data/model/PowerOfSqrtTwoZoomConstraint.cpp --- a/data/model/PowerOfSqrtTwoZoomConstraint.cpp Fri Oct 05 10:25:25 2018 +0100 +++ b/data/model/PowerOfSqrtTwoZoomConstraint.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -103,6 +103,7 @@ if (base == blockSize) { result = base; +// SVCERR << "Equal, accepting" << endl; break; } @@ -110,8 +111,12 @@ if (dir == RoundNearest) { if (base - blockSize < blockSize - prevBase) { dir = RoundUp; +// SVCERR << "Closer to " << base << " than " << prevBase +// << ", rounding up" << endl; } else { dir = RoundDown; +// SVCERR << "Closer to " << prevBase << " than " << base +// << ", rounding down" << endl; } } if (dir == RoundUp) { @@ -134,5 +139,7 @@ result = getMaxZoomLevel().level; } +// SVCERR << "Returning result " << result << endl; + return result; } diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 data/model/ReadOnlyWaveFileModel.cpp --- a/data/model/ReadOnlyWaveFileModel.cpp Fri Oct 05 10:25:25 2018 +0100 +++ b/data/model/ReadOnlyWaveFileModel.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -48,10 +48,14 @@ m_fillThread(0), m_updateTimer(0), m_lastFillExtent(0), + m_prevCompletion(0), m_exiting(false), m_lastDirectReadStart(0), m_lastDirectReadCount(0) { + SVDEBUG << "ReadOnlyWaveFileModel::ReadOnlyWaveFileModel: path " + << m_path << ", target rate " << targetRate << endl; + m_source.waitForData(); if (m_source.isOK()) { @@ -77,7 +81,7 @@ << m_reader->getSampleRate() << endl; } } - + if (m_reader) setObjectName(m_reader->getTitle()); if (objectName() == "") setObjectName(QFileInfo(m_path).fileName()); if (isOK()) fillCache(); @@ -92,8 +96,12 @@ m_fillThread(0), m_updateTimer(0), m_lastFillExtent(0), + m_prevCompletion(0), m_exiting(false) { + SVDEBUG << "ReadOnlyWaveFileModel::ReadOnlyWaveFileModel: path " + << m_path << ", with reader" << endl; + m_reader = reader; if (m_reader) setObjectName(m_reader->getTitle()); if (objectName() == "") setObjectName(QFileInfo(m_path).fileName()); @@ -121,9 +129,14 @@ bool ReadOnlyWaveFileModel::isReady(int *completion) const { - bool ready = (isOK() && (m_fillThread == 0)); - double c = double(m_lastFillExtent) / double(getEndFrame() - getStartFrame()); - static int prevCompletion = 0; + bool ready = true; + if (!isOK()) ready = false; + if (m_fillThread) ready = false; + if (m_reader && m_reader->isUpdating()) ready = false; + + double c = double(m_lastFillExtent) / + double(getEndFrame() - getStartFrame()); + if (completion) { *completion = int(c * 100.0 + 0.01); if (m_reader) { @@ -133,15 +146,20 @@ } if (*completion != 0 && *completion != 100 && - prevCompletion != 0 && - prevCompletion > *completion) { + m_prevCompletion != 0 && + m_prevCompletion > *completion) { // just to avoid completion going backwards - *completion = prevCompletion; + *completion = m_prevCompletion; } - prevCompletion = *completion; + m_prevCompletion = *completion; } + #ifdef DEBUG_WAVE_FILE_MODEL - SVDEBUG << "ReadOnlyWaveFileModel::isReady(): ready = " << ready << ", completion = " << (completion ? *completion : -1) << endl; + if (completion) { + SVDEBUG << "ReadOnlyWaveFileModel(" << objectName() << ")::isReady(): ready = " << ready << ", m_fillThread = " << m_fillThread << ", m_lastFillExtent = " << m_lastFillExtent << ", end frame = " << getEndFrame() << ", start frame = " << getStartFrame() << ", c = " << c << ", completion = " << *completion << endl; + } else { + SVDEBUG << "ReadOnlyWaveFileModel(" << objectName() << ")::isReady(): ready = " << ready << ", m_fillThread = " << m_fillThread << ", m_lastFillExtent = " << m_lastFillExtent << ", end frame = " << getEndFrame() << ", start frame = " << getStartFrame() << ", c = " << c << ", completion not requested" << endl; + } #endif return ready; } @@ -550,7 +568,7 @@ m_fillThread->start(); #ifdef DEBUG_WAVE_FILE_MODEL - SVDEBUG << "ReadOnlyWaveFileModel::fillCache: started fill thread" << endl; + SVDEBUG << "ReadOnlyWaveFileModel(" << objectName() << ")::fillCache: started fill thread" << endl; #endif } @@ -560,7 +578,7 @@ if (m_fillThread) { sv_frame_t fillExtent = m_fillThread->getFillExtent(); #ifdef DEBUG_WAVE_FILE_MODEL - SVDEBUG << "ReadOnlyWaveFileModel::fillTimerTimedOut: extent = " << fillExtent << endl; + SVDEBUG << "ReadOnlyWaveFileModel(" << objectName() << ")::fillTimerTimedOut: extent = " << fillExtent << endl; #endif if (fillExtent > m_lastFillExtent) { emit modelChangedWithin(m_lastFillExtent, fillExtent); @@ -568,7 +586,7 @@ } } else { #ifdef DEBUG_WAVE_FILE_MODEL - SVDEBUG << "ReadOnlyWaveFileModel::fillTimerTimedOut: no thread" << endl; + SVDEBUG << "ReadOnlyWaveFileModel(" << objectName() << ")::fillTimerTimedOut: no thread" << endl; #endif emit modelChanged(); } @@ -582,15 +600,17 @@ m_fillThread = 0; delete m_updateTimer; m_updateTimer = 0; + auto prevFillExtent = m_lastFillExtent; + m_lastFillExtent = getEndFrame(); m_mutex.unlock(); - if (getEndFrame() > m_lastFillExtent) { - emit modelChangedWithin(m_lastFillExtent, getEndFrame()); +#ifdef DEBUG_WAVE_FILE_MODEL + SVDEBUG << "ReadOnlyWaveFileModel(" << objectName() << ")::cacheFilled, about to emit things" << endl; +#endif + if (getEndFrame() > prevFillExtent) { + emit modelChangedWithin(prevFillExtent, getEndFrame()); } emit modelChanged(); emit ready(); -#ifdef DEBUG_WAVE_FILE_MODEL - SVDEBUG << "ReadOnlyWaveFileModel::cacheFilled" << endl; -#endif } void @@ -613,7 +633,7 @@ if (updating) { while (channels == 0 && !m_model.m_exiting) { #ifdef DEBUG_WAVE_FILE_MODEL - cerr << "ReadOnlyWaveFileModel::fill: Waiting for channels..." << endl; + cerr << "ReadOnlyWaveFileModel(" << objectName() << ")::fill: Waiting for channels..." << endl; #endif sleep(1); channels = m_model.getChannelCount(); @@ -642,7 +662,7 @@ m_model.m_mutex.unlock(); #ifdef DEBUG_WAVE_FILE_MODEL - cerr << "ReadOnlyWaveFileModel::fill inner loop: frame = " << frame << ", count = " << m_frameCount << ", blocksize " << readBlockSize << endl; + SVDEBUG << "ReadOnlyWaveFileModel(" << m_model.objectName() << ")::fill inner loop: frame = " << frame << ", count = " << m_frameCount << ", blocksize " << readBlockSize << endl; #endif if (updating && (frame + readBlockSize > m_frameCount)) { @@ -735,7 +755,7 @@ #ifdef DEBUG_WAVE_FILE_MODEL for (int cacheType = 0; cacheType < 2; ++cacheType) { - cerr << "Cache type " << cacheType << " now contains " << m_model.m_cache[cacheType].size() << " ranges" << endl; + SVDEBUG << "ReadOnlyWaveFileModel(" << m_model.objectName() << "): Cache type " << cacheType << " now contains " << m_model.m_cache[cacheType].size() << " ranges" << endl; } #endif } diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 data/model/ReadOnlyWaveFileModel.h --- a/data/model/ReadOnlyWaveFileModel.h Fri Oct 05 10:25:25 2018 +0100 +++ b/data/model/ReadOnlyWaveFileModel.h Tue Nov 06 08:58:57 2018 +0000 @@ -131,6 +131,7 @@ RangeCacheFillThread *m_fillThread; QTimer *m_updateTimer; sv_frame_t m_lastFillExtent; + mutable int m_prevCompletion; bool m_exiting; static PowerOfSqrtTwoZoomConstraint m_zoomConstraint; diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 data/model/RelativelyFineZoomConstraint.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/RelativelyFineZoomConstraint.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -0,0 +1,101 @@ +/* -*- 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 "RelativelyFineZoomConstraint.h" + +#include +#include +#include + +using namespace std; + +ZoomLevel +RelativelyFineZoomConstraint::getNearestZoomLevel(ZoomLevel requested, + RoundingDirection dir) const +{ + static vector levels; + + int maxLevel = getMaxZoomLevel().level; + + if (levels.empty()) { + int level = 1; + while (level <= maxLevel) { +// cerr << level << " "; + levels.push_back(level); + int step = level / 10; + int pwr = 0; + while (step > 0) { + ++pwr; + step /= 2; + } + step = (1 << pwr); + level += step; + } +// cerr << endl; + } + + RoundingDirection effective = dir; + if (requested.zone == ZoomLevel::PixelsPerFrame) { + if (dir == RoundUp) effective = RoundDown; + else if (dir == RoundDown) effective = RoundUp; + } + + // iterator pointing to first level that is >= requested + auto i = lower_bound(levels.begin(), levels.end(), requested.level); + + ZoomLevel newLevel(requested); + + if (i == levels.end()) { + newLevel.level = maxLevel; + + } else if (*i == requested.level) { + newLevel.level = requested.level; + + } else if (effective == RoundUp) { + newLevel.level = *i; + + } else if (effective == RoundDown) { + if (i != levels.begin()) { + --i; + } + newLevel.level = *i; + + } else { // RoundNearest + if (i != levels.begin()) { + auto j = i; + --j; + if (requested.level - *j < *i - requested.level) { + newLevel.level = *j; + } else { + newLevel.level = *i; + } + } + } + + // canonicalise + if (newLevel.level == 1) { + newLevel.zone = ZoomLevel::FramesPerPixel; + } + + using namespace std::rel_ops; + if (newLevel > getMaxZoomLevel()) { + newLevel = getMaxZoomLevel(); + } else if (newLevel < getMinZoomLevel()) { + newLevel = getMinZoomLevel(); + } + + return newLevel; +} + + diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 data/model/RelativelyFineZoomConstraint.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/RelativelyFineZoomConstraint.h Tue Nov 06 08:58:57 2018 +0000 @@ -0,0 +1,29 @@ +/* -*- 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_RELATIVELY_FINE_ZOOM_CONSTRAINT_H +#define SV_RELATIVELY_FINE_ZOOM_CONSTRAINT_H + +#include "base/ZoomConstraint.h" + +class RelativelyFineZoomConstraint : virtual public ZoomConstraint +{ +public: + virtual ZoomLevel getNearestZoomLevel(ZoomLevel requested, + RoundingDirection dir = RoundNearest) + const override; +}; + +#endif + diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 data/model/test/TestZoomConstraints.h --- a/data/model/test/TestZoomConstraints.h Fri Oct 05 10:25:25 2018 +0100 +++ b/data/model/test/TestZoomConstraints.h Tue Nov 06 08:58:57 2018 +0000 @@ -17,6 +17,7 @@ #include "../PowerOfTwoZoomConstraint.h" #include "../PowerOfSqrtTwoZoomConstraint.h" +#include "../RelativelyFineZoomConstraint.h" #include #include @@ -30,24 +31,90 @@ { Q_OBJECT + string roundingName(ZoomConstraint::RoundingDirection dir) { + switch (dir) { + case ZoomConstraint::RoundDown: return "RoundDown"; + case ZoomConstraint::RoundUp: return "RoundUp"; + case ZoomConstraint::RoundNearest: return "RoundNearest"; + } + return ""; + } + + void compare(ZoomLevel zin, + ZoomConstraint::RoundingDirection dir, + ZoomLevel zobt, + ZoomLevel zexp) { + if (zexp.level == 1) { + // A zoom level of "1 pixel per frame" is not considered + // canonical - it should be "1 frame per pixel" + zexp.zone = ZoomLevel::FramesPerPixel; + } + if (zobt == zexp) { + return; + } else { + std::cerr << "For input " << zin << " and rounding direction " + << roundingName(dir) + << ", expected output " << zexp << " but obtained " + << zobt << std::endl; + QCOMPARE(zobt, zexp); + } + } + void checkFpp(const ZoomConstraint &c, ZoomConstraint::RoundingDirection dir, int n, int expected) { - QCOMPARE(c.getNearestZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, n), - dir), - ZoomLevel(ZoomLevel::FramesPerPixel, expected)); + ZoomLevel zin(ZoomLevel::FramesPerPixel, n); + ZoomLevel zexp(ZoomLevel::FramesPerPixel, expected); + ZoomLevel zobt(c.getNearestZoomLevel(zin, dir)); + compare(zin, dir, zobt, zexp); } - + + void checkPpf(const ZoomConstraint &c, + ZoomConstraint::RoundingDirection dir, + int n, + int expected) { + ZoomLevel zin(ZoomLevel::PixelsPerFrame, n); + ZoomLevel zexp(ZoomLevel::PixelsPerFrame, expected); + ZoomLevel zobt(c.getNearestZoomLevel(zin, dir)); + compare(zin, dir, zobt, zexp); + } + + void checkBoth(const ZoomConstraint &c, + ZoomConstraint::RoundingDirection dir, + int n, + int expected) { + checkFpp(c, dir, n, expected); + checkPpf(c, dir, n, expected); + } + + void checkMaxMin(const ZoomConstraint &c, + ZoomConstraint::RoundingDirection dir) { + auto max = c.getMaxZoomLevel(); + compare(max, dir, + c.getNearestZoomLevel(max, dir), max); + compare(max.incremented(), dir, + c.getNearestZoomLevel(max.incremented(), dir), max); + auto min = c.getMinZoomLevel(); + compare(min, dir, + c.getNearestZoomLevel(min, dir), min); + compare(min.decremented(), dir, + c.getNearestZoomLevel(min.decremented(), dir), min); + } + + const static ZoomConstraint::RoundingDirection up = ZoomConstraint::RoundUp; + const static ZoomConstraint::RoundingDirection down = ZoomConstraint::RoundDown; + const static ZoomConstraint::RoundingDirection nearest = ZoomConstraint::RoundNearest; + private slots: void unconstrainedNearest() { ZoomConstraint c; - checkFpp(c, ZoomConstraint::RoundNearest, 1, 1); - checkFpp(c, ZoomConstraint::RoundNearest, 2, 2); - checkFpp(c, ZoomConstraint::RoundNearest, 3, 3); - checkFpp(c, ZoomConstraint::RoundNearest, 4, 4); - checkFpp(c, ZoomConstraint::RoundNearest, 20, 20); - checkFpp(c, ZoomConstraint::RoundNearest, 32, 32); + checkBoth(c, nearest, 1, 1); + checkBoth(c, nearest, 2, 2); + checkBoth(c, nearest, 3, 3); + checkBoth(c, nearest, 4, 4); + checkBoth(c, nearest, 20, 20); + checkBoth(c, nearest, 32, 32); auto max = c.getMaxZoomLevel(); QCOMPARE(c.getNearestZoomLevel(max), max); QCOMPARE(c.getNearestZoomLevel(max.incremented()), max); @@ -55,44 +122,40 @@ void unconstrainedUp() { ZoomConstraint c; - checkFpp(c, ZoomConstraint::RoundUp, 1, 1); - checkFpp(c, ZoomConstraint::RoundUp, 2, 2); - checkFpp(c, ZoomConstraint::RoundUp, 3, 3); - checkFpp(c, ZoomConstraint::RoundUp, 4, 4); - checkFpp(c, ZoomConstraint::RoundUp, 20, 20); - checkFpp(c, ZoomConstraint::RoundUp, 32, 32); + checkBoth(c, up, 1, 1); + checkBoth(c, up, 2, 2); + checkBoth(c, up, 3, 3); + checkBoth(c, up, 4, 4); + checkBoth(c, up, 20, 20); + checkBoth(c, up, 32, 32); auto max = c.getMaxZoomLevel(); - QCOMPARE(c.getNearestZoomLevel(max, - ZoomConstraint::RoundUp), max); - QCOMPARE(c.getNearestZoomLevel(max.incremented(), - ZoomConstraint::RoundUp), max); + QCOMPARE(c.getNearestZoomLevel(max, up), max); + QCOMPARE(c.getNearestZoomLevel(max.incremented(), up), max); } void unconstrainedDown() { ZoomConstraint c; - checkFpp(c, ZoomConstraint::RoundDown, 1, 1); - checkFpp(c, ZoomConstraint::RoundDown, 2, 2); - checkFpp(c, ZoomConstraint::RoundDown, 3, 3); - checkFpp(c, ZoomConstraint::RoundDown, 4, 4); - checkFpp(c, ZoomConstraint::RoundDown, 20, 20); - checkFpp(c, ZoomConstraint::RoundDown, 32, 32); + checkBoth(c, down, 1, 1); + checkBoth(c, down, 2, 2); + checkBoth(c, down, 3, 3); + checkBoth(c, down, 4, 4); + checkBoth(c, down, 20, 20); + checkBoth(c, down, 32, 32); auto max = c.getMaxZoomLevel(); - QCOMPARE(c.getNearestZoomLevel(max, - ZoomConstraint::RoundDown), max); - QCOMPARE(c.getNearestZoomLevel(max.incremented(), - ZoomConstraint::RoundDown), max); + QCOMPARE(c.getNearestZoomLevel(max, down), max); + QCOMPARE(c.getNearestZoomLevel(max.incremented(), down), max); } void powerOfTwoNearest() { PowerOfTwoZoomConstraint c; - checkFpp(c, ZoomConstraint::RoundNearest, 1, 1); - checkFpp(c, ZoomConstraint::RoundNearest, 2, 2); - checkFpp(c, ZoomConstraint::RoundNearest, 3, 2); - checkFpp(c, ZoomConstraint::RoundNearest, 4, 4); - checkFpp(c, ZoomConstraint::RoundNearest, 20, 16); - checkFpp(c, ZoomConstraint::RoundNearest, 23, 16); - checkFpp(c, ZoomConstraint::RoundNearest, 24, 16); - checkFpp(c, ZoomConstraint::RoundNearest, 25, 32); + checkBoth(c, nearest, 1, 1); + checkBoth(c, nearest, 2, 2); + checkBoth(c, nearest, 3, 2); + checkBoth(c, nearest, 4, 4); + checkBoth(c, nearest, 20, 16); + checkBoth(c, nearest, 23, 16); + checkBoth(c, nearest, 24, 16); + checkBoth(c, nearest, 25, 32); auto max = c.getMaxZoomLevel(); QCOMPARE(c.getNearestZoomLevel(max), max); QCOMPARE(c.getNearestZoomLevel(max.incremented()), max); @@ -100,101 +163,157 @@ void powerOfTwoUp() { PowerOfTwoZoomConstraint c; - checkFpp(c, ZoomConstraint::RoundUp, 1, 1); - checkFpp(c, ZoomConstraint::RoundUp, 2, 2); - checkFpp(c, ZoomConstraint::RoundUp, 3, 4); - checkFpp(c, ZoomConstraint::RoundUp, 4, 4); - checkFpp(c, ZoomConstraint::RoundUp, 20, 32); - checkFpp(c, ZoomConstraint::RoundUp, 32, 32); - checkFpp(c, ZoomConstraint::RoundUp, 33, 64); - auto max = c.getMaxZoomLevel(); - QCOMPARE(c.getNearestZoomLevel(max, - ZoomConstraint::RoundUp), max); - QCOMPARE(c.getNearestZoomLevel(max.incremented(), - ZoomConstraint::RoundUp), max); + checkBoth(c, up, 1, 1); + checkBoth(c, up, 2, 2); + checkFpp(c, up, 3, 4); + checkPpf(c, up, 3, 2); + checkBoth(c, up, 4, 4); + checkFpp(c, up, 20, 32); + checkPpf(c, up, 20, 16); + checkBoth(c, up, 32, 32); + checkFpp(c, up, 33, 64); + checkPpf(c, up, 33, 32); + checkMaxMin(c, up); } void powerOfTwoDown() { PowerOfTwoZoomConstraint c; - checkFpp(c, ZoomConstraint::RoundDown, 1, 1); - checkFpp(c, ZoomConstraint::RoundDown, 2, 2); - checkFpp(c, ZoomConstraint::RoundDown, 3, 2); - checkFpp(c, ZoomConstraint::RoundDown, 4, 4); - checkFpp(c, ZoomConstraint::RoundDown, 20, 16); - checkFpp(c, ZoomConstraint::RoundDown, 32, 32); - checkFpp(c, ZoomConstraint::RoundDown, 33, 32); - auto max = c.getMaxZoomLevel(); - QCOMPARE(c.getNearestZoomLevel(max, - ZoomConstraint::RoundDown), max); - QCOMPARE(c.getNearestZoomLevel(max.incremented(), - ZoomConstraint::RoundDown), max); + checkBoth(c, down, 1, 1); + checkBoth(c, down, 2, 2); + checkFpp(c, down, 3, 2); + checkPpf(c, down, 3, 4); + checkBoth(c, down, 4, 4); + checkFpp(c, down, 20, 16); + checkPpf(c, down, 20, 32); + checkBoth(c, down, 32, 32); + checkFpp(c, down, 33, 32); + checkPpf(c, down, 33, 64); + checkMaxMin(c, down); } void powerOfSqrtTwoNearest() { PowerOfSqrtTwoZoomConstraint c; - checkFpp(c, ZoomConstraint::RoundNearest, 1, 1); - checkFpp(c, ZoomConstraint::RoundNearest, 2, 2); - checkFpp(c, ZoomConstraint::RoundNearest, 3, 2); - checkFpp(c, ZoomConstraint::RoundNearest, 4, 4); - checkFpp(c, ZoomConstraint::RoundNearest, 18, 16); - checkFpp(c, ZoomConstraint::RoundNearest, 19, 16); - checkFpp(c, ZoomConstraint::RoundNearest, 20, 22); - checkFpp(c, ZoomConstraint::RoundNearest, 23, 22); - checkFpp(c, ZoomConstraint::RoundNearest, 28, 32); + checkBoth(c, nearest, 1, 1); + checkBoth(c, nearest, 2, 2); + checkBoth(c, nearest, 3, 2); + checkBoth(c, nearest, 4, 4); + checkBoth(c, nearest, 18, 16); + checkBoth(c, nearest, 19, 16); + checkBoth(c, nearest, 20, 22); + checkBoth(c, nearest, 23, 22); + checkBoth(c, nearest, 28, 32); // PowerOfSqrtTwoZoomConstraint makes an effort to ensure // bigger numbers get rounded to a multiple of something // simple (64 or 90 depending on whether they are power-of-two // or power-of-sqrt-two types) - checkFpp(c, ZoomConstraint::RoundNearest, 800, 720); - checkFpp(c, ZoomConstraint::RoundNearest, 1023, 1024); - checkFpp(c, ZoomConstraint::RoundNearest, 1024, 1024); - checkFpp(c, ZoomConstraint::RoundNearest, 1024, 1024); - checkFpp(c, ZoomConstraint::RoundNearest, 1025, 1024); - auto max = c.getMaxZoomLevel(); - QCOMPARE(c.getNearestZoomLevel(max), max); - QCOMPARE(c.getNearestZoomLevel(max.incremented()), max); + checkBoth(c, nearest, 350, 360); + // The most extreme level available in ppf mode + // (getMinZoomLevel()) is currently 512, so these bigger + // numbers will only happen in fpp mode + checkFpp(c, nearest, 800, 720); + checkFpp(c, nearest, 1023, 1024); + checkFpp(c, nearest, 1024, 1024); + checkFpp(c, nearest, 1024, 1024); + checkFpp(c, nearest, 1025, 1024); + checkPpf(c, nearest, 800, 512); + checkPpf(c, nearest, 1025, 512); + checkMaxMin(c, nearest); } void powerOfSqrtTwoUp() { PowerOfSqrtTwoZoomConstraint c; - checkFpp(c, ZoomConstraint::RoundUp, 1, 1); - checkFpp(c, ZoomConstraint::RoundUp, 2, 2); - checkFpp(c, ZoomConstraint::RoundUp, 3, 4); - checkFpp(c, ZoomConstraint::RoundUp, 4, 4); - checkFpp(c, ZoomConstraint::RoundUp, 18, 22); - checkFpp(c, ZoomConstraint::RoundUp, 22, 22); - checkFpp(c, ZoomConstraint::RoundUp, 23, 32); - checkFpp(c, ZoomConstraint::RoundUp, 800, 1024); - checkFpp(c, ZoomConstraint::RoundUp, 1023, 1024); - checkFpp(c, ZoomConstraint::RoundUp, 1024, 1024); - // see comment above - checkFpp(c, ZoomConstraint::RoundUp, 1025, 1440); - auto max = c.getMaxZoomLevel(); - QCOMPARE(c.getNearestZoomLevel(max, - ZoomConstraint::RoundUp), max); - QCOMPARE(c.getNearestZoomLevel(max.incremented(), - ZoomConstraint::RoundUp), max); + checkBoth(c, up, 1, 1); + checkBoth(c, up, 2, 2); + checkFpp(c, up, 3, 4); + checkPpf(c, up, 3, 2); + checkBoth(c, up, 4, 4); + checkFpp(c, up, 18, 22); + checkPpf(c, up, 18, 16); + checkBoth(c, up, 22, 22); + checkFpp(c, up, 23, 32); + checkPpf(c, up, 23, 22); + // see comments above + checkFpp(c, up, 800, 1024); + checkFpp(c, up, 1023, 1024); + checkFpp(c, up, 1024, 1024); + checkFpp(c, up, 1025, 1440); + checkPpf(c, up, 300, 256); + checkPpf(c, up, 800, 512); + checkPpf(c, up, 1600, 512); + checkMaxMin(c, up); } void powerOfSqrtTwoDown() { PowerOfSqrtTwoZoomConstraint c; - checkFpp(c, ZoomConstraint::RoundDown, 1, 1); - checkFpp(c, ZoomConstraint::RoundDown, 2, 2); - checkFpp(c, ZoomConstraint::RoundDown, 3, 2); - checkFpp(c, ZoomConstraint::RoundDown, 4, 4); - checkFpp(c, ZoomConstraint::RoundDown, 18, 16); - checkFpp(c, ZoomConstraint::RoundDown, 22, 22); - checkFpp(c, ZoomConstraint::RoundDown, 23, 22); - // see comment above - checkFpp(c, ZoomConstraint::RoundDown, 800, 720); - checkFpp(c, ZoomConstraint::RoundDown, 1023, 720); - checkFpp(c, ZoomConstraint::RoundDown, 1024, 1024); - checkFpp(c, ZoomConstraint::RoundDown, 1025, 1024); - auto max = c.getMaxZoomLevel(); - QCOMPARE(c.getNearestZoomLevel(max, - ZoomConstraint::RoundDown), max); - QCOMPARE(c.getNearestZoomLevel(max.incremented(), - ZoomConstraint::RoundDown), max); + checkBoth(c, down, 1, 1); + checkBoth(c, down, 2, 2); + checkFpp(c, down, 3, 2); + checkPpf(c, down, 3, 4); + checkBoth(c, down, 4, 4); + checkFpp(c, down, 18, 16); + checkPpf(c, down, 18, 22); + checkBoth(c, down, 22, 22); + checkFpp(c, down, 23, 22); + checkPpf(c, down, 23, 32); + // see comments above + checkFpp(c, down, 800, 720); + checkFpp(c, down, 1023, 720); + checkFpp(c, down, 1024, 1024); + checkFpp(c, down, 1025, 1024); + checkPpf(c, down, 300, 360); + checkPpf(c, down, 800, 512); + checkPpf(c, down, 1600, 512); + checkMaxMin(c, down); + } + + void relativelyFineNearest() { + RelativelyFineZoomConstraint c; + checkBoth(c, nearest, 1, 1); + checkBoth(c, nearest, 2, 2); + checkBoth(c, nearest, 3, 3); + checkBoth(c, nearest, 4, 4); + checkBoth(c, nearest, 20, 20); + checkBoth(c, nearest, 33, 32); + checkBoth(c, nearest, 59, 56); + checkBoth(c, nearest, 69, 72); + checkBoth(c, nearest, 121, 128); + checkMaxMin(c, nearest); + } + + void relativelyFineUp() { + RelativelyFineZoomConstraint c; + checkBoth(c, up, 1, 1); + checkBoth(c, up, 2, 2); + checkBoth(c, up, 3, 3); + checkBoth(c, up, 4, 4); + checkBoth(c, up, 20, 20); + checkFpp(c, up, 33, 36); + checkPpf(c, up, 33, 32); + checkFpp(c, up, 59, 64); + checkPpf(c, up, 59, 56); + checkFpp(c, up, 69, 72); + checkPpf(c, up, 69, 64); + checkFpp(c, up, 121, 128); + checkPpf(c, up, 121, 112); + checkMaxMin(c, up); + } + + void relativelyFineDown() { + RelativelyFineZoomConstraint c; + checkBoth(c, down, 1, 1); + checkBoth(c, down, 2, 2); + checkBoth(c, down, 3, 3); + checkBoth(c, down, 4, 4); + checkBoth(c, down, 20, 20); + checkFpp(c, down, 33, 32); + checkPpf(c, down, 33, 36); + checkFpp(c, down, 59, 56); + checkPpf(c, down, 59, 64); + checkFpp(c, down, 69, 64); + checkPpf(c, down, 69, 72); + checkFpp(c, down, 121, 112); + checkPpf(c, down, 121, 128); + checkMaxMin(c, down); } }; diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 files.pri --- a/files.pri Fri Oct 05 10:25:25 2018 +0100 +++ b/files.pri Tue Nov 06 08:58:57 2018 +0000 @@ -90,6 +90,7 @@ data/model/PowerOfTwoZoomConstraint.h \ data/model/RangeSummarisableTimeValueModel.h \ data/model/RegionModel.h \ + data/model/RelativelyFineZoomConstraint.h \ data/model/SparseModel.h \ data/model/SparseOneDimensionalModel.h \ data/model/SparseTimeValueModel.h \ @@ -212,6 +213,7 @@ data/model/PowerOfSqrtTwoZoomConstraint.cpp \ data/model/PowerOfTwoZoomConstraint.cpp \ data/model/RangeSummarisableTimeValueModel.cpp \ + data/model/RelativelyFineZoomConstraint.cpp \ data/model/WaveformOversampler.cpp \ data/model/WaveFileModel.cpp \ data/model/ReadOnlyWaveFileModel.cpp \ diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 plugin/PiperVampPluginFactory.cpp --- a/plugin/PiperVampPluginFactory.cpp Fri Oct 05 10:25:25 2018 +0100 +++ b/plugin/PiperVampPluginFactory.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -58,18 +58,29 @@ m_logger(new Logger) { QString serverName = "piper-vamp-simple-server"; + float minimumVersion = 2.0; HelperExecPath hep(HelperExecPath::AllInstalled); - m_servers = hep.getHelperExecutables(serverName); - for (auto n: m_servers) { + auto servers = hep.getHelperExecutables(serverName); + + for (auto n: servers) { SVDEBUG << "NOTE: PiperVampPluginFactory: Found server: " << n.executable << endl; + if (serverMeetsMinimumVersion(n, minimumVersion)) { + m_servers.push_back(n); + } else { + SVCERR << "WARNING: PiperVampPluginFactory: Server at " + << n.executable + << " does not meet minimum version requirement (version >= " + << minimumVersion << ")" << endl; + } } if (m_servers.empty()) { SVDEBUG << "NOTE: No Piper Vamp servers found in installation;" - << " found none of the following:" << endl; + << " the following paths are either absent or fail " + << "minimum-version check:" << endl; for (auto d: hep.getHelperCandidatePaths(serverName)) { SVDEBUG << "NOTE: " << d << endl; } @@ -81,6 +92,55 @@ delete m_logger; } +bool +PiperVampPluginFactory::serverMeetsMinimumVersion(const HelperExecPath::HelperExec &server, + float minimumVersion) +{ + QProcess process; + QString executable = server.executable; + process.setReadChannel(QProcess::StandardOutput); + process.setProcessChannelMode(QProcess::ForwardedErrorChannel); + process.start(executable, { "--version" }); + + if (!process.waitForStarted()) { + QProcess::ProcessError err = process.error(); + if (err == QProcess::FailedToStart) { + SVCERR << "WARNING: Unable to start server " << executable + << " for version check" << endl; + } else if (err == QProcess::Crashed) { + SVCERR << "WARNING: Server " << executable + << " crashed on version check" << endl; + } else { + SVCERR << "WARNING: Server " << executable + << " failed on version check with error code " + << err << endl; + } + return false; + } + process.waitForFinished(); + + QByteArray output = process.readAllStandardOutput(); + while (output.endsWith('\n') || output.endsWith('\r')) { + output.chop(1); + } + + QString outputString(output); + bool ok = false; + float version = outputString.toFloat(&ok); + if (!ok) { + SVCERR << "WARNING: Failed to convert server version response \"" + << outputString << "\" into one- or two-part version number" + << endl; + } + + SVDEBUG << "Server " << executable << " reports version number " + << version << endl; + + float eps = 1e-6; + return (version >= minimumVersion || + fabsf(version - minimumVersion) < eps); // arf +} + vector PiperVampPluginFactory::getPluginIdentifiers(QString &errorMessage) { diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 plugin/PiperVampPluginFactory.h --- a/plugin/PiperVampPluginFactory.h Fri Oct 05 10:25:25 2018 +0100 +++ b/plugin/PiperVampPluginFactory.h Tue Nov 06 08:58:57 2018 +0000 @@ -59,6 +59,8 @@ std::map m_pluginData; // identifier -> data std::map m_taxonomy; // identifier -> category string + bool serverMeetsMinimumVersion(const HelperExecPath::HelperExec &server, + float minimumVersion); void populate(QString &errorMessage); void populateFrom(const HelperExecPath::HelperExec &, QString &errorMessage); diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 plugin/PluginScan.cpp --- a/plugin/PluginScan.cpp Fri Oct 05 10:25:25 2018 +0100 +++ b/plugin/PluginScan.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -190,7 +190,7 @@ os << "
    "; for (auto f: failures) { - os << "
  • " + f.library; + os << "
  • " + f.library + ""; SVDEBUG << "PluginScan::formatFailureReport: tag is \"" << tag << "\", failure code is " << int(f.code) << ", message is \"" @@ -225,6 +225,11 @@ ("Library cannot be loaded: %1").arg(userMessage); break; + case PluginCheckCode::FAIL_FORBIDDEN: + userMessage = QObject::tr + ("Permission to load library was refused"); + break; + case PluginCheckCode::FAIL_DESCRIPTOR_MISSING: userMessage = QObject::tr ("Not a valid plugin library (no descriptor found)"); @@ -298,8 +303,7 @@ return report; } - return QObject::tr("Failed to load plugins" - "

    Failed to load one or more plugin libraries:

    ") + return QObject::tr("

    Failed to load one or more plugin libraries:

    ") + report + QObject::tr("

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

    ") diff -r 2fec0d9bd7ac -r 5f9c9d8c3de6 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Fri Oct 05 10:25:25 2018 +0100 +++ b/transform/FeatureExtractionModelTransformer.cpp Tue Nov 06 08:58:57 2018 +0000 @@ -40,6 +40,8 @@ #include +//#define DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN 1 + FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in, const Transform &transform) : ModelTransformer(in, transform), @@ -656,9 +658,11 @@ Transform primaryTransform = m_transforms[0]; while (!input->isReady() && !m_abandoned) { - cerr << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << endl; + SVDEBUG << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << endl; usleep(500000); } + SVDEBUG << "FeatureExtractionModelTransformer::run: Waited, ready = " + << input->isReady() << ", m_abandoned = " << m_abandoned << endl; if (m_abandoned) return; sv_samplerate_t sampleRate = input->getSampleRate(); @@ -678,6 +682,7 @@ bool frequencyDomain = (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain); + std::vector fftModels; if (frequencyDomain) { @@ -754,10 +759,12 @@ contextStart + contextDuration) break; } -// SVDEBUG << "FeatureExtractionModelTransformer::run: blockFrame " -// << blockFrame << ", endFrame " << endFrame << ", blockSize " -// << blockSize << endl; - +#ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN + SVDEBUG << "FeatureExtractionModelTransformer::run: blockFrame " + << blockFrame << ", endFrame " << endFrame << ", blockSize " + << blockSize << endl; +#endif + int completion = int ((((blockFrame - contextStart) / stepSize) * 99) / (contextDuration / stepSize + 1)); @@ -777,7 +784,8 @@ buffers[ch][i*2] = 0.f; buffers[ch][i*2+1] = 0.f; } - } + } + error = fftModels[ch]->getError(); if (error != "") { SVCERR << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error << endl; @@ -793,8 +801,10 @@ if (m_abandoned) break; Vamp::Plugin::FeatureSet features = m_plugin->process - (buffers, RealTime::frame2RealTime(blockFrame, sampleRate).toVampRealTime()); - + (buffers, + RealTime::frame2RealTime(blockFrame, sampleRate) + .toVampRealTime()); + if (m_abandoned) break; for (int j = 0; j < (int)m_outputNos.size(); ++j) { @@ -1122,8 +1132,10 @@ void FeatureExtractionModelTransformer::setCompletion(int n, int completion) { -// SVDEBUG << "FeatureExtractionModelTransformer::setCompletion(" -// << completion << ")" << endl; +#ifdef DEBUG_FEATURE_EXTRACTION_TRANSFORMER_RUN + SVDEBUG << "FeatureExtractionModelTransformer::setCompletion(" + << completion << ")" << endl; +#endif if (isOutput(n)) {