# HG changeset patch # User Chris Cannam # Date 1416498379 0 # Node ID f32a64149602006bb59c39b91951b281e492ba09 # Parent 33fae747db7e05d257d5322d55e7922adf45fcb0 Make alignment using an external program asynchronous diff -r 33fae747db7e -r f32a64149602 framework/Align.cpp --- a/framework/Align.cpp Thu Nov 20 14:08:01 2014 +0000 +++ b/framework/Align.cpp Thu Nov 20 15:46:19 2014 +0000 @@ -151,14 +151,51 @@ return false; } - QProcess process; + m_error = ""; + + AlignmentModel *alignmentModel = new AlignmentModel(reference, other, 0, 0); + rm->setAlignment(alignmentModel); + + QProcess *process = new QProcess; QStringList args; args << refPath << otherPath; - process.start(program, args); + + connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(alignmentProgramFinished(int, QProcess::ExitStatus))); - process.waitForFinished(60000); //!!! nb timeout, but we can do better than blocking anyway + m_processModels[process] = alignmentModel; + process->start(program, args); - if (process.exitStatus() == 0) { + bool success = process->waitForStarted(); + + if (!success) { + cerr << "ERROR: Align::alignModelViaProgram: Program did not start" + << endl; + m_error = "Alignment program could not be started"; + m_processModels.erase(process); + delete alignmentModel; + delete process; + } + + return success; +} + +void +Align::alignmentProgramFinished(int exitCode, QProcess::ExitStatus status) +{ + cerr << "Align::alignmentProgramFinished" << endl; + + QProcess *process = qobject_cast(sender()); + + if (m_processModels.find(process) == m_processModels.end()) { + cerr << "ERROR: Align::alignmentProgramFinished: Process " << process + << " not found in process model map!" << endl; + return; + } + + AlignmentModel *alignmentModel = m_processModels[process]; + + if (exitCode == 0 && status == 0) { CSVFormat format; format.setModelType(CSVFormat::TwoDimensionalModel); @@ -170,38 +207,46 @@ format.setAllowQuoting(false); format.setSeparator(','); - CSVFileReader reader(&process, format, reference->getSampleRate()); + CSVFileReader reader(process, format, alignmentModel->getSampleRate()); if (!reader.isOK()) { + cerr << "ERROR: Align::alignmentProgramFinished: Failed to parse output" + << endl; m_error = QString("Failed to parse output of program: %1") .arg(reader.getError()); - return false; + goto done; } Model *csvOutput = reader.load(); SparseTimeValueModel *path = qobject_cast(csvOutput); if (!path) { + cerr << "ERROR: Align::alignmentProgramFinished: Output did not convert to sparse time-value model" + << endl; m_error = QString("Output of program did not produce sparse time-value model"); - return false; + goto done; } if (path->getPoints().empty()) { + cerr << "ERROR: Align::alignmentProgramFinished: Output contained no mappings" + << endl; m_error = QString("Output of alignment program contained no mappings"); - return false; + goto done; } - - AlignmentModel *alignmentModel = new AlignmentModel - (reference, other, 0, path); - rm->setAlignment(alignmentModel); + cerr << "Align::alignmentProgramFinished: Setting alignment path (" + << path->getPoints().size() << " point(s))" << endl; + + alignmentModel->setPathFrom(path); } else { + cerr << "ERROR: Align::alignmentProgramFinished: Aligner program " + << "failed: exit code " << exitCode << ", status " << status + << endl; m_error = "Aligner process returned non-zero exit status"; - return false; } - cerr << "Align: success" << endl; - - return true; +done: + m_processModels.erase(process); + delete process; } diff -r 33fae747db7e -r f32a64149602 framework/Align.h --- a/framework/Align.h Thu Nov 20 14:08:01 2014 +0000 +++ b/framework/Align.h Thu Nov 20 15:46:19 2014 +0000 @@ -17,23 +17,50 @@ #define ALIGN_H #include +#include +#include +#include class Model; +class AlignmentModel; -class Align +class Align : public QObject { + Q_OBJECT + public: Align() : m_error("") { } + /** + * Align the "other" model to the reference, attaching an + * AlignmentModel to it. Alignment is carried out by the method + * configured in the user preferences (either a plugin transform + * or an external process) and is done asynchronously. + * + * A single Align object may carry out many simultanous alignment + * calls -- you do not need to create a new Align object each + * time, nor to wait for an alignment to be complete before + * starting a new one. + * + * The Align object must survive after this call, for at least as + * long as the alignment takes. There is currently no way in this + * API to discover when an alignment is complete -- the + * expectation is that the Align object will simply share the + * process or document lifespan. + */ bool alignModel(Model *reference, Model *other); // via user preference bool alignModelViaTransform(Model *reference, Model *other); bool alignModelViaProgram(Model *reference, Model *other, QString program); QString getError() const { return m_error; } - + +private slots: + void alignmentProgramFinished(int, QProcess::ExitStatus); + private: QString m_error; + std::map m_processModels; }; #endif diff -r 33fae747db7e -r f32a64149602 framework/Document.cpp --- a/framework/Document.cpp Thu Nov 20 14:08:01 2014 +0000 +++ b/framework/Document.cpp Thu Nov 20 15:46:19 2014 +0000 @@ -38,10 +38,8 @@ #include #include -// For alignment: -#include "data/model/AggregateWaveModel.h" -#include "data/model/SparseTimeValueModel.h" #include "data/model/AlignmentModel.h" +#include "Align.h" using std::vector; @@ -51,7 +49,8 @@ Document::Document() : m_mainModel(0), - m_autoAlignment(false) + m_autoAlignment(false), + m_align(new Align()) { connect(this, SIGNAL(modelAboutToBeDeleted(Model *)), ModelTransformerFactory::getInstance(), @@ -1086,10 +1085,9 @@ return; } - Align a; - if (!a.alignModel(m_mainModel, rm)) { - cerr << "Alignment failed: " << a.getError() << endl; - emit alignmentFailed(a.getError()); + if (!m_align->alignModel(m_mainModel, rm)) { + cerr << "Alignment failed: " << m_align->getError() << endl; + emit alignmentFailed(m_align->getError()); } } diff -r 33fae747db7e -r f32a64149602 framework/Document.h --- a/framework/Document.h Thu Nov 20 14:08:01 2014 +0000 +++ b/framework/Document.h Thu Nov 20 15:46:19 2014 +0000 @@ -32,6 +32,8 @@ class AdditionalModelConverter; +class Align; + /** * A Sonic Visualiser document consists of a set of data models, and * also the visualisation layers used to display them. Changes to the @@ -423,6 +425,7 @@ LayerSet m_layers; bool m_autoAlignment; + Align *m_align; }; #endif diff -r 33fae747db7e -r f32a64149602 framework/MainWindowBase.cpp --- a/framework/MainWindowBase.cpp Thu Nov 20 14:08:01 2014 +0000 +++ b/framework/MainWindowBase.cpp Thu Nov 20 15:46:19 2014 +0000 @@ -2210,8 +2210,8 @@ this, SLOT(modelGenerationFailed(QString, QString))); connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)), this, SLOT(modelRegenerationWarning(QString, QString, QString))); - connect(m_document, SIGNAL(alignmentFailed(QString, QString)), - this, SLOT(alignmentFailed(QString, QString))); + connect(m_document, SIGNAL(alignmentFailed(QString)), + this, SLOT(alignmentFailed(QString))); emit replacedDocument(); }