annotate align/LinearAligner.cpp @ 773:699b5b130ea2 pitch-align

Fixes to aligner destruction sequence when re-aligning during alignment; hide debug output behind a flag
author Chris Cannam
date Mon, 01 Jun 2020 17:13:09 +0100
parents 486add472c3f
children
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@773 109 alignment->setCompletion(100);
Chris@767 110 toAlign->setAlignment(alignmentModelId);
Chris@767 111 m_document->addNonDerivedModel(alignmentModelId);
Chris@770 112
Chris@770 113 emit complete(alignmentModelId);
Chris@767 114 }
Chris@767 115
Chris@770 116 bool
Chris@770 117 LinearAligner::getTrimmedExtents(ModelId modelId,
Chris@770 118 sv_frame_t &start,
Chris@770 119 sv_frame_t &end)
Chris@770 120 {
Chris@770 121 auto model = ModelById::getAs<DenseTimeValueModel>(modelId);
Chris@770 122 if (!model) return false;
Chris@770 123
Chris@770 124 sv_frame_t chunksize = 1024;
Chris@770 125 double threshold = 1e-2;
Chris@770 126
Chris@770 127 auto rms = [](const floatvec_t &samples) {
Chris@770 128 double rms = 0.0;
Chris@770 129 for (auto s: samples) {
Chris@770 130 rms += s * s;
Chris@770 131 }
Chris@770 132 rms /= double(samples.size());
Chris@770 133 rms = sqrt(rms);
Chris@770 134 return rms;
Chris@770 135 };
Chris@770 136
Chris@770 137 while (start < end) {
Chris@770 138 floatvec_t samples = model->getData(-1, start, chunksize);
Chris@770 139 if (samples.empty()) {
Chris@770 140 return false; // no non-silent content found
Chris@770 141 }
Chris@770 142 if (rms(samples) > threshold) {
Chris@770 143 for (auto s: samples) {
Chris@770 144 if (fabsf(s) > threshold) {
Chris@770 145 break;
Chris@770 146 }
Chris@770 147 ++start;
Chris@770 148 }
Chris@770 149 break;
Chris@770 150 }
Chris@770 151 start += chunksize;
Chris@770 152 }
Chris@770 153
Chris@770 154 if (start >= end) {
Chris@770 155 return false;
Chris@770 156 }
Chris@770 157
Chris@770 158 while (end > start) {
Chris@770 159 sv_frame_t probe = end - chunksize;
Chris@770 160 if (probe < 0) probe = 0;
Chris@770 161 floatvec_t samples = model->getData(-1, probe, chunksize);
Chris@770 162 if (samples.empty()) {
Chris@770 163 break;
Chris@770 164 }
Chris@770 165 if (rms(samples) > threshold) {
Chris@770 166 break;
Chris@770 167 }
Chris@770 168 end = probe;
Chris@770 169 }
Chris@770 170
Chris@770 171 return (end > start);
Chris@770 172 }