Chris@152
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@152
|
2
|
Chris@152
|
3 /*
|
Chris@152
|
4 Sonic Visualiser
|
Chris@152
|
5 An audio file viewer and annotation editor.
|
Chris@152
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@202
|
7 This file copyright 2006 Chris Cannam and QMUL.
|
Chris@152
|
8
|
Chris@152
|
9 This program is free software; you can redistribute it and/or
|
Chris@152
|
10 modify it under the terms of the GNU General Public License as
|
Chris@152
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@152
|
12 License, or (at your option) any later version. See the file
|
Chris@152
|
13 COPYING included with this distribution for more information.
|
Chris@152
|
14 */
|
Chris@152
|
15
|
Chris@152
|
16 #include "EditableDenseThreeDimensionalModel.h"
|
Chris@152
|
17
|
Chris@478
|
18 #include "base/LogRange.h"
|
Chris@478
|
19
|
Chris@152
|
20 #include <QTextStream>
|
Chris@387
|
21 #include <QStringList>
|
Chris@1777
|
22 #include <QMutexLocker>
|
Chris@387
|
23
|
Chris@181
|
24 #include <iostream>
|
Chris@181
|
25
|
Chris@256
|
26 #include <cmath>
|
Chris@534
|
27 #include <cassert>
|
Chris@256
|
28
|
Chris@1044
|
29 using std::vector;
|
Chris@1044
|
30
|
Chris@607
|
31 #include "system/System.h"
|
Chris@607
|
32
|
Chris@1040
|
33 EditableDenseThreeDimensionalModel::EditableDenseThreeDimensionalModel(sv_samplerate_t sampleRate,
|
Chris@929
|
34 int resolution,
|
Chris@929
|
35 int yBinCount,
|
Chris@152
|
36 bool notifyOnAdd) :
|
Chris@611
|
37 m_startFrame(0),
|
Chris@152
|
38 m_sampleRate(sampleRate),
|
Chris@152
|
39 m_resolution(resolution),
|
Chris@152
|
40 m_yBinCount(yBinCount),
|
Chris@152
|
41 m_minimum(0.0),
|
Chris@152
|
42 m_maximum(0.0),
|
Chris@256
|
43 m_haveExtents(false),
|
Chris@152
|
44 m_notifyOnAdd(notifyOnAdd),
|
Chris@152
|
45 m_sinceLastNotifyMin(-1),
|
Chris@152
|
46 m_sinceLastNotifyMax(-1),
|
Chris@152
|
47 m_completion(100)
|
Chris@152
|
48 {
|
Chris@152
|
49 }
|
Chris@152
|
50
|
Chris@152
|
51 bool
|
Chris@152
|
52 EditableDenseThreeDimensionalModel::isOK() const
|
Chris@152
|
53 {
|
Chris@152
|
54 return true;
|
Chris@152
|
55 }
|
Chris@152
|
56
|
Chris@1701
|
57 bool
|
Chris@1701
|
58 EditableDenseThreeDimensionalModel::isReady(int *completion) const
|
Chris@1701
|
59 {
|
Chris@1701
|
60 if (completion) *completion = getCompletion();
|
Chris@1701
|
61 return true;
|
Chris@1701
|
62 }
|
Chris@1701
|
63
|
Chris@1040
|
64 sv_samplerate_t
|
Chris@152
|
65 EditableDenseThreeDimensionalModel::getSampleRate() const
|
Chris@152
|
66 {
|
Chris@152
|
67 return m_sampleRate;
|
Chris@152
|
68 }
|
Chris@152
|
69
|
Chris@1038
|
70 sv_frame_t
|
Chris@152
|
71 EditableDenseThreeDimensionalModel::getStartFrame() const
|
Chris@152
|
72 {
|
Chris@611
|
73 return m_startFrame;
|
Chris@611
|
74 }
|
Chris@611
|
75
|
Chris@611
|
76 void
|
Chris@1038
|
77 EditableDenseThreeDimensionalModel::setStartFrame(sv_frame_t f)
|
Chris@611
|
78 {
|
Chris@611
|
79 m_startFrame = f;
|
Chris@152
|
80 }
|
Chris@152
|
81
|
Chris@1038
|
82 sv_frame_t
|
Chris@1725
|
83 EditableDenseThreeDimensionalModel::getTrueEndFrame() const
|
Chris@152
|
84 {
|
Chris@152
|
85 return m_resolution * m_data.size() + (m_resolution - 1);
|
Chris@152
|
86 }
|
Chris@152
|
87
|
Chris@929
|
88 int
|
Chris@152
|
89 EditableDenseThreeDimensionalModel::getResolution() const
|
Chris@152
|
90 {
|
Chris@152
|
91 return m_resolution;
|
Chris@152
|
92 }
|
Chris@152
|
93
|
Chris@152
|
94 void
|
Chris@929
|
95 EditableDenseThreeDimensionalModel::setResolution(int sz)
|
Chris@152
|
96 {
|
Chris@152
|
97 m_resolution = sz;
|
Chris@152
|
98 }
|
Chris@152
|
99
|
Chris@929
|
100 int
|
Chris@182
|
101 EditableDenseThreeDimensionalModel::getWidth() const
|
Chris@182
|
102 {
|
Chris@1154
|
103 return int(m_data.size());
|
Chris@182
|
104 }
|
Chris@182
|
105
|
Chris@929
|
106 int
|
Chris@182
|
107 EditableDenseThreeDimensionalModel::getHeight() const
|
Chris@152
|
108 {
|
Chris@152
|
109 return m_yBinCount;
|
Chris@152
|
110 }
|
Chris@152
|
111
|
Chris@152
|
112 void
|
Chris@929
|
113 EditableDenseThreeDimensionalModel::setHeight(int sz)
|
Chris@152
|
114 {
|
Chris@152
|
115 m_yBinCount = sz;
|
Chris@152
|
116 }
|
Chris@152
|
117
|
Chris@152
|
118 float
|
Chris@152
|
119 EditableDenseThreeDimensionalModel::getMinimumLevel() const
|
Chris@152
|
120 {
|
Chris@152
|
121 return m_minimum;
|
Chris@152
|
122 }
|
Chris@152
|
123
|
Chris@152
|
124 void
|
Chris@152
|
125 EditableDenseThreeDimensionalModel::setMinimumLevel(float level)
|
Chris@152
|
126 {
|
Chris@152
|
127 m_minimum = level;
|
Chris@152
|
128 }
|
Chris@152
|
129
|
Chris@152
|
130 float
|
Chris@152
|
131 EditableDenseThreeDimensionalModel::getMaximumLevel() const
|
Chris@152
|
132 {
|
Chris@152
|
133 return m_maximum;
|
Chris@152
|
134 }
|
Chris@152
|
135
|
Chris@152
|
136 void
|
Chris@152
|
137 EditableDenseThreeDimensionalModel::setMaximumLevel(float level)
|
Chris@152
|
138 {
|
Chris@152
|
139 m_maximum = level;
|
Chris@152
|
140 }
|
Chris@152
|
141
|
Chris@533
|
142 EditableDenseThreeDimensionalModel::Column
|
Chris@929
|
143 EditableDenseThreeDimensionalModel::getColumn(int index) const
|
Chris@152
|
144 {
|
Chris@1777
|
145 QMutexLocker locker(&m_mutex);
|
Chris@1777
|
146 if (!in_range_for(m_data, index)) {
|
Chris@1777
|
147 return {};
|
Chris@152
|
148 }
|
Chris@1777
|
149 Column c = m_data.at(index);
|
Chris@1777
|
150 if (int(c.size()) == m_yBinCount) {
|
Chris@1777
|
151 return c;
|
Chris@1777
|
152 } else {
|
Chris@1252
|
153 Column cc(c);
|
Chris@1252
|
154 cc.resize(m_yBinCount, 0.0);
|
Chris@1252
|
155 return cc;
|
Chris@1252
|
156 }
|
Chris@1252
|
157 }
|
Chris@1252
|
158
|
Chris@1777
|
159 float
|
Chris@1777
|
160 EditableDenseThreeDimensionalModel::getValueAt(int index, int n) const
|
Chris@534
|
161 {
|
Chris@1777
|
162 QMutexLocker locker(&m_mutex);
|
Chris@1777
|
163 if (!in_range_for(m_data, index)) {
|
Chris@1777
|
164 return m_minimum;
|
Chris@534
|
165 }
|
Chris@1777
|
166 const Column &c = m_data.at(index);
|
Chris@1777
|
167 if (!in_range_for(c, n)) {
|
Chris@1777
|
168 return m_minimum;
|
Chris@534
|
169 }
|
Chris@1777
|
170 return c.at(n);
|
Chris@152
|
171 }
|
Chris@152
|
172
|
Chris@152
|
173 void
|
Chris@929
|
174 EditableDenseThreeDimensionalModel::setColumn(int index,
|
Chris@182
|
175 const Column &values)
|
Chris@152
|
176 {
|
Chris@152
|
177 bool allChange = false;
|
Chris@1110
|
178 sv_frame_t windowStart = index;
|
Chris@182
|
179 windowStart *= m_resolution;
|
Chris@182
|
180
|
Chris@1777
|
181 {
|
Chris@1777
|
182 QMutexLocker locker(&m_mutex);
|
Chris@1777
|
183
|
Chris@1777
|
184 while (index >= int(m_data.size())) {
|
Chris@1777
|
185 m_data.push_back(Column());
|
Chris@1777
|
186 }
|
Chris@1777
|
187
|
Chris@1777
|
188 for (int i = 0; in_range_for(values, i); ++i) {
|
Chris@1777
|
189 float value = values[i];
|
Chris@1777
|
190 if (ISNAN(value) || ISINF(value)) {
|
Chris@1777
|
191 continue;
|
Chris@1777
|
192 }
|
Chris@1777
|
193 if (!m_haveExtents || value < m_minimum) {
|
Chris@1777
|
194 m_minimum = value;
|
Chris@1777
|
195 allChange = true;
|
Chris@1777
|
196 }
|
Chris@1777
|
197 if (!m_haveExtents || value > m_maximum) {
|
Chris@1777
|
198 m_maximum = value;
|
Chris@1777
|
199 allChange = true;
|
Chris@1777
|
200 }
|
Chris@1777
|
201 m_haveExtents = true;
|
Chris@1777
|
202 }
|
Chris@1777
|
203
|
Chris@1777
|
204 m_data[index] = values;
|
Chris@1777
|
205
|
Chris@1777
|
206 if (allChange) {
|
Chris@1777
|
207 m_sinceLastNotifyMin = -1;
|
Chris@1777
|
208 m_sinceLastNotifyMax = -1;
|
Chris@1777
|
209 } else {
|
Chris@1777
|
210 if (m_sinceLastNotifyMin == -1 ||
|
Chris@1777
|
211 windowStart < m_sinceLastNotifyMin) {
|
Chris@1777
|
212 m_sinceLastNotifyMin = windowStart;
|
Chris@1777
|
213 }
|
Chris@1777
|
214 if (m_sinceLastNotifyMax == -1 ||
|
Chris@1777
|
215 windowStart > m_sinceLastNotifyMax) {
|
Chris@1777
|
216 m_sinceLastNotifyMax = windowStart;
|
Chris@1777
|
217 }
|
Chris@1777
|
218 }
|
Chris@1777
|
219 }
|
Chris@1777
|
220
|
Chris@152
|
221 if (m_notifyOnAdd) {
|
Chris@1429
|
222 if (allChange) {
|
Chris@1752
|
223 emit modelChanged(getId());
|
Chris@1429
|
224 } else {
|
Chris@1752
|
225 emit modelChangedWithin(getId(),
|
Chris@1752
|
226 windowStart, windowStart + m_resolution);
|
Chris@1429
|
227 }
|
Chris@152
|
228 } else {
|
Chris@1429
|
229 if (allChange) {
|
Chris@1752
|
230 emit modelChanged(getId());
|
Chris@1429
|
231 }
|
Chris@152
|
232 }
|
Chris@152
|
233 }
|
Chris@152
|
234
|
Chris@152
|
235 QString
|
Chris@929
|
236 EditableDenseThreeDimensionalModel::getBinName(int n) const
|
Chris@152
|
237 {
|
Chris@939
|
238 if (n >= 0 && (int)m_binNames.size() > n) return m_binNames[n];
|
Chris@152
|
239 else return "";
|
Chris@152
|
240 }
|
Chris@152
|
241
|
Chris@152
|
242 void
|
Chris@929
|
243 EditableDenseThreeDimensionalModel::setBinName(int n, QString name)
|
Chris@152
|
244 {
|
Chris@929
|
245 while ((int)m_binNames.size() <= n) m_binNames.push_back("");
|
Chris@152
|
246 m_binNames[n] = name;
|
Chris@1752
|
247 emit modelChanged(getId());
|
Chris@152
|
248 }
|
Chris@152
|
249
|
Chris@152
|
250 void
|
Chris@152
|
251 EditableDenseThreeDimensionalModel::setBinNames(std::vector<QString> names)
|
Chris@152
|
252 {
|
Chris@152
|
253 m_binNames = names;
|
Chris@1752
|
254 emit modelChanged(getId());
|
Chris@152
|
255 }
|
Chris@152
|
256
|
Chris@478
|
257 bool
|
Chris@886
|
258 EditableDenseThreeDimensionalModel::hasBinValues() const
|
Chris@886
|
259 {
|
Chris@886
|
260 return !m_binValues.empty();
|
Chris@886
|
261 }
|
Chris@886
|
262
|
Chris@886
|
263 float
|
Chris@929
|
264 EditableDenseThreeDimensionalModel::getBinValue(int n) const
|
Chris@886
|
265 {
|
Chris@929
|
266 if (n < (int)m_binValues.size()) return m_binValues[n];
|
Chris@886
|
267 else return 0.f;
|
Chris@886
|
268 }
|
Chris@886
|
269
|
Chris@886
|
270 void
|
Chris@886
|
271 EditableDenseThreeDimensionalModel::setBinValues(std::vector<float> values)
|
Chris@886
|
272 {
|
Chris@886
|
273 m_binValues = values;
|
Chris@886
|
274 }
|
Chris@886
|
275
|
Chris@886
|
276 QString
|
Chris@886
|
277 EditableDenseThreeDimensionalModel::getBinValueUnit() const
|
Chris@886
|
278 {
|
Chris@886
|
279 return m_binValueUnit;
|
Chris@886
|
280 }
|
Chris@886
|
281
|
Chris@886
|
282 void
|
Chris@886
|
283 EditableDenseThreeDimensionalModel::setBinValueUnit(QString unit)
|
Chris@886
|
284 {
|
Chris@886
|
285 m_binValueUnit = unit;
|
Chris@886
|
286 }
|
Chris@886
|
287
|
Chris@886
|
288 bool
|
Chris@478
|
289 EditableDenseThreeDimensionalModel::shouldUseLogValueScale() const
|
Chris@478
|
290 {
|
Chris@1777
|
291 QMutexLocker locker(&m_mutex);
|
Chris@534
|
292
|
Chris@1044
|
293 vector<double> sample;
|
Chris@1044
|
294 vector<int> n;
|
Chris@478
|
295
|
Chris@478
|
296 for (int i = 0; i < 10; ++i) {
|
Chris@929
|
297 int index = i * 10;
|
Chris@1154
|
298 if (in_range_for(m_data, index)) {
|
Chris@533
|
299 const Column &c = m_data.at(index);
|
Chris@1154
|
300 while (c.size() > sample.size()) {
|
Chris@1044
|
301 sample.push_back(0.0);
|
Chris@478
|
302 n.push_back(0);
|
Chris@478
|
303 }
|
Chris@1154
|
304 for (int j = 0; in_range_for(c, j); ++j) {
|
Chris@533
|
305 sample[j] += c.at(j);
|
Chris@478
|
306 ++n[j];
|
Chris@478
|
307 }
|
Chris@478
|
308 }
|
Chris@478
|
309 }
|
Chris@478
|
310
|
Chris@478
|
311 if (sample.empty()) return false;
|
Chris@1044
|
312 for (decltype(sample)::size_type j = 0; j < sample.size(); ++j) {
|
Chris@1044
|
313 if (n[j]) sample[j] /= n[j];
|
Chris@478
|
314 }
|
Chris@478
|
315
|
Chris@1392
|
316 return LogRange::shouldUseLogScale(sample);
|
Chris@478
|
317 }
|
Chris@478
|
318
|
Chris@152
|
319 void
|
Chris@333
|
320 EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update)
|
Chris@152
|
321 {
|
Chris@152
|
322 if (m_completion != completion) {
|
Chris@1429
|
323 m_completion = completion;
|
Chris@152
|
324
|
Chris@1429
|
325 if (completion == 100) {
|
Chris@152
|
326
|
Chris@1429
|
327 m_notifyOnAdd = true; // henceforth
|
Chris@1752
|
328 emit modelChanged(getId());
|
Chris@152
|
329
|
Chris@1429
|
330 } else if (!m_notifyOnAdd) {
|
Chris@152
|
331
|
Chris@1429
|
332 if (update &&
|
Chris@333
|
333 m_sinceLastNotifyMin >= 0 &&
|
Chris@1429
|
334 m_sinceLastNotifyMax >= 0) {
|
Chris@1752
|
335 emit modelChangedWithin(getId(),
|
Chris@1752
|
336 m_sinceLastNotifyMin,
|
Chris@931
|
337 m_sinceLastNotifyMax + m_resolution);
|
Chris@1429
|
338 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
|
Chris@1429
|
339 } else {
|
Chris@1752
|
340 emit completionChanged(getId());
|
Chris@1429
|
341 }
|
Chris@1429
|
342 } else {
|
Chris@1752
|
343 emit completionChanged(getId());
|
Chris@1429
|
344 }
|
Chris@152
|
345 }
|
Chris@152
|
346 }
|
Chris@152
|
347
|
Chris@1701
|
348 int
|
Chris@1701
|
349 EditableDenseThreeDimensionalModel::getCompletion() const
|
Chris@1701
|
350 {
|
Chris@1701
|
351 return m_completion;
|
Chris@1701
|
352 }
|
Chris@1701
|
353
|
Chris@318
|
354 QString
|
Chris@1815
|
355 EditableDenseThreeDimensionalModel::getDelimitedDataHeaderLine(QString delimiter,
|
Chris@1815
|
356 DataExportOptions) const
|
Chris@1815
|
357 {
|
Chris@1815
|
358 QStringList list;
|
Chris@1815
|
359 for (int i = 0; i < m_yBinCount; ++i) {
|
Chris@1815
|
360 list << QString("Bin%1").arg(i+1);
|
Chris@1815
|
361 }
|
Chris@1815
|
362 return list.join(delimiter);
|
Chris@1815
|
363 }
|
Chris@1815
|
364
|
Chris@1815
|
365 QString
|
Chris@1679
|
366 EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter,
|
Chris@1679
|
367 DataExportOptions,
|
Chris@1679
|
368 sv_frame_t startFrame,
|
Chris@1679
|
369 sv_frame_t duration) const
|
Chris@838
|
370 {
|
Chris@1777
|
371 QMutexLocker locker(&m_mutex);
|
Chris@838
|
372 QString s;
|
Chris@1154
|
373 for (int i = 0; in_range_for(m_data, i); ++i) {
|
Chris@1038
|
374 sv_frame_t fr = m_startFrame + i * m_resolution;
|
Chris@1679
|
375 if (fr >= startFrame && fr < startFrame + duration) {
|
Chris@838
|
376 QStringList list;
|
Chris@1154
|
377 for (int j = 0; in_range_for(m_data.at(i), j); ++j) {
|
Chris@838
|
378 list << QString("%1").arg(m_data.at(i).at(j));
|
Chris@838
|
379 }
|
Chris@838
|
380 s += list.join(delimiter) + "\n";
|
Chris@838
|
381 }
|
Chris@838
|
382 }
|
Chris@838
|
383 return s;
|
Chris@838
|
384 }
|
Chris@838
|
385
|
Chris@152
|
386 void
|
Chris@152
|
387 EditableDenseThreeDimensionalModel::toXml(QTextStream &out,
|
Chris@314
|
388 QString indent,
|
Chris@314
|
389 QString extraAttributes) const
|
Chris@152
|
390 {
|
Chris@1777
|
391 QMutexLocker locker(&m_mutex);
|
Chris@534
|
392
|
Chris@1677
|
393 // For historical reasons we read and write "resolution" as "windowSize".
|
Chris@1677
|
394
|
Chris@1677
|
395 // Our dataset doesn't have its own export ID, we just use
|
Chris@1677
|
396 // ours. Actually any model could do that, since datasets aren't
|
Chris@1677
|
397 // in the same id-space as models when re-read
|
Chris@152
|
398
|
Chris@690
|
399 SVDEBUG << "EditableDenseThreeDimensionalModel::toXml" << endl;
|
Chris@318
|
400
|
Chris@314
|
401 Model::toXml
|
Chris@1429
|
402 (out, indent,
|
Chris@611
|
403 QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7")
|
Chris@1429
|
404 .arg(m_resolution)
|
Chris@1429
|
405 .arg(m_yBinCount)
|
Chris@1429
|
406 .arg(m_minimum)
|
Chris@1429
|
407 .arg(m_maximum)
|
Chris@1677
|
408 .arg(getExportId())
|
Chris@611
|
409 .arg(m_startFrame)
|
Chris@1429
|
410 .arg(extraAttributes));
|
Chris@152
|
411
|
Chris@152
|
412 out << indent;
|
Chris@152
|
413 out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
|
Chris@1677
|
414 .arg(getExportId());
|
Chris@152
|
415
|
Chris@1777
|
416 for (int i = 0; in_range_for(m_binNames, i); ++i) {
|
Chris@1429
|
417 if (m_binNames[i] != "") {
|
Chris@1429
|
418 out << indent + " ";
|
Chris@1429
|
419 out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
|
Chris@1429
|
420 .arg(i).arg(m_binNames[i]);
|
Chris@1429
|
421 }
|
Chris@152
|
422 }
|
Chris@152
|
423
|
Chris@1777
|
424 for (int i = 0; in_range_for(m_data, i); ++i) {
|
Chris@1777
|
425 Column c = getColumn(i);
|
Chris@1429
|
426 out << indent + " ";
|
Chris@1429
|
427 out << QString("<row n=\"%1\">").arg(i);
|
Chris@1777
|
428 for (int j = 0; in_range_for(c, j); ++j) {
|
Chris@1429
|
429 if (j > 0) out << " ";
|
Chris@1777
|
430 out << c.at(j);
|
Chris@1429
|
431 }
|
Chris@1429
|
432 out << QString("</row>\n");
|
Chris@318
|
433 out.flush();
|
Chris@152
|
434 }
|
Chris@152
|
435
|
Chris@152
|
436 out << indent + "</dataset>\n";
|
Chris@152
|
437 }
|
Chris@152
|
438
|
Chris@152
|
439
|