changeset 420:662aef012679 alignment_view

Start making it possible to get alignment from an external program (not wired up yet though)
author Chris Cannam
date Fri, 14 Nov 2014 17:24:40 +0000
parents 8039c7352ae2
children 33fae747db7e
files framework/Align.cpp framework/Align.h framework/Document.cpp framework/Document.h framework/MainWindowBase.h svapp.pro
diffstat 6 files changed, 241 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/framework/Align.cpp	Fri Nov 14 17:24:40 2014 +0000
@@ -0,0 +1,191 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam and QMUL.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "Align.h"
+
+#include "data/model/WaveFileModel.h"
+#include "data/model/AggregateWaveModel.h"
+#include "data/model/RangeSummarisableTimeValueModel.h"
+#include "data/model/SparseTimeValueModel.h"
+#include "data/model/AlignmentModel.h"
+
+#include "data/fileio/CSVFileReader.h"
+
+#include "transform/TransformFactory.h"
+#include "transform/ModelTransformerFactory.h"
+#include "transform/FeatureExtractionModelTransformer.h"
+
+#include <QProcess>
+
+bool
+Align::alignModelViaTransform(Model *ref, Model *other)
+{
+    RangeSummarisableTimeValueModel *reference = qobject_cast
+        <RangeSummarisableTimeValueModel *>(ref);
+    
+    RangeSummarisableTimeValueModel *rm = qobject_cast
+        <RangeSummarisableTimeValueModel *>(other);
+
+    if (!reference || !rm) return false; // but this should have been tested already
+   
+    // This involves creating three new models:
+
+    // 1. an AggregateWaveModel to provide the mixdowns of the main
+    // model and the new model in its two channels, as input to the
+    // MATCH plugin
+
+    // 2. a SparseTimeValueModel, which is the model automatically
+    // created by FeatureExtractionPluginTransformer when running the
+    // MATCH plugin (thus containing the alignment path)
+
+    // 3. an AlignmentModel, which stores the path model and carries
+    // out alignment lookups on it.
+
+    // The first two of these are provided as arguments to the
+    // constructor for the third, which takes responsibility for
+    // deleting them.  The AlignmentModel, meanwhile, is passed to the
+    // new model we are aligning, which also takes responsibility for
+    // it.  We should not have to delete any of these new models here.
+
+    AggregateWaveModel::ChannelSpecList components;
+
+    components.push_back(AggregateWaveModel::ModelChannelSpec
+                         (reference, -1));
+
+    components.push_back(AggregateWaveModel::ModelChannelSpec
+                         (rm, -1));
+
+    Model *aggregateModel = new AggregateWaveModel(components);
+    ModelTransformer::Input aggregate(aggregateModel);
+
+    TransformId id = "vamp:match-vamp-plugin:match:path"; //!!! configure
+    
+    TransformFactory *tf = TransformFactory::getInstance();
+
+    Transform transform = tf->getDefaultTransformFor
+        (id, aggregateModel->getSampleRate());
+
+    transform.setStepSize(transform.getBlockSize()/2);
+    transform.setParameter("serialise", 1);
+    transform.setParameter("smooth", 0);
+
+    SVDEBUG << "Align::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl;
+
+    ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
+
+    QString message;
+    Model *transformOutput = mtf->transform(transform, aggregate, message);
+
+    if (!transformOutput) {
+        transform.setStepSize(0);
+        transformOutput = mtf->transform(transform, aggregate, message);
+    }
+
+    SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *>
+        (transformOutput);
+
+    if (!path) {
+        cerr << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl;
+        delete transformOutput;
+        delete aggregateModel;
+	m_error = message;
+        return false;
+    }
+
+    path->setCompletion(0);
+
+    AlignmentModel *alignmentModel = new AlignmentModel
+        (reference, other, aggregateModel, path);
+
+    rm->setAlignment(alignmentModel);
+
+    return true;
+}
+
+bool
+Align::alignModelViaProgram(Model *ref, Model *other)
+{
+    WaveFileModel *reference = qobject_cast<WaveFileModel *>(ref);
+    WaveFileModel *rm = qobject_cast<WaveFileModel *>(other);
+
+    if (!rm) return false; // but this should have been tested already
+
+    // Run an external program, passing to it paths to the main
+    // model's audio file and the new model's audio file. It returns
+    // the path in CSV form through stdout.
+
+    QString refPath = reference->getLocalFilename();
+    QString otherPath = rm->getLocalFilename();
+
+    if (refPath == "" || otherPath == "") {
+	m_error = "Failed to find local filepath for wave-file model";
+	return false;
+    }
+
+    QProcess process;
+    QString program = "/home/cannam/code/tido-audio/aligner/vect-align.sh";
+    QStringList args;
+    args << refPath << otherPath;
+    process.start(program, args);
+
+    process.waitForFinished(60000); //!!! nb timeout, but we can do better than blocking anyway
+
+    if (process.exitStatus() == 0) {
+
+	CSVFormat format;
+	format.setModelType(CSVFormat::TwoDimensionalModel);
+	format.setTimingType(CSVFormat::ExplicitTiming);
+	format.setTimeUnits(CSVFormat::TimeSeconds);
+	format.setColumnCount(2);
+	format.setColumnPurpose(0, CSVFormat::ColumnStartTime);
+	format.setColumnPurpose(1, CSVFormat::ColumnValue);
+	format.setAllowQuoting(false);
+	format.setSeparator(',');
+
+	CSVFileReader reader(&process, format, reference->getSampleRate());
+	if (!reader.isOK()) {
+	    m_error = QString("Failed to parse output of program: %1")
+		.arg(reader.getError());
+	    return false;
+	}
+
+	Model *csvOutput = reader.load();
+
+	SparseTimeValueModel *path = qobject_cast<SparseTimeValueModel *>(csvOutput);
+	if (!path) {
+	    m_error = QString("Output of program did not produce sparse time-value model");
+	    return false;
+	}
+
+	if (path->getPoints().empty()) {
+	    m_error = QString("Output of alignment program contained no mappings");
+	    return false;
+	}
+	
+	AlignmentModel *alignmentModel = new AlignmentModel
+	    (reference, other, 0, path);
+
+	rm->setAlignment(alignmentModel);
+
+    } else {
+	m_error = "Aligner process returned non-zero exit status";
+	return false;
+    }
+
+    cerr << "Align: success" << endl;
+    
+    return true;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/framework/Align.h	Fri Nov 14 17:24:40 2014 +0000
@@ -0,0 +1,38 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam and QMUL.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef ALIGN_H
+#define ALIGN_H
+
+#include <QString>
+
+class Model;
+
+class Align
+{
+public:
+    Align() : m_error("") { }
+
+    bool alignModelViaTransform(Model *reference, Model *other);
+    bool alignModelViaProgram(Model *reference, Model *other);
+
+    QString getError() const { return m_error; }
+
+private:
+    QString m_error;
+};
+
+#endif
+
--- a/framework/Document.cpp	Fri Nov 14 10:20:05 2014 +0000
+++ b/framework/Document.cpp	Fri Nov 14 17:24:40 2014 +0000
@@ -15,6 +15,8 @@
 
 #include "Document.h"
 
+#include "Align.h"
+
 #include "data/model/WaveFileModel.h"
 #include "data/model/WritableWaveFileModel.h"
 #include "data/model/DenseThreeDimensionalModel.h"
@@ -1084,76 +1086,11 @@
         return;
     }
 
