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 }