annotate data/model/EditableDenseThreeDimensionalModel.cpp @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +0100
parents c546429d4c2f
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