-    // This involves creating three new models:
-
-    // 1. an AggregateWaveModel to provide the mixdowns of the main
-    // model and the new model in its two channels, as input to the
-    // MATCH plugin
-
-    // 2. a SparseTimeValueModel, which is the model automatically
-    // created by FeatureExtractionPluginTransformer when running the
-    // MATCH plugin (thus containing the alignment path)
-
-    // 3. an AlignmentModel, which stores the path model and carries
-    // out alignment lookups on it.
-
-    // The first two of these are provided as arguments to the
-    // constructor for the third, which takes responsibility for
-    // deleting them.  The AlignmentModel, meanwhile, is passed to the
-    // new model we are aligning, which also takes responsibility for
-    // it.  We should not have to delete any of these new models here.
-
-    AggregateWaveModel::ChannelSpecList components;
-
-    components.push_back(AggregateWaveModel::ModelChannelSpec
-                         (m_mainModel, -1));
-
-    components.push_back(AggregateWaveModel::ModelChannelSpec
-                         (rm, -1));
-
-    Model *aggregateModel = new AggregateWaveModel(components);
-    ModelTransformer::Input aggregate(aggregateModel);
-
-    TransformId id = "vamp:match-vamp-plugin:match:path"; //!!! configure
-    
-    TransformFactory *tf = TransformFactory::getInstance();
-
-    Transform transform = tf->getDefaultTransformFor
-        (id, aggregateModel->getSampleRate());
-
-    transform.setStepSize(transform.getBlockSize()/2);
-    transform.setParameter("serialise", 1);
-    transform.setParameter("smooth", 0);
-
-    SVDEBUG << "Document::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl;
-
-    ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance();
-
-    QString message;
-    Model *transformOutput = mtf->transform(transform, aggregate, message);
-
-    if (!transformOutput) {
-        transform.setStepSize(0);
-        transformOutput = mtf->transform(transform, aggregate, message);
+    Align a;
+    if (!a.alignModelViaTransform(m_mainModel, rm)) {
+        cerr << "Alignment failed: " << a.getError() << endl;
+        emit alignmentFailed(a.getError());
     }
-
-    SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *>
-        (transformOutput);
-
-    if (!path) {
-        cerr << "Document::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl;
-        emit alignmentFailed(id, message);
-        delete transformOutput;
-        delete aggregateModel;
-        return;
-    }
-
-    path->setCompletion(0);
-
-    AlignmentModel *alignmentModel = new AlignmentModel
-        (m_mainModel, model, aggregateModel, path);
-
-    rm->setAlignment(alignmentModel);
 }
 
 void
