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