Mercurial > hg > svapp
comparison framework/Align.cpp @ 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 | |
| children | 33fae747db7e |
comparison
equal
deleted
inserted
replaced
| 419:8039c7352ae2 | 420:662aef012679 |
|---|---|
| 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
| 2 | |
| 3 /* | |
| 4 Sonic Visualiser | |
| 5 An audio file viewer and annotation editor. | |
| 6 Centre for Digital Music, Queen Mary, University of London. | |
| 7 This file copyright 2006 Chris Cannam and QMUL. | |
| 8 | |
| 9 This program is free software; you can redistribute it and/or | |
| 10 modify it under the terms of the GNU General Public License as | |
| 11 published by the Free Software Foundation; either version 2 of the | |
| 12 License, or (at your option) any later version. See the file | |
| 13 COPYING included with this distribution for more information. | |
| 14 */ | |
| 15 | |
| 16 #include "Align.h" | |
| 17 | |
| 18 #include "data/model/WaveFileModel.h" | |
| 19 #include "data/model/AggregateWaveModel.h" | |
| 20 #include "data/model/RangeSummarisableTimeValueModel.h" | |
| 21 #include "data/model/SparseTimeValueModel.h" | |
| 22 #include "data/model/AlignmentModel.h" | |
| 23 | |
| 24 #include "data/fileio/CSVFileReader.h" | |
| 25 | |
| 26 #include "transform/TransformFactory.h" | |
| 27 #include "transform/ModelTransformerFactory.h" | |
| 28 #include "transform/FeatureExtractionModelTransformer.h" | |
| 29 | |
| 30 #include <QProcess> | |
| 31 | |
| 32 bool | |
| 33 Align::alignModelViaTransform(Model *ref, Model *other) | |
| 34 { | |
| 35 RangeSummarisableTimeValueModel *reference = qobject_cast | |
| 36 <RangeSummarisableTimeValueModel *>(ref); | |
| 37 | |
| 38 RangeSummarisableTimeValueModel *rm = qobject_cast | |
| 39 <RangeSummarisableTimeValueModel *>(other); | |
| 40 | |
| 41 if (!reference || !rm) return false; // but this should have been tested already | |
| 42 | |
| 43 // This involves creating three new models: | |
| 44 | |
| 45 // 1. an AggregateWaveModel to provide the mixdowns of the main | |
| 46 // model and the new model in its two channels, as input to the | |
| 47 // MATCH plugin | |
| 48 | |
| 49 // 2. a SparseTimeValueModel, which is the model automatically | |
| 50 // created by FeatureExtractionPluginTransformer when running the | |
| 51 // MATCH plugin (thus containing the alignment path) | |
| 52 | |
| 53 // 3. an AlignmentModel, which stores the path model and carries | |
| 54 // out alignment lookups on it. | |
| 55 | |
| 56 // The first two of these are provided as arguments to the | |
| 57 // constructor for the third, which takes responsibility for | |
| 58 // deleting them. The AlignmentModel, meanwhile, is passed to the | |
| 59 // new model we are aligning, which also takes responsibility for | |
| 60 // it. We should not have to delete any of these new models here. | |
| 61 | |
| 62 AggregateWaveModel::ChannelSpecList components; | |
| 63 | |
| 64 components.push_back(AggregateWaveModel::ModelChannelSpec | |
| 65 (reference, -1)); | |
| 66 | |
| 67 components.push_back(AggregateWaveModel::ModelChannelSpec | |
| 68 (rm, -1)); | |
| 69 | |
| 70 Model *aggregateModel = new AggregateWaveModel(components); | |
| 71 ModelTransformer::Input aggregate(aggregateModel); | |
| 72 | |
| 73 TransformId id = "vamp:match-vamp-plugin:match:path"; //!!! configure | |
| 74 | |
| 75 TransformFactory *tf = TransformFactory::getInstance(); | |
| 76 | |
| 77 Transform transform = tf->getDefaultTransformFor | |
| 78 (id, aggregateModel->getSampleRate()); | |
| 79 | |
| 80 transform.setStepSize(transform.getBlockSize()/2); | |
| 81 transform.setParameter("serialise", 1); | |
| 82 transform.setParameter("smooth", 0); | |
| 83 | |
| 84 SVDEBUG << "Align::alignModel: Alignment transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; | |
| 85 | |
| 86 ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); | |
| 87 | |
| 88 QString message; | |
| 89 Model *transformOutput = mtf->transform(transform, aggregate, message); | |
| 90 | |
| 91 if (!transformOutput) { | |
| 92 transform.setStepSize(0); | |
| 93 transformOutput = mtf->transform(transform, aggregate, message); | |
| 94 } | |
| 95 | |
| 96 SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *> | |
| 97 (transformOutput); | |
| 98 | |
| 99 if (!path) { | |
| 100 cerr << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; | |
| 101 delete transformOutput; | |
| 102 delete aggregateModel; | |
| 103 m_error = message; | |
| 104 return false; | |
| 105 } | |
| 106 | |
| 107 path->setCompletion(0); | |
| 108 | |
| 109 AlignmentModel *alignmentModel = new AlignmentModel | |
| 110 (reference, other, aggregateModel, path); | |
| 111 | |
| 112 rm->setAlignment(alignmentModel); | |
| 113 | |
| 114 return true; | |
| 115 } | |
| 116 | |
| 117 bool | |
| 118 Align::alignModelViaProgram(Model *ref, Model *other) | |
| 119 { | |
| 120 WaveFileModel *reference = qobject_cast<WaveFileModel *>(ref); | |
| 121 WaveFileModel *rm = qobject_cast<WaveFileModel *>(other); | |
| 122 | |
| 123 if (!rm) return false; // but this should have been tested already | |
| 124 | |
| 125 // Run an external program, passing to it paths to the main | |
| 126 // model's audio file and the new model's audio file. It returns | |
| 127 // the path in CSV form through stdout. | |
| 128 | |
| 129 QString refPath = reference->getLocalFilename(); | |
| 130 QString otherPath = rm->getLocalFilename(); | |
| 131 | |
| 132 if (refPath == "" || otherPath == "") { | |
| 133 m_error = "Failed to find local filepath for wave-file model"; | |
| 134 return false; | |
| 135 } | |
| 136 | |
| 137 QProcess process; | |
| 138 QString program = "/home/cannam/code/tido-audio/aligner/vect-align.sh"; | |
| 139 QStringList args; | |
| 140 args << refPath << otherPath; | |
| 141 process.start(program, args); | |
| 142 | |
| 143 process.waitForFinished(60000); //!!! nb timeout, but we can do better than blocking anyway | |
| 144 | |
| 145 if (process.exitStatus() == 0) { | |
| 146 | |
| 147 CSVFormat format; | |
| 148 format.setModelType(CSVFormat::TwoDimensionalModel); | |
| 149 format.setTimingType(CSVFormat::ExplicitTiming); | |
| 150 format.setTimeUnits(CSVFormat::TimeSeconds); | |
| 151 format.setColumnCount(2); | |
| 152 format.setColumnPurpose(0, CSVFormat::ColumnStartTime); | |
| 153 format.setColumnPurpose(1, CSVFormat::ColumnValue); | |
| 154 format.setAllowQuoting(false); | |
| 155 format.setSeparator(','); | |
| 156 | |
| 157 CSVFileReader reader(&process, format, reference->getSampleRate()); | |
| 158 if (!reader.isOK()) { | |
| 159 m_error = QString("Failed to parse output of program: %1") | |
| 160 .arg(reader.getError()); | |
| 161 return false; | |
| 162 } | |
| 163 | |
| 164 Model *csvOutput = reader.load(); | |
| 165 | |
| 166 SparseTimeValueModel *path = qobject_cast<SparseTimeValueModel *>(csvOutput); | |
| 167 if (!path) { | |
| 168 m_error = QString("Output of program did not produce sparse time-value model"); | |
| 169 return false; | |
| 170 } | |
| 171 | |
| 172 if (path->getPoints().empty()) { | |
| 173 m_error = QString("Output of alignment program contained no mappings"); | |
| 174 return false; | |
| 175 } | |
| 176 | |
| 177 AlignmentModel *alignmentModel = new AlignmentModel | |
| 178 (reference, other, 0, path); | |
| 179 | |
| 180 rm->setAlignment(alignmentModel); | |
| 181 | |
| 182 } else { | |
| 183 m_error = "Aligner process returned non-zero exit status"; | |
| 184 return false; | |
| 185 } | |
| 186 | |
| 187 cerr << "Align: success" << endl; | |
| 188 | |
| 189 return true; | |
| 190 } | |
| 191 |
