Mercurial > hg > svapp
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 } |