comparison align/Align.cpp @ 778:83a7b10b7415

Merge from branch pitch-align
author Chris Cannam
date Fri, 26 Jun 2020 13:48:52 +0100
parents 87d33e79855b
children b651dc5ff555
comparison
equal deleted inserted replaced
774:7bded7599874 778:83a7b10b7415
11 License, or (at your option) any later version. See the file 11 License, or (at your option) any later version. See the file
12 COPYING included with this distribution for more information. 12 COPYING included with this distribution for more information.
13 */ 13 */
14 14
15 #include "Align.h" 15 #include "Align.h"
16 #include "TransformAligner.h" 16
17 #include "LinearAligner.h"
18 #include "MATCHAligner.h"
19 #include "TransformDTWAligner.h"
17 #include "ExternalProgramAligner.h" 20 #include "ExternalProgramAligner.h"
21
18 #include "framework/Document.h" 22 #include "framework/Document.h"
23
24 #include "transform/Transform.h"
25 #include "transform/TransformFactory.h"
26
27 #include "base/Pitch.h"
19 28
20 #include <QSettings> 29 #include <QSettings>
21 #include <QTimer> 30 #include <QTimer>
31
32 using std::make_shared;
33
34 QString
35 Align::getAlignmentTypeTag(AlignmentType type)
36 {
37 switch (type) {
38 case NoAlignment:
39 default:
40 return "no-alignment";
41 case LinearAlignment:
42 return "linear-alignment";
43 case TrimmedLinearAlignment:
44 return "trimmed-linear-alignment";
45 case MATCHAlignment:
46 return "match-alignment";
47 case MATCHAlignmentWithPitchCompare:
48 return "match-alignment-with-pitch";
49 case SungNoteContourAlignment:
50 return "sung-note-alignment";
51 case TransformDrivenDTWAlignment:
52 return "transform-driven-alignment";
53 case ExternalProgramAlignment:
54 return "external-program-alignment";
55 }
56 }
57
58 Align::AlignmentType
59 Align::getAlignmentTypeForTag(QString tag)
60 {
61 for (int i = 0; i <= int(LastAlignmentType); ++i) {
62 if (tag == getAlignmentTypeTag(AlignmentType(i))) {
63 return AlignmentType(i);
64 }
65 }
66 return NoAlignment;
67 }
22 68
23 void 69 void
24 Align::alignModel(Document *doc, 70 Align::alignModel(Document *doc,
25 ModelId reference, 71 ModelId reference,
26 ModelId toAlign) 72 ModelId toAlign)
27 { 73 {
28 addAligner(doc, reference, toAlign); 74 if (addAligner(doc, reference, toAlign)) {
29 m_aligners[toAlign]->begin(); 75 m_aligners[toAlign]->begin();
76 }
30 } 77 }
31 78
32 void 79 void
33 Align::scheduleAlignment(Document *doc, 80 Align::scheduleAlignment(Document *doc,
34 ModelId reference, 81 ModelId reference,
35 ModelId toAlign) 82 ModelId toAlign)
36 { 83 {
37 addAligner(doc, reference, toAlign); 84 int delay = 700 * int(m_aligners.size());
38 int delay = 500 + 500 * int(m_aligners.size());
39 if (delay > 3500) { 85 if (delay > 3500) {
40 delay = 3500; 86 delay = 3500;
41 } 87 }
88 if (!addAligner(doc, reference, toAlign)) {
89 return;
90 }
42 SVCERR << "Align::scheduleAlignment: delaying " << delay << "ms" << endl; 91 SVCERR << "Align::scheduleAlignment: delaying " << delay << "ms" << endl;
43 QTimer::singleShot(delay, m_aligners[toAlign].get(), SLOT(begin())); 92 QTimer::singleShot(delay, m_aligners[toAlign].get(), SLOT(begin()));
44 } 93 }
45 94
46 void 95 bool
47 Align::addAligner(Document *doc, 96 Align::addAligner(Document *doc,
48 ModelId reference, 97 ModelId reference,
49 ModelId toAlign) 98 ModelId toAlign)
50 { 99 {
51 bool useProgram; 100 AlignmentType type = getAlignmentPreference();
52 QString program;
53 getAlignerPreference(useProgram, program);
54 101
55 std::shared_ptr<Aligner> aligner; 102 std::shared_ptr<Aligner> aligner;
56 103
104 if (m_aligners.find(toAlign) != m_aligners.end()) {
105 // We don't want a callback on removeAligner to happen during
106 // our own call to addAligner! Disconnect and delete the old
107 // aligner first
108 disconnect(m_aligners[toAlign].get(), nullptr, this, nullptr);
109 m_aligners.erase(toAlign);
110 }
111
57 { 112 {
58 // Replace the aligner with a new one. This also stops any 113 // Replace the aligner with a new one. This also stops any
59 // previously-running alignment, when the old entry is 114 // previously-running alignment, when the old entry is
60 // replaced and its aligner destroyed. 115 // replaced and its aligner destroyed.
61 116
62 QMutexLocker locker(&m_mutex); 117 QMutexLocker locker(&m_mutex);
63 118
64 if (useProgram && (program != "")) { 119 switch (type) {
65 m_aligners[toAlign] = 120
66 std::make_shared<ExternalProgramAligner>(doc, 121 case NoAlignment:
67 reference, 122 return false;
68 toAlign, 123
69 program); 124 case LinearAlignment:
70 } else { 125 case TrimmedLinearAlignment: {
71 m_aligners[toAlign] = 126 bool trimmed = (type == TrimmedLinearAlignment);
72 std::make_shared<TransformAligner>(doc, 127 aligner = make_shared<LinearAligner>(doc,
73 reference, 128 reference,
74 toAlign); 129 toAlign,
75 } 130 trimmed);
76 131 break;
77 aligner = m_aligners[toAlign]; 132 }
133
134 case MATCHAlignment:
135 case MATCHAlignmentWithPitchCompare: {
136
137 bool withTuningDifference =
138 (type == MATCHAlignmentWithPitchCompare);
139
140 aligner = make_shared<MATCHAligner>(doc,
141 reference,
142 toAlign,
143 withTuningDifference);
144 break;
145 }
146
147 case SungNoteContourAlignment:
148 {
149 auto refModel = ModelById::get(reference);
150 if (!refModel) return false;
151
152 Transform transform = TransformFactory::getInstance()->
153 getDefaultTransformFor("vamp:pyin:pyin:notes",
154 refModel->getSampleRate());
155
156 aligner = make_shared<TransformDTWAligner>
157 (doc,
158 reference,
159 toAlign,
160 transform,
161 [](double prev, double curr) {
162 RiseFallDTW::Value v;
163 if (curr <= 0.0) {
164 v = { RiseFallDTW::Direction::None, 0.0 };
165 } else if (prev <= 0.0) {
166 v = { RiseFallDTW::Direction::Up, 0.0 };
167 } else {
168 double prevP = Pitch::getPitchForFrequency(prev);
169 double currP = Pitch::getPitchForFrequency(curr);
170 if (currP >= prevP) {
171 v = { RiseFallDTW::Direction::Up, currP - prevP };
172 } else {
173 v = { RiseFallDTW::Direction::Down, prevP - currP };
174 }
175 }
176 return v;
177 });
178 break;
179 }
180
181 case TransformDrivenDTWAlignment:
182 throw std::logic_error("Not yet implemented"); //!!!
183
184 case ExternalProgramAlignment: {
185 aligner = make_shared<ExternalProgramAligner>
186 (doc,
187 reference,
188 toAlign,
189 getPreferredAlignmentProgram());
190 }
191 }
192
193 m_aligners[toAlign] = aligner;
78 } 194 }
79 195
80 connect(aligner.get(), SIGNAL(complete(ModelId)), 196 connect(aligner.get(), SIGNAL(complete(ModelId)),
81 this, SLOT(alignerComplete(ModelId))); 197 this, SLOT(alignerComplete(ModelId)));
82 198
83 connect(aligner.get(), SIGNAL(failed(ModelId, QString)), 199 connect(aligner.get(), SIGNAL(failed(ModelId, QString)),
84 this, SLOT(alignerFailed(ModelId, QString))); 200 this, SLOT(alignerFailed(ModelId, QString)));
85 } 201
86 202 return true;
87 void 203 }
88 Align::getAlignerPreference(bool &useProgram, QString &program) 204
89 { 205 Align::AlignmentType
90 QSettings settings; 206 Align::getAlignmentPreference()
91 settings.beginGroup("Preferences"); 207 {
92 useProgram = settings.value("use-external-alignment", false).toBool(); 208 QSettings settings;
93 program = settings.value("external-alignment-program", "").toString(); 209 settings.beginGroup("Alignment");
210 QString tag = settings.value
211 ("alignment-type", getAlignmentTypeTag(MATCHAlignment)).toString();
212 return getAlignmentTypeForTag(tag);
213 }
214
215 QString
216 Align::getPreferredAlignmentProgram()
217 {
218 QSettings settings;
219 settings.beginGroup("Alignment");
220 return settings.value("alignment-program", "").toString();
221 }
222
223 Transform
224 Align::getPreferredAlignmentTransform()
225 {
226 QSettings settings;
227 settings.beginGroup("Alignment");
228 QString xml = settings.value("alignment-transform", "").toString();
229 return Transform(xml);
230 }
231
232 void
233 Align::setAlignmentPreference(AlignmentType type)
234 {
235 QSettings settings;
236 settings.beginGroup("Alignment");
237 QString tag = getAlignmentTypeTag(type);
238 settings.setValue("alignment-type", tag);
239 settings.endGroup();
240 }
241
242 void
243 Align::setPreferredAlignmentProgram(QString program)
244 {
245 QSettings settings;
246 settings.beginGroup("Alignment");
247 settings.setValue("alignment-program", program);
248 settings.endGroup();
249 }
250
251 void
252 Align::setPreferredAlignmentTransform(Transform transform)
253 {
254 QSettings settings;
255 settings.beginGroup("Alignment");
256 settings.setValue("alignment-transform", transform.toXmlString());
94 settings.endGroup(); 257 settings.endGroup();
95 } 258 }
96 259
97 bool 260 bool
98 Align::canAlign() 261 Align::canAlign()
99 { 262 {
100 bool useProgram; 263 AlignmentType type = getAlignmentPreference();
101 QString program; 264
102 getAlignerPreference(useProgram, program); 265 if (type == ExternalProgramAlignment) {
103 266 return ExternalProgramAligner::isAvailable
104 if (useProgram) { 267 (getPreferredAlignmentProgram());
105 return ExternalProgramAligner::isAvailable(program);
106 } else { 268 } else {
107 return TransformAligner::isAvailable(); 269 return MATCHAligner::isAvailable();
108 } 270 }
109 } 271 }
110 272
111 void 273 void
112 Align::alignerComplete(ModelId alignmentModel) 274 Align::alignerComplete(ModelId alignmentModel)