Mercurial > hg > svapp
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) |