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 |