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@387
|
22
|
Chris@181
|
23 #include <iostream>
|
Chris@181
|
24
|
Chris@256
|
25 #include <cmath>
|
Chris@256
|
26
|
Chris@152
|
27 EditableDenseThreeDimensionalModel::EditableDenseThreeDimensionalModel(size_t sampleRate,
|
Chris@152
|
28 size_t resolution,
|
Chris@152
|
29 size_t yBinCount,
|
Chris@152
|
30 bool notifyOnAdd) :
|
Chris@152
|
31 m_sampleRate(sampleRate),
|
Chris@152
|
32 m_resolution(resolution),
|
Chris@152
|
33 m_yBinCount(yBinCount),
|
Chris@152
|
34 m_minimum(0.0),
|
Chris@152
|
35 m_maximum(0.0),
|
Chris@256
|
36 m_haveExtents(false),
|
Chris@152
|
37 m_notifyOnAdd(notifyOnAdd),
|
Chris@152
|
38 m_sinceLastNotifyMin(-1),
|
Chris@152
|
39 m_sinceLastNotifyMax(-1),
|
Chris@152
|
40 m_completion(100)
|
Chris@152
|
41 {
|
Chris@152
|
42 }
|
Chris@152
|
43
|
Chris@152
|
44 bool
|
Chris@152
|
45 EditableDenseThreeDimensionalModel::isOK() const
|
Chris@152
|
46 {
|
Chris@152
|
47 return true;
|
Chris@152
|
48 }
|
Chris@152
|
49
|
Chris@152
|
50 size_t
|
Chris@152
|
51 EditableDenseThreeDimensionalModel::getSampleRate() const
|
Chris@152
|
52 {
|
Chris@152
|
53 return m_sampleRate;
|
Chris@152
|
54 }
|
Chris@152
|
55
|
Chris@152
|
56 size_t
|
Chris@152
|
57 EditableDenseThreeDimensionalModel::getStartFrame() const
|
Chris@152
|
58 {
|
Chris@152
|
59 return 0;
|
Chris@152
|
60 }
|
Chris@152
|
61
|
Chris@152
|
62 size_t
|
Chris@152
|
63 EditableDenseThreeDimensionalModel::getEndFrame() const
|
Chris@152
|
64 {
|
Chris@152
|
65 return m_resolution * m_data.size() + (m_resolution - 1);
|
Chris@152
|
66 }
|
Chris@152
|
67
|
Chris@152
|
68 Model *
|
Chris@152
|
69 EditableDenseThreeDimensionalModel::clone() const
|
Chris@152
|
70 {
|
Chris@152
|
71 EditableDenseThreeDimensionalModel *model =
|
Chris@152
|
72 new EditableDenseThreeDimensionalModel
|
Chris@152
|
73 (m_sampleRate, m_resolution, m_yBinCount);
|
Chris@152
|
74
|
Chris@152
|
75 model->m_minimum = m_minimum;
|
Chris@152
|
76 model->m_maximum = m_maximum;
|
Chris@256
|
77 model->m_haveExtents = m_haveExtents;
|
Chris@152
|
78
|
Chris@152
|
79 for (size_t i = 0; i < m_data.size(); ++i) {
|
Chris@533
|
80 model->setColumn(i, m_data.at(i));
|
Chris@152
|
81 }
|
Chris@152
|
82
|
Chris@152
|
83 return model;
|
Chris@152
|
84 }
|
Chris@152
|
85
|
Chris@152
|
86 size_t
|
Chris@152
|
87 EditableDenseThreeDimensionalModel::getResolution() const
|
Chris@152
|
88 {
|
Chris@152
|
89 return m_resolution;
|
Chris@152
|
90 }
|
Chris@152
|
91
|
Chris@152
|
92 void
|
Chris@152
|
93 EditableDenseThreeDimensionalModel::setResolution(size_t sz)
|
Chris@152
|
94 {
|
Chris@152
|
95 m_resolution = sz;
|
Chris@152
|
96 }
|
Chris@152
|
97
|
Chris@152
|
98 size_t
|
Chris@182
|
99 EditableDenseThreeDimensionalModel::getWidth() const
|
Chris@182
|
100 {
|
Chris@182
|
101 return m_data.size();
|
Chris@182
|
102 }
|
Chris@182
|
103
|
Chris@182
|
104 size_t
|
Chris@182
|
105 EditableDenseThreeDimensionalModel::getHeight() const
|
Chris@152
|
106 {
|
Chris@152
|
107 return m_yBinCount;
|
Chris@152
|
108 }
|
Chris@152
|
109
|
Chris@152
|
110 void
|
Chris@182
|
111 EditableDenseThreeDimensionalModel::setHeight(size_t sz)
|
Chris@152
|
112 {
|
Chris@152
|
113 m_yBinCount = sz;
|
Chris@152
|
114 }
|
Chris@152
|
115
|
Chris@152
|
116 float
|
Chris@152
|
117 EditableDenseThreeDimensionalModel::getMinimumLevel() const
|
Chris@152
|
118 {
|
Chris@152
|
119 return m_minimum;
|
Chris@152
|
120 }
|
Chris@152
|
121
|
Chris@152
|
122 void
|
Chris@152
|
123 EditableDenseThreeDimensionalModel::setMinimumLevel(float level)
|
Chris@152
|
124 {
|
Chris@152
|
125 m_minimum = level;
|
Chris@152
|
126 }
|
Chris@152
|
127
|
Chris@152
|
128 float
|
Chris@152
|
129 EditableDenseThreeDimensionalModel::getMaximumLevel() const
|
Chris@152
|
130 {
|
Chris@152
|
131 return m_maximum;
|
Chris@152
|
132 }
|
Chris@152
|
133
|
Chris@152
|
134 void
|
Chris@152
|
135 EditableDenseThreeDimensionalModel::setMaximumLevel(float level)
|
Chris@152
|
136 {
|
Chris@152
|
137 m_maximum = level;
|
Chris@152
|
138 }
|
Chris@152
|
139
|
Chris@533
|
140 EditableDenseThreeDimensionalModel::Column
|
Chris@533
|
141 EditableDenseThreeDimensionalModel::getColumn(size_t index) const
|
Chris@152
|
142 {
|
Chris@152
|
143 QMutexLocker locker(&m_mutex);
|
Chris@152
|
144
|
Chris@533
|
145 Column result;
|
Chris@533
|
146
|
Chris@182
|
147 if (index < m_data.size()) {
|
Chris@533
|
148 result = m_data.at(index);
|
Chris@152
|
149 } else {
|
Chris@152
|
150 result.clear();
|
Chris@152
|
151 }
|
Chris@152
|
152
|
Chris@152
|
153 while (result.size() < m_yBinCount) result.push_back(m_minimum);
|
Chris@533
|
154 return result;
|
Chris@152
|
155 }
|
Chris@152
|
156
|
Chris@152
|
157 float
|
Chris@182
|
158 EditableDenseThreeDimensionalModel::getValueAt(size_t index, size_t n) const
|
Chris@152
|
159 {
|
Chris@152
|
160 QMutexLocker locker(&m_mutex);
|
Chris@152
|
161
|
Chris@182
|
162 if (index < m_data.size()) {
|
Chris@533
|
163 const Column &s = m_data.at(index);
|
Chris@256
|
164 // std::cerr << "index " << index << ", n " << n << ", res " << m_resolution << ", size " << s.size()
|
Chris@256
|
165 // << std::endl;
|
Chris@533
|
166 if (n < s.size()) return s.at(n);
|
Chris@152
|
167 }
|
Chris@152
|
168
|
Chris@152
|
169 return m_minimum;
|
Chris@152
|
170 }
|
Chris@152
|
171
|
Chris@152
|
172 void
|
Chris@182
|
173 EditableDenseThreeDimensionalModel::setColumn(size_t index,
|
Chris@182
|
174 const Column &values)
|
Chris@152
|
175 {
|
Chris@152
|
176 QMutexLocker locker(&m_mutex);
|
Chris@152
|
177
|
Chris@182
|
178 while (index >= m_data.size()) {
|
Chris@182
|
179 m_data.push_back(Column());
|
Chris@152
|
180 }
|
Chris@152
|
181
|
Chris@152
|
182 bool allChange = false;
|
Chris@152
|
183
|
Chris@439
|
184 if (values.size() > m_yBinCount) m_yBinCount = values.size();
|
Chris@439
|
185
|
Chris@152
|
186 for (size_t i = 0; i < values.size(); ++i) {
|
Chris@256
|
187 float value = values[i];
|
Chris@257
|
188 if (std::isnan(value) || std::isinf(value)) {
|
Chris@256
|
189 continue;
|
Chris@256
|
190 }
|
Chris@256
|
191 if (!m_haveExtents || value < m_minimum) {
|
Chris@256
|
192 m_minimum = value;
|
Chris@152
|
193 allChange = true;
|
Chris@152
|
194 }
|
Chris@256
|
195 if (!m_haveExtents || value > m_maximum) {
|
Chris@256
|
196 m_maximum = value;
|
Chris@152
|
197 allChange = true;
|
Chris@152
|
198 }
|
Chris@256
|
199 m_haveExtents = true;
|
Chris@152
|
200 }
|
Chris@152
|
201
|
Chris@152
|
202 m_data[index] = values;
|
Chris@152
|
203
|
Chris@182
|
204 long windowStart = index;
|
Chris@182
|
205 windowStart *= m_resolution;
|
Chris@182
|
206
|
Chris@152
|
207 if (m_notifyOnAdd) {
|
Chris@152
|
208 if (allChange) {
|
Chris@152
|
209 emit modelChanged();
|
Chris@152
|
210 } else {
|
Chris@152
|
211 emit modelChanged(windowStart, windowStart + m_resolution);
|
Chris@152
|
212 }
|
Chris@152
|
213 } else {
|
Chris@152
|
214 if (allChange) {
|
Chris@152
|
215 m_sinceLastNotifyMin = -1;
|
Chris@152
|
216 m_sinceLastNotifyMax = -1;
|
Chris@152
|
217 emit modelChanged();
|
Chris@152
|
218 } else {
|
Chris@152
|
219 if (m_sinceLastNotifyMin == -1 ||
|
Chris@152
|
220 windowStart < m_sinceLastNotifyMin) {
|
Chris@152
|
221 m_sinceLastNotifyMin = windowStart;
|
Chris@152
|
222 }
|
Chris@152
|
223 if (m_sinceLastNotifyMax == -1 ||
|
Chris@152
|
224 windowStart > m_sinceLastNotifyMax) {
|
Chris@152
|
225 m_sinceLastNotifyMax = windowStart;
|
Chris@152
|
226 }
|
Chris@152
|
227 }
|
Chris@152
|
228 }
|
Chris@152
|
229 }
|
Chris@152
|
230
|
Chris@152
|
231 QString
|
Chris@152
|
232 EditableDenseThreeDimensionalModel::getBinName(size_t n) const
|
Chris@152
|
233 {
|
Chris@152
|
234 if (m_binNames.size() > n) return m_binNames[n];
|
Chris@152
|
235 else return "";
|
Chris@152
|
236 }
|
Chris@152
|
237
|
Chris@152
|
238 void
|
Chris@152
|
239 EditableDenseThreeDimensionalModel::setBinName(size_t n, QString name)
|
Chris@152
|
240 {
|
Chris@152
|
241 while (m_binNames.size() <= n) m_binNames.push_back("");
|
Chris@152
|
242 m_binNames[n] = name;
|
Chris@152
|
243 emit modelChanged();
|
Chris@152
|
244 }
|
Chris@152
|
245
|
Chris@152
|
246 void
|
Chris@152
|
247 EditableDenseThreeDimensionalModel::setBinNames(std::vector<QString> names)
|
Chris@152
|
248 {
|
Chris@152
|
249 m_binNames = names;
|
Chris@152
|
250 emit modelChanged();
|
Chris@152
|
251 }
|
Chris@152
|
252
|
Chris@478
|
253 bool
|
Chris@478
|
254 EditableDenseThreeDimensionalModel::shouldUseLogValueScale() const
|
Chris@478
|
255 {
|
Chris@533
|
256 QVector<float> sample;
|
Chris@533
|
257 QVector<int> n;
|
Chris@478
|
258
|
Chris@478
|
259 for (int i = 0; i < 10; ++i) {
|
Chris@478
|
260 size_t index = i * 10;
|
Chris@478
|
261 if (index < m_data.size()) {
|
Chris@533
|
262 const Column &c = m_data.at(index);
|
Chris@478
|
263 while (c.size() > sample.size()) {
|
Chris@478
|
264 sample.push_back(0.f);
|
Chris@478
|
265 n.push_back(0);
|
Chris@478
|
266 }
|
Chris@478
|
267 for (int j = 0; j < c.size(); ++j) {
|
Chris@533
|
268 sample[j] += c.at(j);
|
Chris@478
|
269 ++n[j];
|
Chris@478
|
270 }
|
Chris@478
|
271 }
|
Chris@478
|
272 }
|
Chris@478
|
273
|
Chris@478
|
274 if (sample.empty()) return false;
|
Chris@478
|
275 for (int j = 0; j < sample.size(); ++j) {
|
Chris@478
|
276 if (n[j]) sample[j] /= n[j];
|
Chris@478
|
277 }
|
Chris@478
|
278
|
Chris@533
|
279 return LogRange::useLogScale(sample.toStdVector());
|
Chris@478
|
280 }
|
Chris@478
|
281
|
Chris@152
|
282 void
|
Chris@333
|
283 EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update)
|
Chris@152
|
284 {
|
Chris@152
|
285 if (m_completion != completion) {
|
Chris@152
|
286 m_completion = completion;
|
Chris@152
|
287
|
Chris@152
|
288 if (completion == 100) {
|
Chris@152
|
289
|
Chris@152
|
290 m_notifyOnAdd = true; // henceforth
|
Chris@152
|
291 emit modelChanged();
|
Chris@152
|
292
|
Chris@152
|
293 } else if (!m_notifyOnAdd) {
|
Chris@152
|
294
|
Chris@333
|
295 if (update &&
|
Chris@333
|
296 m_sinceLastNotifyMin >= 0 &&
|
Chris@152
|
297 m_sinceLastNotifyMax >= 0) {
|
Chris@152
|
298 emit modelChanged(m_sinceLastNotifyMin,
|
Chris@152
|
299 m_sinceLastNotifyMax + m_resolution);
|
Chris@152
|
300 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
|
Chris@152
|
301 } else {
|
Chris@152
|
302 emit completionChanged();
|
Chris@152
|
303 }
|
Chris@152
|
304 } else {
|
Chris@152
|
305 emit completionChanged();
|
Chris@152
|
306 }
|
Chris@152
|
307 }
|
Chris@152
|
308 }
|
Chris@152
|
309
|
Chris@318
|
310 QString
|
Chris@318
|
311 EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter) const
|
Chris@318
|
312 {
|
Chris@318
|
313 QString s;
|
Chris@318
|
314 for (size_t i = 0; i < m_data.size(); ++i) {
|
Chris@318
|
315 QStringList list;
|
Chris@533
|
316 for (size_t j = 0; j < m_data.at(i).size(); ++j) {
|
Chris@533
|
317 list << QString("%1").arg(m_data.at(i).at(j));
|
Chris@318
|
318 }
|
Chris@318
|
319 s += list.join(delimiter) + "\n";
|
Chris@318
|
320 }
|
Chris@318
|
321 return s;
|
Chris@318
|
322 }
|
Chris@318
|
323
|
Chris@152
|
324 void
|
Chris@152
|
325 EditableDenseThreeDimensionalModel::toXml(QTextStream &out,
|
Chris@314
|
326 QString indent,
|
Chris@314
|
327 QString extraAttributes) const
|
Chris@152
|
328 {
|
Chris@152
|
329 // For historical reasons we read and write "resolution" as "windowSize"
|
Chris@152
|
330
|
Chris@318
|
331 std::cerr << "EditableDenseThreeDimensionalModel::toXml" << std::endl;
|
Chris@318
|
332
|
Chris@314
|
333 Model::toXml
|
Chris@314
|
334 (out, indent,
|
Chris@314
|
335 QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" %6")
|
Chris@152
|
336 .arg(m_resolution)
|
Chris@152
|
337 .arg(m_yBinCount)
|
Chris@152
|
338 .arg(m_minimum)
|
Chris@152
|
339 .arg(m_maximum)
|
Chris@152
|
340 .arg(getObjectExportId(&m_data))
|
Chris@152
|
341 .arg(extraAttributes));
|
Chris@152
|
342
|
Chris@152
|
343 out << indent;
|
Chris@152
|
344 out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
|
Chris@152
|
345 .arg(getObjectExportId(&m_data));
|
Chris@152
|
346
|
Chris@152
|
347 for (size_t i = 0; i < m_binNames.size(); ++i) {
|
Chris@152
|
348 if (m_binNames[i] != "") {
|
Chris@152
|
349 out << indent + " ";
|
Chris@152
|
350 out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
|
Chris@152
|
351 .arg(i).arg(m_binNames[i]);
|
Chris@152
|
352 }
|
Chris@152
|
353 }
|
Chris@152
|
354
|
Chris@152
|
355 for (size_t i = 0; i < m_data.size(); ++i) {
|
Chris@152
|
356 out << indent + " ";
|
Chris@152
|
357 out << QString("<row n=\"%1\">").arg(i);
|
Chris@533
|
358 for (size_t j = 0; j < m_data.at(i).size(); ++j) {
|
Chris@152
|
359 if (j > 0) out << " ";
|
Chris@533
|
360 out << m_data.at(i).at(j);
|
Chris@152
|
361 }
|
Chris@152
|
362 out << QString("</row>\n");
|
Chris@318
|
363 out.flush();
|
Chris@152
|
364 }
|
Chris@152
|
365
|
Chris@152
|
366 out << indent + "</dataset>\n";
|
Chris@152
|
367 }
|
Chris@152
|
368
|
Chris@152
|
369
|