annotate align/LinearAligner.cpp @ 771:1d6cca5a5621 pitch-align

Allow use of proper sparse models (i.e. retaining event time info) in alignment; use this to switch to note alignment, which is what we have most recently been doing in the external program. Not currently producing correct results, though
author Chris Cannam
date Fri, 29 May 2020 17:39:02 +0100
parents 486add472c3f
children 699b5b130ea2
rev   line source
Chris@767 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@767 2
Chris@767 3 /*
Chris@767 4 Sonic Visualiser
Chris@767 5 An audio file viewer and annotation editor.
Chris@767 6 Centre for Digital Music, Queen Mary, University of London.
Chris@767 7
Chris@767 8 This program is free software; you can redistribute it and/or
Chris@767 9 modify it under the terms of the GNU General Public License as
Chris@767 10 published by the Free Software Foundation; either version 2 of the
Chris@767 11 License, or (at your option) any later version. See the file
Chris@767 12 COPYING included with this distribution for more information.
Chris@767 13 */
Chris@767 14
Chris@767 15 #include "LinearAligner.h"
Chris@767 16
Chris@767 17 #include "system/System.h"
Chris@767 18
Chris@767 19 #include "data/model/Path.h"
Chris@767 20 #include "data/model/AlignmentModel.h"
Chris@767 21
Chris@767 22 #include "framework/Document.h"
Chris@767 23
Chris@770 24 #include "svcore/data/model/DenseTimeValueModel.h"
Chris@770 25
Chris@770 26 #include <QApplication>
Chris@770 27
Chris@767 28 LinearAligner::LinearAligner(Document *doc,
Chris@767 29 ModelId reference,
Chris@767 30 ModelId toAlign,
Chris@767 31 bool trimmed) :
Chris@767 32 m_document(doc),
Chris@767 33 m_reference(reference),
Chris@767 34 m_toAlign(toAlign),
Chris@767 35 m_trimmed(trimmed)
Chris@767 36 {
Chris@767 37 }
Chris@767 38
Chris@767 39 LinearAligner::~LinearAligner()
Chris@767 40 {
Chris@767 41 }
Chris@767 42
Chris@767 43 void
Chris@767 44 LinearAligner::begin()
Chris@767 45 {
Chris@767 46 bool ready = false;
Chris@767 47 while (!ready) {
Chris@767 48 { // scope so as to release input shared_ptr before sleeping
Chris@767 49 auto reference = ModelById::get(m_reference);
Chris@767 50 auto toAlign = ModelById::get(m_toAlign);
Chris@767 51 if (!reference || !reference->isOK() ||
Chris@767 52 !toAlign || !toAlign->isOK()) {
Chris@767 53 return;
Chris@767 54 }
Chris@770 55 ready = (reference->isReady() && toAlign->isReady());
Chris@767 56 }
Chris@767 57 if (!ready) {
Chris@767 58 SVDEBUG << "LinearAligner: Waiting for models..." << endl;
Chris@770 59 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents |
Chris@770 60 QEventLoop::ExcludeSocketNotifiers,
Chris@770 61 500);
Chris@767 62 }
Chris@767 63 }
Chris@767 64
Chris@767 65 auto reference = ModelById::get(m_reference);
Chris@767 66 auto toAlign = ModelById::get(m_toAlign);
Chris@767 67
Chris@767 68 if (!reference || !reference->isOK() ||
Chris@767 69 !toAlign || !toAlign->isOK()) {
Chris@767 70 return;
Chris@767 71 }
Chris@770 72
Chris@770 73 sv_frame_t s0, e0, s1, e1;
Chris@770 74 s0 = reference->getStartFrame();
Chris@770 75 e0 = reference->getEndFrame();
Chris@770 76 s1 = toAlign->getStartFrame();
Chris@770 77 e1 = toAlign->getEndFrame();
Chris@770 78
Chris@770 79 if (m_trimmed) {
Chris@770 80 getTrimmedExtents(m_reference, s0, e0);
Chris@770 81 getTrimmedExtents(m_toAlign, s1, e1);
Chris@770 82 SVCERR << "Trimmed extents: reference: " << s0 << " to " << e0
Chris@770 83 << ", toAlign: " << s1 << " to " << e1 << endl;
Chris@770 84 }
Chris@770 85
Chris@767 86 sv_frame_t d0 = e0 - s0, d1 = e1 - s1;
Chris@767 87
Chris@767 88 if (d1 == 0) {
Chris@767 89 return;
Chris@767 90 }
Chris@767 91
Chris@767 92 double ratio = double(d0) / double(d1);
Chris@770 93 int resolution = 1024;
Chris@767 94
Chris@767 95 Path path(reference->getSampleRate(), resolution);
Chris@767 96
Chris@767 97 for (sv_frame_t f = s1; f < e1; f += resolution) {
Chris@770 98 sv_frame_t target = s0 + sv_frame_t(double(f - s1) * ratio);
Chris@767 99 path.add(PathPoint(f, target));
Chris@767 100 }
Chris@767 101
Chris@767 102 auto alignment = std::make_shared<AlignmentModel>(m_reference,
Chris@767 103 m_toAlign,
Chris@767 104 ModelId());
Chris@767 105
Chris@767 106 auto alignmentModelId = ModelById::add(alignment);
Chris@767 107
Chris@767 108 alignment->setPath(path);
Chris@767 109 toAlign->setAlignment(alignmentModelId);
Chris@767 110 m_document->addNonDerivedModel(alignmentModelId);
Chris@770 111
Chris@770 112 emit complete(alignmentModelId);
Chris@767 113 }
Chris@767 114
Chris@770 115 bool
Chris@770 116 LinearAligner::getTrimmedExtents(ModelId modelId,
Chris@770 117 sv_frame_t &start,
Chris@770 118 sv_frame_t &end)
Chris@770 119 {
Chris@770 120 auto model = ModelById::getAs<DenseTimeValueModel>(modelId);
Chris@770 121 if (!model) return false;
Chris@770 122
Chris@770 123 sv_frame_t chunksize = 1024;
Chris@770 124 double threshold = 1e-2;
Chris@770 125
Chris@770 126 auto rms = [](const floatvec_t &samples) {
Chris@770 127 double rms = 0.0;
Chris@770 128 for (auto s: samples) {
Chris@770 129 rms += s * s;
Chris@770 130 }
Chris@770 131 rms /= double(samples.size());
Chris@770 132 rms = sqrt(rms);
Chris@770 133 return rms;
Chris@770 134 };
Chris@770 135
Chris@770 136 while (start < end) {
Chris@770 137 floatvec_t samples = model->getData(-1, start, chunksize);
Chris@770 138 if (samples.empty()) {
Chris@770 139 return false; // no non-silent content found
Chris@770 140 }
Chris@770 141 if (rms(samples) > threshold) {
Chris@770 142 for (auto s: samples) {
Chris@770 143 if (fabsf(s) > threshold) {
Chris@770 144 break;
Chris@770 145 }
Chris@770 146 ++start;
Chris@770 147 }
Chris@770 148 break;
Chris@770 149 }
Chris@770 150 start += chunksize;
Chris@770 151 }
Chris@770 152
Chris@770 153 if (start >= end) {
Chris@770 154 return false;
Chris@770 155 }
Chris@770 156
Chris@770 157 while (end > start) {
Chris@770 158 sv_frame_t probe = end - chunksize;
Chris@770 159 if (probe < 0) probe = 0;
Chris@770 160 floatvec_t samples = model->getData(-1, probe, chunksize);
Chris@770 161 if (samples.empty()) {
Chris@770 162 break;
Chris@770 163 }
Chris@770 164 if (rms(samples) > threshold) {
Chris@770 165 break;
Chris@770 166 }
Chris@770 167 end = probe;
Chris@770 168 }
Chris@770 169
Chris@770 170 return (end > start);
Chris@770 171 }