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