--- a/framework/Document.h	Fri Nov 14 10:20:05 2014 +0000
+++ b/framework/Document.h	Fri Nov 14 17:24:40 2014 +0000
@@ -301,7 +301,7 @@
                                  QString message);
     void modelRegenerationWarning(QString layerName, QString transformName,
                                   QString message);
-    void alignmentFailed(QString transformName, QString message);
+    void alignmentFailed(QString message);
 
     void activity(QString);
 
--- a/framework/MainWindowBase.h	Fri Nov 14 10:20:05 2014 +0000
+++ b/framework/MainWindowBase.h	Fri Nov 14 17:24:40 2014 +0000
@@ -265,7 +265,7 @@
     virtual void modelGenerationWarning(QString, QString) = 0;
     virtual void modelRegenerationFailed(QString, QString, QString) = 0;
     virtual void modelRegenerationWarning(QString, QString, QString) = 0;
-    virtual void alignmentFailed(QString, QString) = 0;
+    virtual void alignmentFailed(QString) = 0;
 
     virtual void rightButtonMenuRequested(Pane *, QPoint point) = 0;
 
--- a/svapp.pro	Fri Nov 14 10:20:05 2014 +0000
+++ b/svapp.pro	Fri Nov 14 17:24:40 2014 +0000
@@ -62,13 +62,15 @@
            audioio/ContinuousSynth.cpp \
            audioio/PlaySpeedRangeMapper.cpp
 
-HEADERS += framework/Document.h \
+HEADERS += framework/Align.h \
+	   framework/Document.h \
            framework/MainWindowBase.h \
            framework/SVFileReader.h \
            framework/TransformUserConfigurator.h \
            framework/VersionTester.h
 
-SOURCES += framework/Document.cpp \
+SOURCES += framework/Align.cpp \
+	   framework/Document.cpp \
            framework/MainWindowBase.cpp \
            framework/SVFileReader.cpp \
            framework/TransformUserConfigurator.cpp \