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 }
|