Mercurial > hg > svapp
changeset 761:6429a164b7e1 pitch-align
Schedule alignments with a small delay to avoid too much UI unresponsiveness. Also overhaul error reporting to use signals throughout.
author | Chris Cannam |
---|---|
date | Wed, 06 May 2020 11:45:27 +0100 |
parents | f32df46d0c84 |
children | da57ab54f0e8 dd742e566e60 |
files | align/Align.cpp align/Align.h align/Aligner.h align/ExternalProgramAligner.cpp align/ExternalProgramAligner.h align/TransformAligner.cpp align/TransformAligner.h framework/Document.cpp framework/Document.h framework/MainWindowBase.cpp framework/MainWindowBase.h |
diffstat | 11 files changed, 144 insertions(+), 67 deletions(-) [+] |
line wrap: on
line diff
--- a/align/Align.cpp Mon Apr 27 14:59:56 2020 +0100 +++ b/align/Align.cpp Wed May 06 11:45:27 2020 +0100 @@ -18,12 +18,35 @@ #include "framework/Document.h" #include <QSettings> +#include <QTimer> -bool +void Align::alignModel(Document *doc, ModelId reference, - ModelId toAlign, - QString &error) + ModelId toAlign) +{ + addAligner(doc, reference, toAlign); + m_aligners[toAlign]->begin(); +} + +void +Align::scheduleAlignment(Document *doc, + ModelId reference, + ModelId toAlign) +{ + addAligner(doc, reference, toAlign); + int delay = 500 + 500 * int(m_aligners.size()); + if (delay > 3500) { + delay = 3500; + } + SVCERR << "Align::scheduleAlignment: delaying " << delay << "ms" << endl; + QTimer::singleShot(delay, m_aligners[toAlign].get(), SLOT(begin())); +} + +void +Align::addAligner(Document *doc, + ModelId reference, + ModelId toAlign) { bool useProgram; QString program; @@ -56,8 +79,9 @@ connect(aligner.get(), SIGNAL(complete(ModelId)), this, SLOT(alignerComplete(ModelId))); - - return aligner->begin(error); + + connect(aligner.get(), SIGNAL(failed(ModelId, QString)), + this, SLOT(alignerFailed(ModelId, QString))); } void @@ -87,23 +111,33 @@ void Align::alignerComplete(ModelId alignmentModel) { - Aligner *aligner = qobject_cast<Aligner *>(sender()); + removeAligner(sender()); + emit alignmentComplete(alignmentModel); +} + +void +Align::alignerFailed(ModelId toAlign, QString error) +{ + removeAligner(sender()); + emit alignmentFailed(toAlign, error); +} + +void +Align::removeAligner(QObject *obj) +{ + Aligner *aligner = qobject_cast<Aligner *>(obj); if (!aligner) { - SVCERR << "ERROR: Align::alignerComplete: Caller is not an Aligner" - << endl; + SVCERR << "ERROR: Align::removeAligner: Not an Aligner" << endl; return; } - { - QMutexLocker locker (&m_mutex); + QMutexLocker locker (&m_mutex); - for (auto p: m_aligners) { - if (aligner == p.second.get()) { - m_aligners.erase(p.first); - break; - } + for (auto p: m_aligners) { + if (aligner == p.second.get()) { + m_aligners.erase(p.first); + break; } } +} - emit alignmentComplete(alignmentModel); -}
--- a/align/Align.h Mon Apr 27 14:59:56 2020 +0100 +++ b/align/Align.h Wed May 06 11:45:27 2020 +0100 @@ -39,16 +39,15 @@ * configured in the user preferences (either a plugin transform * or an external process) and is done asynchronously. * - * The return value indicates whether the alignment procedure - * started successfully. If it is true, then an AlignmentModel has + * Any errors are reported by firing the alignmentFailed + * signal. Note that the signal may be fired during the call to + * this function, if the aligner fails to start at all. + * + * If alignment starts successfully, then an AlignmentModel has * been constructed and attached to the toAlign model, and you can * query that model to discover the alignment progress, eventual - * outcome, and any error message generated during alignment. (The - * AlignmentModel is subsequently owned by the toAlign model.) - * Conversely if alignModel returns false, no AlignmentModel has - * been created, and the error return argument will contain an - * error report about whatever problem prevented this from - * happening. + * outcome, and also (separately from the alignmentFailed signal + * here) any error message generated during alignment. * * A single Align object may carry out many simultanous alignment * calls -- you do not need to create a new Align object each @@ -60,12 +59,26 @@ * Align object will simply share the process or document * lifespan. */ - bool alignModel(Document *doc, + void alignModel(Document *doc, ModelId reference, - ModelId toAlign, - QString &error); + ModelId toAlign); /** + * As alignModel, except that the alignment does not begin + * immediately, but is instead placed behind an event callback + * with a small delay. Useful to avoid an unresponsive GUI when + * firing off alignments while doing something else as well. Any + * error is reported by firing the alignmentFailed signal. + * + * Scheduled alignments are not queued or serialised - many could + * happen at once. They are just delayed a little for UI + * responsiveness. + */ + void scheduleAlignment(Document *doc, + ModelId reference, + ModelId toAlign); + + /** * Return true if the alignment facility is available (relevant * plugin installed, etc). */ @@ -79,8 +92,15 @@ */ void alignmentComplete(ModelId alignmentModel); // an AlignmentModel + /** + * Emitted when an alignment fails. The model is the toAlign model + * that was passed to the call to alignModel or scheduleAlignment. + */ + void alignmentFailed(ModelId toAlign, QString errorText); + private slots: void alignerComplete(ModelId alignmentModel); // an AlignmentModel + void alignerFailed(ModelId toAlign, QString errorText); private: QMutex m_mutex; @@ -91,6 +111,9 @@ // we don't key this on the whole (reference, toAlign) pair std::map<ModelId, std::shared_ptr<Aligner>> m_aligners; + void addAligner(Document *doc, ModelId reference, ModelId toAlign); + void removeAligner(QObject *); + static void getAlignerPreference(bool &useProgram, QString &program); };
--- a/align/Aligner.h Mon Apr 27 14:59:56 2020 +0100 +++ b/align/Aligner.h Wed May 06 11:45:27 2020 +0100 @@ -26,7 +26,8 @@ public: virtual ~Aligner() { } - virtual bool begin(QString &error) = 0; +public slots: + virtual void begin() = 0; signals: /** @@ -34,6 +35,11 @@ * and toAlign models can be queried from the alignment model. */ void complete(ModelId alignmentModel); // an AlignmentModel + + /** + * Emitted when alignment fails. + */ + void failed(ModelId toAlign, QString errorText); // the toAlign model }; #endif
--- a/align/ExternalProgramAligner.cpp Mon Apr 27 14:59:56 2020 +0100 +++ b/align/ExternalProgramAligner.cpp Wed May 06 11:45:27 2020 +0100 @@ -49,8 +49,8 @@ return file.exists() && file.isExecutable(); } -bool -ExternalProgramAligner::begin(QString &error) +void +ExternalProgramAligner::begin() { // Run an external program, passing to it paths to the main // model's audio file and the new model's audio file. It returns @@ -60,7 +60,7 @@ auto other = ModelById::getAs<ReadOnlyWaveFileModel>(m_toAlign); if (!reference || !other) { SVCERR << "ERROR: ExternalProgramAligner: Can't align non-read-only models via program (no local filename available)" << endl; - return false; + return; } while (!reference->isReady(nullptr) || !other->isReady(nullptr)) { @@ -78,8 +78,9 @@ } if (refPath == "" || otherPath == "") { - error = "Failed to find local filepath for wave-file model"; - return false; + emit failed(m_toAlign, + tr("Failed to find local filepath for wave-file model")); + return; } auto alignmentModel = @@ -113,7 +114,9 @@ if (!success) { SVCERR << "ERROR: ExternalProgramAligner: Program did not start" << endl; - error = "Alignment program \"" + m_program + "\" did not start"; + emit failed(m_toAlign, + tr("Alignment program \"%1\" did not start") + .arg(m_program)); other->setAlignment({}); ModelById::release(m_alignmentModel); @@ -123,8 +126,6 @@ } else { m_document->addNonDerivedModel(m_alignmentModel); } - - return success; } void @@ -169,9 +170,10 @@ if (!reader.isOK()) { SVCERR << "ERROR: ExternalProgramAligner: Failed to parse output" << endl; - alignmentModel->setError - (QString("Failed to parse output of program: %1") - .arg(reader.getError())); + QString error = tr("Failed to parse output of program: %1") + .arg(reader.getError()); + alignmentModel->setError(error); + emit failed(m_toAlign, error); goto done; } @@ -184,18 +186,22 @@ if (!path) { SVCERR << "ERROR: ExternalProgramAligner: Output did not convert to sparse time-value model" << endl; - alignmentModel->setError - ("Output of program did not produce sparse time-value model"); + QString error = + tr("Output of alignment program was not in the proper format"); + alignmentModel->setError(error); delete csvOutput; + emit failed(m_toAlign, error); goto done; } if (path->isEmpty()) { SVCERR << "ERROR: ExternalProgramAligner: Output contained no mappings" << endl; - alignmentModel->setError - ("Output of alignment program contained no mappings"); + QString error = + tr("Output of alignment program contained no mappings"); + alignmentModel->setError(error); delete path; + emit failed(m_toAlign, error); goto done; } @@ -214,8 +220,9 @@ SVCERR << "ERROR: ExternalProgramAligner: Aligner program " << "failed: exit code " << exitCode << ", status " << status << endl; - alignmentModel->setError - ("Aligner process returned non-zero exit status"); + QString error = tr("Aligner process returned non-zero exit status"); + alignmentModel->setError(error); + emit failed(m_toAlign, error); } done:
--- a/align/ExternalProgramAligner.h Mon Apr 27 14:59:56 2020 +0100 +++ b/align/ExternalProgramAligner.h Wed May 06 11:45:27 2020 +0100 @@ -36,7 +36,7 @@ // Destroy the aligner, cleanly cancelling any ongoing alignment ~ExternalProgramAligner(); - bool begin(QString &error) override; + void begin() override; static bool isAvailable(QString program);
--- a/align/TransformAligner.cpp Mon Apr 27 14:59:56 2020 +0100 +++ b/align/TransformAligner.cpp Wed May 06 11:45:27 2020 +0100 @@ -93,15 +93,15 @@ (tdId == "" || factory->haveTransform(tdId)); } -bool -TransformAligner::begin(QString &error) +void +TransformAligner::begin() { auto reference = ModelById::getAs<RangeSummarisableTimeValueModel>(m_reference); auto other = ModelById::getAs<RangeSummarisableTimeValueModel>(m_toAlign); - if (!reference || !other) return false; + if (!reference || !other) return; // This involves creating a number of new models: // @@ -165,9 +165,10 @@ other->setAlignment(m_alignmentModel); m_document->addNonDerivedModel(m_alignmentModel); } else { - error = alignmentModel->getError(); + QString error = alignmentModel->getError(); ModelById::release(alignmentModel); - return false; + emit failed(m_toAlign, error); + return; } } else { @@ -197,9 +198,9 @@ ModelById::getAs<SparseTimeValueModel>(m_tuningDiffOutputModel); if (!tuningDiffOutputModel) { SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl; - error = message; ModelById::release(alignmentModel); - return false; + emit failed(m_toAlign, message); + return; } other->setAlignment(m_alignmentModel); @@ -218,13 +219,16 @@ progressModel->setCompletion(0); alignmentModel->setPathFrom(m_tuningDiffProgressModel); } - - return true; } void TransformAligner::tuningDifferenceCompletionChanged(ModelId tuningDiffOutputModelId) { + if (m_tuningDiffOutputModel.isNone()) { + // we're done, this is probably a spurious queued event + return; + } + if (tuningDiffOutputModelId != m_tuningDiffOutputModel) { SVCERR << "WARNING: TransformAligner::tuningDifferenceCompletionChanged: Model " << tuningDiffOutputModelId @@ -252,6 +256,10 @@ int completion = 0; bool done = tuningDiffOutputModel->isReady(&completion); + SVDEBUG << "TransformAligner::tuningDifferenceCompletionChanged: model " + << m_tuningDiffOutputModel << ", completion = " << completion + << ", done = " << done << endl; + if (!done) { // This will be the completion the alignment model reports, // before the alignment actually begins. It goes up from 0 to
--- a/align/TransformAligner.h Mon Apr 27 14:59:56 2020 +0100 +++ b/align/TransformAligner.h Wed May 06 11:45:27 2020 +0100 @@ -32,7 +32,7 @@ // Destroy the aligner, cleanly cancelling any ongoing alignment ~TransformAligner(); - bool begin(QString &error) override; + void begin() override; static bool isAvailable();
--- a/framework/Document.cpp Mon Apr 27 14:59:56 2020 +0100 +++ b/framework/Document.cpp Wed May 06 11:45:27 2020 +0100 @@ -57,6 +57,9 @@ connect(m_align, SIGNAL(alignmentComplete(ModelId)), this, SIGNAL(alignmentComplete(ModelId))); + + connect(m_align, SIGNAL(alignmentFailed(ModelId, QString)), + this, SIGNAL(alignmentFailed(ModelId, QString))); } Document::~Document() @@ -555,7 +558,7 @@ } if (m_autoAlignment) { - SVDEBUG << "Document::setMainModel: auto-alignment is on, aligning model if possible" << endl; + SVDEBUG << "Document::setMainModel: auto-alignment is on, aligning main model if applicable" << endl; alignModel(m_mainModel); } else { SVDEBUG << "Document::setMainModel: auto-alignment is off" << endl; @@ -1145,11 +1148,7 @@ << endl; } - QString err; - if (!m_align->alignModel(this, m_mainModel, modelId, err)) { - SVCERR << "Alignment failed: " << err << endl; - emit alignmentFailed(err); - } + m_align->scheduleAlignment(this, m_mainModel, modelId); } void
--- a/framework/Document.h Mon Apr 27 14:59:56 2020 +0100 +++ b/framework/Document.h Wed May 06 11:45:27 2020 +0100 @@ -331,7 +331,7 @@ QString message); void alignmentComplete(ModelId); // an AlignmentModel - void alignmentFailed(QString message); + void alignmentFailed(ModelId, QString message); // an AlignmentModel void activity(QString);
--- a/framework/MainWindowBase.cpp Mon Apr 27 14:59:56 2020 +0100 +++ b/framework/MainWindowBase.cpp Wed May 06 11:45:27 2020 +0100 @@ -2714,8 +2714,8 @@ this, SLOT(modelRegenerationWarning(QString, QString, QString))); connect(m_document, SIGNAL(alignmentComplete(ModelId)), this, SLOT(alignmentComplete(ModelId))); - connect(m_document, SIGNAL(alignmentFailed(QString)), - this, SLOT(alignmentFailed(QString))); + connect(m_document, SIGNAL(alignmentFailed(ModelId, QString)), + this, SLOT(alignmentFailed(ModelId, QString))); m_document->setAutoAlignment(m_viewManager->getAlignMode());
--- a/framework/MainWindowBase.h Mon Apr 27 14:59:56 2020 +0100 +++ b/framework/MainWindowBase.h Wed May 06 11:45:27 2020 +0100 @@ -361,7 +361,7 @@ virtual void modelRegenerationWarning(QString, QString, QString) = 0; virtual void alignmentComplete(ModelId); - virtual void alignmentFailed(QString) = 0; + virtual void alignmentFailed(ModelId, QString) = 0; virtual void paneRightButtonMenuRequested(Pane *, QPoint point) = 0; virtual void panePropertiesRightButtonMenuRequested(Pane *, QPoint point) = 0;