Mercurial > hg > svapp
changeset 777:87d33e79855b pitch-align
Rename TransformAligner -> MATCHAligner. It is still specific to MATCH, and at this point I think it's simpler to leave it that way and reduce confusion with the TransformDTWAligner which is much more generic.
author | Chris Cannam |
---|---|
date | Thu, 25 Jun 2020 17:43:10 +0100 |
parents | 32e66fcc4cb7 |
children | 83a7b10b7415 |
files | align/Align.cpp align/MATCHAligner.cpp align/MATCHAligner.h align/TransformAligner.cpp align/TransformAligner.h files.pri |
diffstat | 6 files changed, 465 insertions(+), 467 deletions(-) [+] |
line wrap: on
line diff
--- a/align/Align.cpp Thu Jun 25 09:32:01 2020 +0100 +++ b/align/Align.cpp Thu Jun 25 17:43:10 2020 +0100 @@ -15,7 +15,7 @@ #include "Align.h" #include "LinearAligner.h" -#include "TransformAligner.h" +#include "MATCHAligner.h" #include "TransformDTWAligner.h" #include "ExternalProgramAligner.h" @@ -137,10 +137,10 @@ bool withTuningDifference = (type == MATCHAlignmentWithPitchCompare); - aligner = make_shared<TransformAligner>(doc, - reference, - toAlign, - withTuningDifference); + aligner = make_shared<MATCHAligner>(doc, + reference, + toAlign, + withTuningDifference); break; } @@ -266,7 +266,7 @@ return ExternalProgramAligner::isAvailable (getPreferredAlignmentProgram()); } else { - return TransformAligner::isAvailable(); + return MATCHAligner::isAvailable(); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/align/MATCHAligner.cpp Thu Jun 25 17:43:10 2020 +0100 @@ -0,0 +1,395 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "MATCHAligner.h" + +#include "data/model/SparseTimeValueModel.h" +#include "data/model/RangeSummarisableTimeValueModel.h" +#include "data/model/AlignmentModel.h" +#include "data/model/AggregateWaveModel.h" + +#include "framework/Document.h" + +#include "transform/TransformFactory.h" +#include "transform/ModelTransformerFactory.h" +#include "transform/FeatureExtractionModelTransformer.h" + +#include <QSettings> + +MATCHAligner::MATCHAligner(Document *doc, + ModelId reference, + ModelId toAlign, + bool withTuningDifference) : + m_document(doc), + m_reference(reference), + m_toAlign(toAlign), + m_withTuningDifference(withTuningDifference), + m_tuningFrequency(440.f), + m_incomplete(true) +{ +} + +MATCHAligner::~MATCHAligner() +{ + if (m_incomplete) { + auto other = + ModelById::getAs<RangeSummarisableTimeValueModel>(m_toAlign); + if (other) { + other->setAlignment({}); + } + } + + ModelById::release(m_tuningDiffOutputModel); + ModelById::release(m_pathOutputModel); +} + +QString +MATCHAligner::getAlignmentTransformName() +{ + QSettings settings; + settings.beginGroup("Alignment"); + TransformId id = settings.value + ("transform-id", + "vamp:match-vamp-plugin:match:path").toString(); + settings.endGroup(); + return id; +} + +QString +MATCHAligner::getTuningDifferenceTransformName() +{ + QSettings settings; + settings.beginGroup("Alignment"); + TransformId id = settings.value + ("tuning-difference-transform-id", + "vamp:tuning-difference:tuning-difference:tuningfreq") + .toString(); + settings.endGroup(); + return id; +} + +bool +MATCHAligner::isAvailable() +{ + TransformFactory *factory = TransformFactory::getInstance(); + TransformId id = getAlignmentTransformName(); + TransformId tdId = getTuningDifferenceTransformName(); + return factory->haveTransform(id) && + (tdId == "" || factory->haveTransform(tdId)); +} + +void +MATCHAligner::begin() +{ + auto reference = + ModelById::getAs<RangeSummarisableTimeValueModel>(m_reference); + auto other = + ModelById::getAs<RangeSummarisableTimeValueModel>(m_toAlign); + + if (!reference || !other) return; + + // This involves creating a number of new models: + // + // 1. an AggregateWaveModel to provide the mixdowns of the main + // model and the new model in its two channels, as input to the + // MATCH plugin. We just call this one aggregateModel + // + // 2a. a SparseTimeValueModel which will be automatically created + // by FeatureExtractionModelTransformer when running the + // TuningDifference plugin to receive the relative tuning of the + // second model (if pitch-aware alignment is enabled in the + // preferences). This is m_tuningDiffOutputModel. + // + // 2b. a SparseTimeValueModel which will be automatically created + // by FeatureExtractionPluginTransformer when running the MATCH + // plugin to perform alignment (so containing the alignment path). + // This is m_pathOutputModel. + // + // 3. an AlignmentModel, which stores the path and carries out + // alignment lookups on it. This one is m_alignmentModel. + // + // Models 1 and 3 are registered with the document, which will + // eventually release them. We don't release them here except in + // the case where an activity fails before the point where we + // would otherwise have registered them with the document. + // + // Models 2a (m_tuningDiffOutputModel) and 2b (m_pathOutputModel) + // are not registered with the document, because they are not + // intended to persist. These have to be released by us when + // finished with, but their lifespans do not extend beyond the end + // of the alignment procedure, so this should be ok. + + AggregateWaveModel::ChannelSpecList components; + components.push_back + (AggregateWaveModel::ModelChannelSpec(m_reference, -1)); + + components.push_back + (AggregateWaveModel::ModelChannelSpec(m_toAlign, -1)); + + auto aggregateModel = std::make_shared<AggregateWaveModel>(components); + m_aggregateModel = ModelById::add(aggregateModel); + m_document->addNonDerivedModel(m_aggregateModel); + + auto alignmentModel = std::make_shared<AlignmentModel> + (m_reference, m_toAlign, ModelId()); + m_alignmentModel = ModelById::add(alignmentModel); + + TransformId tdId; + if (m_withTuningDifference) { + tdId = getTuningDifferenceTransformName(); + } + + if (tdId == "") { + + if (beginAlignmentPhase()) { + other->setAlignment(m_alignmentModel); + m_document->addNonDerivedModel(m_alignmentModel); + } else { + QString error = alignmentModel->getError(); + ModelById::release(alignmentModel); + emit failed(m_toAlign, error); + return; + } + + } else { + + // Have a tuning-difference transform id, so run it + // asynchronously first + + TransformFactory *tf = TransformFactory::getInstance(); + + Transform transform = tf->getDefaultTransformFor + (tdId, aggregateModel->getSampleRate()); + + transform.setParameter("maxduration", 60); + transform.setParameter("maxrange", 6); + transform.setParameter("finetuning", false); + + SVDEBUG << "MATCHAligner: Tuning difference transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; + + ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); + + QString message; + m_tuningDiffOutputModel = mtf->transform(transform, + m_aggregateModel, + message); + + auto tuningDiffOutputModel = + ModelById::getAs<SparseTimeValueModel>(m_tuningDiffOutputModel); + if (!tuningDiffOutputModel) { + SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl; + ModelById::release(alignmentModel); + emit failed(m_toAlign, message); + return; + } + + other->setAlignment(m_alignmentModel); + m_document->addNonDerivedModel(m_alignmentModel); + + connect(tuningDiffOutputModel.get(), + SIGNAL(completionChanged(ModelId)), + this, SLOT(tuningDifferenceCompletionChanged(ModelId))); + } +} + +void +MATCHAligner::tuningDifferenceCompletionChanged(ModelId tuningDiffOutputModelId) +{ + if (m_tuningDiffOutputModel.isNone()) { + // we're done, this is probably a spurious queued event + return; + } + + if (tuningDiffOutputModelId != m_tuningDiffOutputModel) { + SVCERR << "WARNING: MATCHAligner::tuningDifferenceCompletionChanged: Model " + << tuningDiffOutputModelId + << " is not ours! (ours is " + << m_tuningDiffOutputModel << ")" << endl; + return; + } + + auto tuningDiffOutputModel = + ModelById::getAs<SparseTimeValueModel>(m_tuningDiffOutputModel); + if (!tuningDiffOutputModel) { + SVCERR << "WARNING: MATCHAligner::tuningDifferenceCompletionChanged: Model " + << tuningDiffOutputModelId + << " not known as SparseTimeValueModel" << endl; + return; + } + + auto alignmentModel = ModelById::getAs<AlignmentModel>(m_alignmentModel); + if (!alignmentModel) { + SVCERR << "WARNING: MATCHAligner::tuningDifferenceCompletionChanged:" + << "alignment model has disappeared" << endl; + return; + } + + int completion = 0; + bool done = tuningDiffOutputModel->isReady(&completion); + + SVDEBUG << "MATCHAligner::tuningDifferenceCompletionChanged: model " + << m_tuningDiffOutputModel << ", completion = " << completion + << ", done = " << done << endl; + + if (!done) { + // This will be the completion the alignment model reports, + // before the alignment actually begins + alignmentModel->setCompletion(completion / 2); + return; + } + + m_tuningFrequency = 440.f; + + if (!tuningDiffOutputModel->isEmpty()) { + m_tuningFrequency = tuningDiffOutputModel->getAllEvents()[0].getValue(); + SVCERR << "MATCHAligner::tuningDifferenceCompletionChanged: Reported tuning frequency = " << m_tuningFrequency << endl; + } else { + SVCERR << "MATCHAligner::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl; + } + + ModelById::release(tuningDiffOutputModel); + m_tuningDiffOutputModel = {}; + + beginAlignmentPhase(); +} + +bool +MATCHAligner::beginAlignmentPhase() +{ + TransformId id = getAlignmentTransformName(); + + SVDEBUG << "MATCHAligner::beginAlignmentPhase: transform is " + << id << endl; + + TransformFactory *tf = TransformFactory::getInstance(); + + auto aggregateModel = + ModelById::getAs<AggregateWaveModel>(m_aggregateModel); + auto alignmentModel = + ModelById::getAs<AlignmentModel>(m_alignmentModel); + + if (!aggregateModel || !alignmentModel) { + SVCERR << "MATCHAligner::alignModel: ERROR: One or other of the aggregate & alignment models has disappeared" << endl; + return false; + } + + Transform transform = tf->getDefaultTransformFor + (id, aggregateModel->getSampleRate()); + + transform.setStepSize(transform.getBlockSize()/2); + transform.setParameter("serialise", 1); + transform.setParameter("smooth", 0); + transform.setParameter("zonewidth", 40); + transform.setParameter("noise", true); + transform.setParameter("minfreq", 500); + + int cents = 0; + + if (m_tuningFrequency != 0.f) { + transform.setParameter("freq2", m_tuningFrequency); + + double centsOffset = 0.f; + int pitch = Pitch::getPitchForFrequency(m_tuningFrequency, + ¢sOffset); + cents = int(round((pitch - 69) * 100 + centsOffset)); + SVCERR << "MATCHAligner: frequency " << m_tuningFrequency + << " yields cents offset " << centsOffset + << " and pitch " << pitch << " -> cents " << cents << endl; + } + + alignmentModel->setRelativePitch(cents); + + SVDEBUG << "MATCHAligner: Alignment transform step size " + << transform.getStepSize() << ", block size " + << transform.getBlockSize() << endl; + + ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); + + QString message; + m_pathOutputModel = mtf->transform + (transform, m_aggregateModel, message); + + if (m_pathOutputModel.isNone()) { + transform.setStepSize(0); + m_pathOutputModel = mtf->transform + (transform, m_aggregateModel, message); + } + + auto pathOutputModel = + ModelById::getAs<SparseTimeValueModel>(m_pathOutputModel); + + //!!! callers will need to be updated to get error from + //!!! alignment model after initial call + + if (!pathOutputModel) { + SVCERR << "MATCHAligner: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; + alignmentModel->setError(message); + return false; + } + + pathOutputModel->setCompletion(0); + alignmentModel->setPathFrom(m_pathOutputModel); + + connect(pathOutputModel.get(), SIGNAL(completionChanged(ModelId)), + this, SLOT(alignmentCompletionChanged(ModelId))); + + return true; +} + +void +MATCHAligner::alignmentCompletionChanged(ModelId pathOutputModelId) +{ + if (pathOutputModelId != m_pathOutputModel) { + SVCERR << "WARNING: MATCHAligner::alignmentCompletionChanged: Model " + << pathOutputModelId + << " is not ours! (ours is " + << m_pathOutputModel << ")" << endl; + return; + } + + auto pathOutputModel = + ModelById::getAs<SparseTimeValueModel>(m_pathOutputModel); + if (!pathOutputModel) { + SVCERR << "WARNING: MATCHAligner::alignmentCompletionChanged: Path output model " + << m_pathOutputModel << " no longer exists" << endl; + return; + } + + int completion = 0; + bool done = pathOutputModel->isReady(&completion); + + if (m_withTuningDifference) { + if (auto alignmentModel = + ModelById::getAs<AlignmentModel>(m_alignmentModel)) { + if (!done) { + int adjustedCompletion = 50 + completion/2; + if (adjustedCompletion > 99) { + adjustedCompletion = 99; + } + alignmentModel->setCompletion(adjustedCompletion); + } else { + alignmentModel->setCompletion(100); + } + } + } + + if (done) { + m_incomplete = false; + + ModelById::release(m_pathOutputModel); + m_pathOutputModel = {}; + + emit complete(m_alignmentModel); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/align/MATCHAligner.h Thu Jun 25 17:43:10 2020 +0100 @@ -0,0 +1,62 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_MATCH_ALIGNER_H +#define SV_MATCH_ALIGNER_H + +#include "Aligner.h" + +class AlignmentModel; +class Document; + +class MATCHAligner : public Aligner +{ + Q_OBJECT + +public: + MATCHAligner(Document *doc, + ModelId reference, + ModelId toAlign, + bool withTuningDifference); + + // Destroy the aligner, cleanly cancelling any ongoing alignment + ~MATCHAligner(); + + void begin() override; + + static bool isAvailable(); + +private slots: + void alignmentCompletionChanged(ModelId); + void tuningDifferenceCompletionChanged(ModelId); + +private: + static QString getAlignmentTransformName(); + static QString getTuningDifferenceTransformName(); + + bool beginAlignmentPhase(); + + Document *m_document; + ModelId m_reference; + ModelId m_toAlign; + ModelId m_aggregateModel; // an AggregateWaveModel + ModelId m_alignmentModel; // an AlignmentModel + ModelId m_tuningDiffOutputModel; // SparseTimeValueModel, unreg'd with doc + ModelId m_pathOutputModel; // SparseTimeValueModel, unreg'd with doc + bool m_withTuningDifference; + float m_tuningFrequency; + bool m_incomplete; +}; + +#endif
--- a/align/TransformAligner.cpp Thu Jun 25 09:32:01 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,395 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "TransformAligner.h" - -#include "data/model/SparseTimeValueModel.h" -#include "data/model/RangeSummarisableTimeValueModel.h" -#include "data/model/AlignmentModel.h" -#include "data/model/AggregateWaveModel.h" - -#include "framework/Document.h" - -#include "transform/TransformFactory.h" -#include "transform/ModelTransformerFactory.h" -#include "transform/FeatureExtractionModelTransformer.h" - -#include <QSettings> - -TransformAligner::TransformAligner(Document *doc, - ModelId reference, - ModelId toAlign, - bool withTuningDifference) : - m_document(doc), - m_reference(reference), - m_toAlign(toAlign), - m_withTuningDifference(withTuningDifference), - m_tuningFrequency(440.f), - m_incomplete(true) -{ -} - -TransformAligner::~TransformAligner() -{ - if (m_incomplete) { - auto other = - ModelById::getAs<RangeSummarisableTimeValueModel>(m_toAlign); - if (other) { - other->setAlignment({}); - } - } - - ModelById::release(m_tuningDiffOutputModel); - ModelById::release(m_pathOutputModel); -} - -QString -TransformAligner::getAlignmentTransformName() -{ - QSettings settings; - settings.beginGroup("Alignment"); - TransformId id = settings.value - ("transform-id", - "vamp:match-vamp-plugin:match:path").toString(); - settings.endGroup(); - return id; -} - -QString -TransformAligner::getTuningDifferenceTransformName() -{ - QSettings settings; - settings.beginGroup("Alignment"); - TransformId id = settings.value - ("tuning-difference-transform-id", - "vamp:tuning-difference:tuning-difference:tuningfreq") - .toString(); - settings.endGroup(); - return id; -} - -bool -TransformAligner::isAvailable() -{ - TransformFactory *factory = TransformFactory::getInstance(); - TransformId id = getAlignmentTransformName(); - TransformId tdId = getTuningDifferenceTransformName(); - return factory->haveTransform(id) && - (tdId == "" || factory->haveTransform(tdId)); -} - -void -TransformAligner::begin() -{ - auto reference = - ModelById::getAs<RangeSummarisableTimeValueModel>(m_reference); - auto other = - ModelById::getAs<RangeSummarisableTimeValueModel>(m_toAlign); - - if (!reference || !other) return; - - // This involves creating a number of new models: - // - // 1. an AggregateWaveModel to provide the mixdowns of the main - // model and the new model in its two channels, as input to the - // MATCH plugin. We just call this one aggregateModel - // - // 2a. a SparseTimeValueModel which will be automatically created - // by FeatureExtractionModelTransformer when running the - // TuningDifference plugin to receive the relative tuning of the - // second model (if pitch-aware alignment is enabled in the - // preferences). This is m_tuningDiffOutputModel. - // - // 2b. a SparseTimeValueModel which will be automatically created - // by FeatureExtractionPluginTransformer when running the MATCH - // plugin to perform alignment (so containing the alignment path). - // This is m_pathOutputModel. - // - // 3. an AlignmentModel, which stores the path and carries out - // alignment lookups on it. This one is m_alignmentModel. - // - // Models 1 and 3 are registered with the document, which will - // eventually release them. We don't release them here except in - // the case where an activity fails before the point where we - // would otherwise have registered them with the document. - // - // Models 2a (m_tuningDiffOutputModel) and 2b (m_pathOutputModel) - // are not registered with the document, because they are not - // intended to persist. These have to be released by us when - // finished with, but their lifespans do not extend beyond the end - // of the alignment procedure, so this should be ok. - - AggregateWaveModel::ChannelSpecList components; - components.push_back - (AggregateWaveModel::ModelChannelSpec(m_reference, -1)); - - components.push_back - (AggregateWaveModel::ModelChannelSpec(m_toAlign, -1)); - - auto aggregateModel = std::make_shared<AggregateWaveModel>(components); - m_aggregateModel = ModelById::add(aggregateModel); - m_document->addNonDerivedModel(m_aggregateModel); - - auto alignmentModel = std::make_shared<AlignmentModel> - (m_reference, m_toAlign, ModelId()); - m_alignmentModel = ModelById::add(alignmentModel); - - TransformId tdId; - if (m_withTuningDifference) { - tdId = getTuningDifferenceTransformName(); - } - - if (tdId == "") { - - if (beginAlignmentPhase()) { - other->setAlignment(m_alignmentModel); - m_document->addNonDerivedModel(m_alignmentModel); - } else { - QString error = alignmentModel->getError(); - ModelById::release(alignmentModel); - emit failed(m_toAlign, error); - return; - } - - } else { - - // Have a tuning-difference transform id, so run it - // asynchronously first - - TransformFactory *tf = TransformFactory::getInstance(); - - Transform transform = tf->getDefaultTransformFor - (tdId, aggregateModel->getSampleRate()); - - transform.setParameter("maxduration", 60); - transform.setParameter("maxrange", 6); - transform.setParameter("finetuning", false); - - SVDEBUG << "TransformAligner: Tuning difference transform step size " << transform.getStepSize() << ", block size " << transform.getBlockSize() << endl; - - ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); - - QString message; - m_tuningDiffOutputModel = mtf->transform(transform, - m_aggregateModel, - message); - - auto tuningDiffOutputModel = - ModelById::getAs<SparseTimeValueModel>(m_tuningDiffOutputModel); - if (!tuningDiffOutputModel) { - SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl; - ModelById::release(alignmentModel); - emit failed(m_toAlign, message); - return; - } - - other->setAlignment(m_alignmentModel); - m_document->addNonDerivedModel(m_alignmentModel); - - connect(tuningDiffOutputModel.get(), - SIGNAL(completionChanged(ModelId)), - this, SLOT(tuningDifferenceCompletionChanged(ModelId))); - } -} - -void -TransformAligner::tuningDifferenceCompletionChanged(ModelId tuningDiffOutputModelId) -{ - if (m_tuningDiffOutputModel.isNone()) { - // we're done, this is probably a spurious queued event - return; - } - - if (tuningDiffOutputModelId != m_tuningDiffOutputModel) { - SVCERR << "WARNING: TransformAligner::tuningDifferenceCompletionChanged: Model " - << tuningDiffOutputModelId - << " is not ours! (ours is " - << m_tuningDiffOutputModel << ")" << endl; - return; - } - - auto tuningDiffOutputModel = - ModelById::getAs<SparseTimeValueModel>(m_tuningDiffOutputModel); - if (!tuningDiffOutputModel) { - SVCERR << "WARNING: TransformAligner::tuningDifferenceCompletionChanged: Model " - << tuningDiffOutputModelId - << " not known as SparseTimeValueModel" << endl; - return; - } - - auto alignmentModel = ModelById::getAs<AlignmentModel>(m_alignmentModel); - if (!alignmentModel) { - SVCERR << "WARNING: TransformAligner::tuningDifferenceCompletionChanged:" - << "alignment model has disappeared" << endl; - return; - } - - int completion = 0; - bool done = tuningDiffOutputModel->isReady(&completion); - - SVDEBUG << "TransformAligner::tuningDifferenceCompletionChanged: model " - << m_tuningDiffOutputModel << ", completion = " << completion - << ", done = " << done << endl; - - if (!done) { - // This will be the completion the alignment model reports, - // before the alignment actually begins - alignmentModel->setCompletion(completion / 2); - return; - } - - m_tuningFrequency = 440.f; - - if (!tuningDiffOutputModel->isEmpty()) { - m_tuningFrequency = tuningDiffOutputModel->getAllEvents()[0].getValue(); - SVCERR << "TransformAligner::tuningDifferenceCompletionChanged: Reported tuning frequency = " << m_tuningFrequency << endl; - } else { - SVCERR << "TransformAligner::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl; - } - - ModelById::release(tuningDiffOutputModel); - m_tuningDiffOutputModel = {}; - - beginAlignmentPhase(); -} - -bool -TransformAligner::beginAlignmentPhase() -{ - TransformId id = getAlignmentTransformName(); - - SVDEBUG << "TransformAligner::beginAlignmentPhase: transform is " - << id << endl; - - TransformFactory *tf = TransformFactory::getInstance(); - - auto aggregateModel = - ModelById::getAs<AggregateWaveModel>(m_aggregateModel); - auto alignmentModel = - ModelById::getAs<AlignmentModel>(m_alignmentModel); - - if (!aggregateModel || !alignmentModel) { - SVCERR << "TransformAligner::alignModel: ERROR: One or other of the aggregate & alignment models has disappeared" << endl; - return false; - } - - Transform transform = tf->getDefaultTransformFor - (id, aggregateModel->getSampleRate()); - - transform.setStepSize(transform.getBlockSize()/2); - transform.setParameter("serialise", 1); - transform.setParameter("smooth", 0); - transform.setParameter("zonewidth", 40); - transform.setParameter("noise", true); - transform.setParameter("minfreq", 500); - - int cents = 0; - - if (m_tuningFrequency != 0.f) { - transform.setParameter("freq2", m_tuningFrequency); - - double centsOffset = 0.f; - int pitch = Pitch::getPitchForFrequency(m_tuningFrequency, - ¢sOffset); - cents = int(round((pitch - 69) * 100 + centsOffset)); - SVCERR << "TransformAligner: frequency " << m_tuningFrequency - << " yields cents offset " << centsOffset - << " and pitch " << pitch << " -> cents " << cents << endl; - } - - alignmentModel->setRelativePitch(cents); - - SVDEBUG << "TransformAligner: Alignment transform step size " - << transform.getStepSize() << ", block size " - << transform.getBlockSize() << endl; - - ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); - - QString message; - m_pathOutputModel = mtf->transform - (transform, m_aggregateModel, message); - - if (m_pathOutputModel.isNone()) { - transform.setStepSize(0); - m_pathOutputModel = mtf->transform - (transform, m_aggregateModel, message); - } - - auto pathOutputModel = - ModelById::getAs<SparseTimeValueModel>(m_pathOutputModel); - - //!!! callers will need to be updated to get error from - //!!! alignment model after initial call - - if (!pathOutputModel) { - SVCERR << "TransformAligner: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; - alignmentModel->setError(message); - return false; - } - - pathOutputModel->setCompletion(0); - alignmentModel->setPathFrom(m_pathOutputModel); - - connect(pathOutputModel.get(), SIGNAL(completionChanged(ModelId)), - this, SLOT(alignmentCompletionChanged(ModelId))); - - return true; -} - -void -TransformAligner::alignmentCompletionChanged(ModelId pathOutputModelId) -{ - if (pathOutputModelId != m_pathOutputModel) { - SVCERR << "WARNING: TransformAligner::alignmentCompletionChanged: Model " - << pathOutputModelId - << " is not ours! (ours is " - << m_pathOutputModel << ")" << endl; - return; - } - - auto pathOutputModel = - ModelById::getAs<SparseTimeValueModel>(m_pathOutputModel); - if (!pathOutputModel) { - SVCERR << "WARNING: TransformAligner::alignmentCompletionChanged: Path output model " - << m_pathOutputModel << " no longer exists" << endl; - return; - } - - int completion = 0; - bool done = pathOutputModel->isReady(&completion); - - if (m_withTuningDifference) { - if (auto alignmentModel = - ModelById::getAs<AlignmentModel>(m_alignmentModel)) { - if (!done) { - int adjustedCompletion = 50 + completion/2; - if (adjustedCompletion > 99) { - adjustedCompletion = 99; - } - alignmentModel->setCompletion(adjustedCompletion); - } else { - alignmentModel->setCompletion(100); - } - } - } - - if (done) { - m_incomplete = false; - - ModelById::release(m_pathOutputModel); - m_pathOutputModel = {}; - - emit complete(m_alignmentModel); - } -}
--- a/align/TransformAligner.h Thu Jun 25 09:32:01 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef SV_TRANSFORM_ALIGNER_H -#define SV_TRANSFORM_ALIGNER_H - -#include "Aligner.h" - -class AlignmentModel; -class Document; - -class TransformAligner : public Aligner -{ - Q_OBJECT - -public: - TransformAligner(Document *doc, - ModelId reference, - ModelId toAlign, - bool withTuningDifference); - - //!!! pass in transform id - - // Destroy the aligner, cleanly cancelling any ongoing alignment - ~TransformAligner(); - - void begin() override; - - static bool isAvailable(); - -private slots: - void alignmentCompletionChanged(ModelId); - void tuningDifferenceCompletionChanged(ModelId); - -private: - static QString getAlignmentTransformName(); - static QString getTuningDifferenceTransformName(); - - bool beginAlignmentPhase(); - - Document *m_document; - ModelId m_reference; - ModelId m_toAlign; - ModelId m_aggregateModel; // an AggregateWaveModel - ModelId m_alignmentModel; // an AlignmentModel - ModelId m_tuningDiffOutputModel; // SparseTimeValueModel, unreg'd with doc - ModelId m_pathOutputModel; // SparseTimeValueModel, unreg'd with doc - bool m_withTuningDifference; - float m_tuningFrequency; - bool m_incomplete; -}; - -#endif
--- a/files.pri Thu Jun 25 09:32:01 2020 +0100 +++ b/files.pri Thu Jun 25 17:43:10 2020 +0100 @@ -4,7 +4,7 @@ align/Aligner.h \ align/ExternalProgramAligner.h \ align/LinearAligner.h \ - align/TransformAligner.h \ + align/MATCHAligner.h \ align/TransformDTWAligner.h \ audio/AudioCallbackPlaySource.h \ audio/AudioCallbackRecordTarget.h \ @@ -25,7 +25,7 @@ align/Align.cpp \ align/ExternalProgramAligner.cpp \ align/LinearAligner.cpp \ - align/TransformAligner.cpp \ + align/MATCHAligner.cpp \ align/TransformDTWAligner.cpp \ audio/AudioCallbackPlaySource.cpp \ audio/AudioCallbackRecordTarget.cpp \