Chris@420
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@420
|
2
|
Chris@420
|
3 /*
|
Chris@420
|
4 Sonic Visualiser
|
Chris@420
|
5 An audio file viewer and annotation editor.
|
Chris@420
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@420
|
7
|
Chris@420
|
8 This program is free software; you can redistribute it and/or
|
Chris@420
|
9 modify it under the terms of the GNU General Public License as
|
Chris@420
|
10 published by the Free Software Foundation; either version 2 of the
|
Chris@420
|
11 License, or (at your option) any later version. See the file
|
Chris@420
|
12 COPYING included with this distribution for more information.
|
Chris@420
|
13 */
|
Chris@420
|
14
|
Chris@420
|
15 #include "Align.h"
|
Chris@767
|
16
|
Chris@767
|
17 #include "LinearAligner.h"
|
Chris@753
|
18 #include "TransformAligner.h"
|
Chris@767
|
19 #include "TransformDTWAligner.h"
|
Chris@753
|
20 #include "ExternalProgramAligner.h"
|
Chris@767
|
21
|
Chris@744
|
22 #include "framework/Document.h"
|
Chris@420
|
23
|
Chris@767
|
24 #include "transform/Transform.h"
|
Chris@767
|
25 #include "transform/TransformFactory.h"
|
Chris@767
|
26
|
Chris@422
|
27 #include <QSettings>
|
Chris@761
|
28 #include <QTimer>
|
Chris@422
|
29
|
Chris@767
|
30 using std::make_shared;
|
Chris@767
|
31
|
Chris@767
|
32 QString
|
Chris@767
|
33 Align::getAlignmentTypeTag(AlignmentType type)
|
Chris@767
|
34 {
|
Chris@767
|
35 switch (type) {
|
Chris@767
|
36 case NoAlignment:
|
Chris@767
|
37 default:
|
Chris@767
|
38 return "no-alignment";
|
Chris@767
|
39 case LinearAlignment:
|
Chris@767
|
40 return "linear-alignment";
|
Chris@767
|
41 case TrimmedLinearAlignment:
|
Chris@767
|
42 return "trimmed-linear-alignment";
|
Chris@767
|
43 case MATCHAlignment:
|
Chris@767
|
44 return "match-alignment";
|
Chris@767
|
45 case MATCHAlignmentWithPitchCompare:
|
Chris@767
|
46 return "match-alignment-with-pitch";
|
Chris@767
|
47 case SungPitchContourAlignment:
|
Chris@767
|
48 return "sung-pitch-alignment";
|
Chris@767
|
49 case TransformDrivenDTWAlignment:
|
Chris@767
|
50 return "transform-driven-alignment";
|
Chris@767
|
51 case ExternalProgramAlignment:
|
Chris@767
|
52 return "external-program-alignment";
|
Chris@767
|
53 }
|
Chris@767
|
54 }
|
Chris@767
|
55
|
Chris@767
|
56 Align::AlignmentType
|
Chris@767
|
57 Align::getAlignmentTypeForTag(QString tag)
|
Chris@767
|
58 {
|
Chris@767
|
59 for (int i = 0; i <= int(LastAlignmentType); ++i) {
|
Chris@767
|
60 if (tag == getAlignmentTypeTag(AlignmentType(i))) {
|
Chris@767
|
61 return AlignmentType(i);
|
Chris@767
|
62 }
|
Chris@767
|
63 }
|
Chris@767
|
64 return NoAlignment;
|
Chris@767
|
65 }
|
Chris@767
|
66
|
Chris@761
|
67 void
|
Chris@753
|
68 Align::alignModel(Document *doc,
|
Chris@753
|
69 ModelId reference,
|
Chris@761
|
70 ModelId toAlign)
|
Chris@761
|
71 {
|
Chris@767
|
72 if (addAligner(doc, reference, toAlign)) {
|
Chris@767
|
73 m_aligners[toAlign]->begin();
|
Chris@767
|
74 }
|
Chris@761
|
75 }
|
Chris@761
|
76
|
Chris@761
|
77 void
|
Chris@761
|
78 Align::scheduleAlignment(Document *doc,
|
Chris@761
|
79 ModelId reference,
|
Chris@761
|
80 ModelId toAlign)
|
Chris@761
|
81 {
|
Chris@767
|
82 int delay = 700 * int(m_aligners.size());
|
Chris@761
|
83 if (delay > 3500) {
|
Chris@761
|
84 delay = 3500;
|
Chris@761
|
85 }
|
Chris@767
|
86 if (!addAligner(doc, reference, toAlign)) {
|
Chris@767
|
87 return;
|
Chris@767
|
88 }
|
Chris@761
|
89 SVCERR << "Align::scheduleAlignment: delaying " << delay << "ms" << endl;
|
Chris@761
|
90 QTimer::singleShot(delay, m_aligners[toAlign].get(), SLOT(begin()));
|
Chris@761
|
91 }
|
Chris@761
|
92
|
Chris@767
|
93 bool
|
Chris@761
|
94 Align::addAligner(Document *doc,
|
Chris@761
|
95 ModelId reference,
|
Chris@761
|
96 ModelId toAlign)
|
Chris@753
|
97 {
|
Chris@767
|
98 QString additionalData;
|
Chris@767
|
99 AlignmentType type = getAlignmentPreference(additionalData);
|
Chris@753
|
100
|
Chris@753
|
101 std::shared_ptr<Aligner> aligner;
|
Chris@753
|
102
|
Chris@753
|
103 {
|
Chris@753
|
104 // Replace the aligner with a new one. This also stops any
|
Chris@753
|
105 // previously-running alignment, when the old entry is
|
Chris@753
|
106 // replaced and its aligner destroyed.
|
Chris@753
|
107
|
Chris@753
|
108 QMutexLocker locker(&m_mutex);
|
Chris@767
|
109
|
Chris@767
|
110 switch (type) {
|
Chris@767
|
111
|
Chris@767
|
112 case NoAlignment:
|
Chris@767
|
113 return false;
|
Chris@767
|
114
|
Chris@767
|
115 case LinearAlignment:
|
Chris@767
|
116 case TrimmedLinearAlignment: {
|
Chris@767
|
117 bool trimmed = (type == TrimmedLinearAlignment);
|
Chris@767
|
118 aligner = make_shared<LinearAligner>(doc,
|
Chris@767
|
119 reference,
|
Chris@767
|
120 toAlign,
|
Chris@767
|
121 trimmed);
|
Chris@767
|
122 break;
|
Chris@753
|
123 }
|
Chris@753
|
124
|
Chris@767
|
125 case MATCHAlignment:
|
Chris@767
|
126 case MATCHAlignmentWithPitchCompare: {
|
Chris@767
|
127
|
Chris@767
|
128 bool withTuningDifference =
|
Chris@767
|
129 (type == MATCHAlignmentWithPitchCompare);
|
Chris@767
|
130
|
Chris@767
|
131 aligner = make_shared<TransformAligner>(doc,
|
Chris@767
|
132 reference,
|
Chris@767
|
133 toAlign,
|
Chris@767
|
134 withTuningDifference);
|
Chris@767
|
135 break;
|
Chris@767
|
136 }
|
Chris@767
|
137
|
Chris@767
|
138 case SungPitchContourAlignment:
|
Chris@767
|
139 {
|
Chris@767
|
140 auto refModel = ModelById::get(reference);
|
Chris@767
|
141 if (!refModel) return false;
|
Chris@767
|
142
|
Chris@767
|
143 Transform transform = TransformFactory::getInstance()->
|
Chris@767
|
144 getDefaultTransformFor("vamp:pyin:pyin:smoothedpitchtrack",
|
Chris@767
|
145 refModel->getSampleRate());
|
Chris@767
|
146
|
Chris@767
|
147 transform.setParameter("outputunvoiced", 2.f);
|
Chris@767
|
148
|
Chris@767
|
149 aligner = make_shared<TransformDTWAligner>
|
Chris@767
|
150 (doc,
|
Chris@767
|
151 reference,
|
Chris@767
|
152 toAlign,
|
Chris@767
|
153 transform,
|
Chris@767
|
154 TransformDTWAligner::RiseFall);
|
Chris@767
|
155 break;
|
Chris@767
|
156 }
|
Chris@767
|
157
|
Chris@767
|
158 case TransformDrivenDTWAlignment:
|
Chris@767
|
159 throw std::logic_error("Not yet implemented"); //!!!
|
Chris@767
|
160
|
Chris@767
|
161 case ExternalProgramAlignment: {
|
Chris@767
|
162 aligner = make_shared<ExternalProgramAligner>(doc,
|
Chris@767
|
163 reference,
|
Chris@767
|
164 toAlign,
|
Chris@767
|
165 additionalData);
|
Chris@767
|
166 }
|
Chris@767
|
167 }
|
Chris@767
|
168
|
Chris@767
|
169 m_aligners[toAlign] = aligner;
|
Chris@753
|
170 }
|
Chris@753
|
171
|
Chris@753
|
172 connect(aligner.get(), SIGNAL(complete(ModelId)),
|
Chris@753
|
173 this, SLOT(alignerComplete(ModelId)));
|
Chris@761
|
174
|
Chris@761
|
175 connect(aligner.get(), SIGNAL(failed(ModelId, QString)),
|
Chris@761
|
176 this, SLOT(alignerFailed(ModelId, QString)));
|
Chris@767
|
177
|
Chris@767
|
178 return true;
|
Chris@767
|
179 }
|
Chris@767
|
180
|
Chris@767
|
181 Align::AlignmentType
|
Chris@767
|
182 Align::getAlignmentPreference(QString &additionalData)
|
Chris@767
|
183 {
|
Chris@767
|
184 QSettings settings;
|
Chris@767
|
185 settings.beginGroup("Alignment");
|
Chris@767
|
186
|
Chris@767
|
187 QString tag = settings.value
|
Chris@767
|
188 ("alignment-type", getAlignmentTypeTag(MATCHAlignment)).toString();
|
Chris@767
|
189
|
Chris@767
|
190 AlignmentType type = getAlignmentTypeForTag(tag);
|
Chris@767
|
191
|
Chris@767
|
192 if (type == TransformDrivenDTWAlignment) {
|
Chris@767
|
193 additionalData = settings.value("alignment-transform", "").toString();
|
Chris@767
|
194 } else if (type == ExternalProgramAlignment) {
|
Chris@767
|
195 additionalData = settings.value("alignment-program", "").toString();
|
Chris@767
|
196 }
|
Chris@767
|
197
|
Chris@767
|
198 settings.endGroup();
|
Chris@767
|
199 return type;
|
Chris@753
|
200 }
|
Chris@753
|
201
|
Chris@753
|
202 void
|
Chris@767
|
203 Align::setAlignmentPreference(AlignmentType type, QString additionalData)
|
Chris@422
|
204 {
|
Chris@422
|
205 QSettings settings;
|
Chris@767
|
206 settings.beginGroup("Alignment");
|
Chris@767
|
207
|
Chris@767
|
208 QString tag = getAlignmentTypeTag(type);
|
Chris@767
|
209 settings.setValue("alignment-type", tag);
|
Chris@767
|
210
|
Chris@767
|
211 if (type == TransformDrivenDTWAlignment) {
|
Chris@767
|
212 settings.setValue("alignment-transform", additionalData);
|
Chris@767
|
213 } else if (type == ExternalProgramAlignment) {
|
Chris@767
|
214 settings.setValue("alignment-program", additionalData);
|
Chris@767
|
215 }
|
Chris@767
|
216
|
Chris@422
|
217 settings.endGroup();
|
Chris@670
|
218 }
|
Chris@670
|
219
|
Chris@428
|
220 bool
|
Chris@428
|
221 Align::canAlign()
|
Chris@428
|
222 {
|
Chris@767
|
223 QString additionalData;
|
Chris@767
|
224 AlignmentType type = getAlignmentPreference(additionalData);
|
Chris@753
|
225
|
Chris@767
|
226 if (type == ExternalProgramAlignment) {
|
Chris@767
|
227 return ExternalProgramAligner::isAvailable(additionalData);
|
Chris@753
|
228 } else {
|
Chris@753
|
229 return TransformAligner::isAvailable();
|
Chris@753
|
230 }
|
Chris@428
|
231 }
|
Chris@428
|
232
|
Chris@702
|
233 void
|
Chris@753
|
234 Align::alignerComplete(ModelId alignmentModel)
|
Chris@702
|
235 {
|
Chris@761
|
236 removeAligner(sender());
|
Chris@761
|
237 emit alignmentComplete(alignmentModel);
|
Chris@761
|
238 }
|
Chris@761
|
239
|
Chris@761
|
240 void
|
Chris@761
|
241 Align::alignerFailed(ModelId toAlign, QString error)
|
Chris@761
|
242 {
|
Chris@761
|
243 removeAligner(sender());
|
Chris@761
|
244 emit alignmentFailed(toAlign, error);
|
Chris@761
|
245 }
|
Chris@761
|
246
|
Chris@761
|
247 void
|
Chris@761
|
248 Align::removeAligner(QObject *obj)
|
Chris@761
|
249 {
|
Chris@761
|
250 Aligner *aligner = qobject_cast<Aligner *>(obj);
|
Chris@753
|
251 if (!aligner) {
|
Chris@761
|
252 SVCERR << "ERROR: Align::removeAligner: Not an Aligner" << endl;
|
Chris@702
|
253 return;
|
Chris@702
|
254 }
|
Chris@702
|
255
|
Chris@761
|
256 QMutexLocker locker (&m_mutex);
|
Chris@702
|
257
|
Chris@761
|
258 for (auto p: m_aligners) {
|
Chris@761
|
259 if (aligner == p.second.get()) {
|
Chris@761
|
260 m_aligners.erase(p.first);
|
Chris@761
|
261 break;
|
Chris@702
|
262 }
|
Chris@702
|
263 }
|
Chris@761
|
264 }
|
Chris@702
|
265
|