annotate data/model/EditableDenseThreeDimensionalModel.cpp @ 1839:915d316a5609

Fix out of range access to magnitudes
author Chris Cannam
date Thu, 09 Apr 2020 14:59:05 +0100
parents 21c792334c2e
children
rev   line source
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@1833 354 QVector<QString>
Chris@1833 355 EditableDenseThreeDimensionalModel::getStringExportHeaders(DataExportOptions)
Chris@1833 356 const
Chris@1815 357 {
Chris@1833 358 QVector<QString> sv;
Chris@1815 359 for (int i = 0; i < m_yBinCount; ++i) {
Chris@1833 360 sv.push_back(QString("Bin%1").arg(i+1));
Chris@1815 361 }
Chris@1833 362 return sv;
Chris@1815 363 }
Chris@1815 364
Chris@1833 365 QVector<QVector<QString>>
Chris@1833 366 EditableDenseThreeDimensionalModel::toStringExportRows(DataExportOptions,
Chris@1833 367 sv_frame_t startFrame,
Chris@1833 368 sv_frame_t duration)
Chris@1833 369 const
Chris@838 370 {
Chris@1777 371 QMutexLocker locker(&m_mutex);
Chris@1833 372
Chris@1833 373 QVector<QVector<QString>> rows;
Chris@1833 374
Chris@1154 375 for (int i = 0; in_range_for(m_data, i); ++i) {
Chris@1038 376 sv_frame_t fr = m_startFrame + i * m_resolution;
Chris@1679 377 if (fr >= startFrame && fr < startFrame + duration) {
Chris@1833 378 QVector<QString> row;
Chris@1154 379 for (int j = 0; in_range_for(m_data.at(i), j); ++j) {
Chris@1833 380 row.push_back(QString("%1").arg(m_data.at(i).at(j)));
Chris@838 381 }
Chris@1833 382 rows.push_back(row);
Chris@838 383 }
Chris@838 384 }
Chris@1833 385 return rows;
Chris@838 386 }
Chris@838 387
Chris@152 388 void
Chris@152 389 EditableDenseThreeDimensionalModel::toXml(QTextStream &out,
Chris@314 390 QString indent,
Chris@314 391 QString extraAttributes) const
Chris@152 392 {
Chris@1777 393 QMutexLocker locker(&m_mutex);
Chris@534 394
Chris@1677 395 // For historical reasons we read and write "resolution" as "windowSize".
Chris@1677 396
Chris@1677 397 // Our dataset doesn't have its own export ID, we just use
Chris@1677 398 // ours. Actually any model could do that, since datasets aren't
Chris@1677 399 // in the same id-space as models when re-read
Chris@152 400
Chris@690 401 SVDEBUG << "EditableDenseThreeDimensionalModel::toXml" << endl;
Chris@318 402
Chris@314 403 Model::toXml
Chris@1429 404 (out, indent,
Chris@611 405 QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7")
Chris@1429 406 .arg(m_resolution)
Chris@1429 407 .arg(m_yBinCount)
Chris@1429 408 .arg(m_minimum)
Chris@1429 409 .arg(m_maximum)
Chris@1677 410 .arg(getExportId())
Chris@611 411 .arg(m_startFrame)
Chris@1429 412 .arg(extraAttributes));
Chris@152 413
Chris@152 414 out << indent;
Chris@152 415 out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
Chris@1677 416 .arg(getExportId());
Chris@152 417
Chris@1777 418 for (int i = 0; in_range_for(m_binNames, i); ++i) {
Chris@1429 419 if (m_binNames[i] != "") {
Chris@1429 420 out << indent + " ";
Chris@1429 421 out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
Chris@1429 422 .arg(i).arg(m_binNames[i]);
Chris@1429 423 }
Chris@152 424 }
Chris@152 425
Chris@1777 426 for (int i = 0; in_range_for(m_data, i); ++i) {
Chris@1777 427 Column c = getColumn(i);
Chris@1429 428 out << indent + " ";
Chris@1429 429 out << QString("<row n=\"%1\">").arg(i);
Chris@1777 430 for (int j = 0; in_range_for(c, j); ++j) {
Chris@1429 431 if (j > 0) out << " ";
Chris@1777 432 out << c.at(j);
Chris@1429 433 }
Chris@1429 434 out << QString("</row>\n");
Chris@318 435 out.flush();
Chris@152 436 }
Chris@152 437
Chris@152 438 out << indent + "</dataset>\n";
Chris@152 439 }
Chris@152 440
Chris@152 441