comparison align/LinearAligner.cpp @ 770:486add472c3f pitch-align

Implement trimmed linear aligner
author Chris Cannam
date Thu, 28 May 2020 17:52:19 +0100
parents dd742e566e60
children 699b5b130ea2
comparison
equal deleted inserted replaced
769:a316cb6fed81 770:486add472c3f
18 18
19 #include "data/model/Path.h" 19 #include "data/model/Path.h"
20 #include "data/model/AlignmentModel.h" 20 #include "data/model/AlignmentModel.h"
21 21
22 #include "framework/Document.h" 22 #include "framework/Document.h"
23
24 #include "svcore/data/model/DenseTimeValueModel.h"
25
26 #include <QApplication>
23 27
24 LinearAligner::LinearAligner(Document *doc, 28 LinearAligner::LinearAligner(Document *doc,
25 ModelId reference, 29 ModelId reference,
26 ModelId toAlign, 30 ModelId toAlign,
27 bool trimmed) : 31 bool trimmed) :
46 auto toAlign = ModelById::get(m_toAlign); 50 auto toAlign = ModelById::get(m_toAlign);
47 if (!reference || !reference->isOK() || 51 if (!reference || !reference->isOK() ||
48 !toAlign || !toAlign->isOK()) { 52 !toAlign || !toAlign->isOK()) {
49 return; 53 return;
50 } 54 }
51 ready = reference->isReady() && toAlign->isReady(); 55 ready = (reference->isReady() && toAlign->isReady());
52 } 56 }
53 if (!ready) { 57 if (!ready) {
54 SVDEBUG << "LinearAligner: Waiting for models..." << endl; 58 SVDEBUG << "LinearAligner: Waiting for models..." << endl;
55 usleep(500000); 59 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents |
60 QEventLoop::ExcludeSocketNotifiers,
61 500);
56 } 62 }
57 } 63 }
58 64
59 auto reference = ModelById::get(m_reference); 65 auto reference = ModelById::get(m_reference);
60 auto toAlign = ModelById::get(m_toAlign); 66 auto toAlign = ModelById::get(m_toAlign);
61 67
62 if (!reference || !reference->isOK() || 68 if (!reference || !reference->isOK() ||
63 !toAlign || !toAlign->isOK()) { 69 !toAlign || !toAlign->isOK()) {
64 return; 70 return;
65 } 71 }
66 72
67 sv_frame_t s0 = reference->getStartFrame(), s1 = toAlign->getStartFrame(); 73 sv_frame_t s0, e0, s1, e1;
68 sv_frame_t e0 = reference->getEndFrame(), e1 = toAlign->getEndFrame(); 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
69 sv_frame_t d0 = e0 - s0, d1 = e1 - s1; 86 sv_frame_t d0 = e0 - s0, d1 = e1 - s1;
70 87
71 if (d1 == 0) { 88 if (d1 == 0) {
72 return; 89 return;
73 } 90 }
74 91
75 double ratio = double(d0) / double(d1); 92 double ratio = double(d0) / double(d1);
76 sv_frame_t resolution = 1024; 93 int resolution = 1024;
77 94
78 Path path(reference->getSampleRate(), resolution); 95 Path path(reference->getSampleRate(), resolution);
79 96
80 for (sv_frame_t f = s1; f < e1; f += resolution) { 97 for (sv_frame_t f = s1; f < e1; f += resolution) {
81 sv_frame_t target = sv_frame_t(double(f - s1) * ratio); 98 sv_frame_t target = s0 + sv_frame_t(double(f - s1) * ratio);
82 path.add(PathPoint(f, target)); 99 path.add(PathPoint(f, target));
83 } 100 }
84 101
85 auto alignment = std::make_shared<AlignmentModel>(m_reference, 102 auto alignment = std::make_shared<AlignmentModel>(m_reference,
86 m_toAlign, 103 m_toAlign,
89 auto alignmentModelId = ModelById::add(alignment); 106 auto alignmentModelId = ModelById::add(alignment);
90 107
91 alignment->setPath(path); 108 alignment->setPath(path);
92 toAlign->setAlignment(alignmentModelId); 109 toAlign->setAlignment(alignmentModelId);
93 m_document->addNonDerivedModel(alignmentModelId); 110 m_document->addNonDerivedModel(alignmentModelId);
111
112 emit complete(alignmentModelId);
94 } 113 }
95 114
96 //!!! + trimmed 115 bool
116 LinearAligner::getTrimmedExtents(ModelId modelId,
117 sv_frame_t &start,
118 sv_frame_t &end)
119 {
120 auto model = ModelById::getAs<DenseTimeValueModel>(modelId);
121 if (!model) return false;
122
123 sv_frame_t chunksize = 1024;
124 double threshold = 1e-2;
125
126 auto rms = [](const floatvec_t &samples) {
127 double rms = 0.0;
128 for (auto s: samples) {
129 rms += s * s;
130 }
131 rms /= double(samples.size());
132 rms = sqrt(rms);
133 return rms;
134 };
135
136 while (start < end) {
137 floatvec_t samples = model->getData(-1, start, chunksize);
138 if (samples.empty()) {
139 return false; // no non-silent content found
140 }
141 if (rms(samples) > threshold) {
142 for (auto s: samples) {
143 if (fabsf(s) > threshold) {
144 break;
145 }
146 ++start;
147 }
148 break;
149 }
150 start += chunksize;
151 }
152
153 if (start >= end) {
154 return false;
155 }
156
157 while (end > start) {
158 sv_frame_t probe = end - chunksize;
159 if (probe < 0) probe = 0;
160 floatvec_t samples = model->getData(-1, probe, chunksize);
161 if (samples.empty()) {
162 break;
163 }
164 if (rms(samples) > threshold) {
165 break;
166 }
167 end = probe;
168 }
169
170 return (end > start);
171 }