annotate align/Align.cpp @ 767:dd742e566e60 pitch-align

Make a start on further alignment methods
author Chris Cannam
date Thu, 21 May 2020 16:21:57 +0100
parents 6429a164b7e1
children 1b1960009be6
rev   line source
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