annotate data/model/SparseModel.h @ 1520:954d0cf29ca7 import-audio-data

Switch the normalisation option in WritableWaveFileModel from normalising on read to normalising on write, so that the saved file is already normalised and therefore can be read again without having to remember to normalise it
author Chris Cannam
date Wed, 12 Sep 2018 13:56:56 +0100
parents f68911282993
children c01cbe41aeb5
rev   line source
Chris@147 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@147 2
Chris@147 3 /*
Chris@147 4 Sonic Visualiser
Chris@147 5 An audio file viewer and annotation editor.
Chris@147 6 Centre for Digital Music, Queen Mary, University of London.
Chris@147 7 This file copyright 2006 Chris Cannam.
Chris@147 8
Chris@147 9 This program is free software; you can redistribute it and/or
Chris@147 10 modify it under the terms of the GNU General Public License as
Chris@147 11 published by the Free Software Foundation; either version 2 of the
Chris@147 12 License, or (at your option) any later version. See the file
Chris@147 13 COPYING included with this distribution for more information.
Chris@147 14 */
Chris@147 15
cannam@1452 16 #ifndef SV_SPARSE_MODEL_H
cannam@1452 17 #define SV_SPARSE_MODEL_H
Chris@147 18
Chris@150 19 #include "Model.h"
Chris@420 20 #include "TabularModel.h"
Chris@147 21 #include "base/Command.h"
Chris@425 22 #include "base/RealTime.h"
Chris@1218 23 #include "system/System.h"
Chris@147 24
Chris@147 25 #include <iostream>
Chris@147 26
Chris@147 27 #include <set>
Chris@420 28 #include <vector>
Chris@420 29 #include <algorithm>
Chris@608 30 #include <iterator>
Chris@420 31
Chris@425 32 #include <cmath>
Chris@425 33
Chris@147 34 #include <QMutex>
Chris@147 35 #include <QTextStream>
Chris@147 36
Chris@147 37 /**
Chris@147 38 * Model containing sparse data (points with some properties). The
Chris@147 39 * properties depend on the point type.
Chris@147 40 */
Chris@147 41
Chris@147 42 template <typename PointType>
Chris@420 43 class SparseModel : public Model,
Chris@420 44 public TabularModel
Chris@147 45 {
Chris@1458 46 // If we omit the Q_OBJECT macro, lupdate complains.
Chris@1458 47
Chris@1458 48 // If we include it, moc fails (can't handle template classes).
Chris@1458 49
Chris@1458 50 // If we omit it, lupdate still seems to emit translatable
Chris@1458 51 // messages for the tr() strings in here. So I guess we omit it.
Chris@1458 52
Chris@147 53 public:
Chris@1040 54 SparseModel(sv_samplerate_t sampleRate, int resolution,
Chris@1429 55 bool notifyOnAdd = true);
Chris@147 56 virtual ~SparseModel() { }
Chris@147 57
Chris@147 58 virtual bool isOK() const { return true; }
Chris@1038 59 virtual sv_frame_t getStartFrame() const;
Chris@1038 60 virtual sv_frame_t getEndFrame() const;
Chris@1040 61 virtual sv_samplerate_t getSampleRate() const { return m_sampleRate; }
Chris@147 62
Chris@147 63 // Number of frames of the underlying sample rate that this model
Chris@147 64 // is capable of resolving to. For example, if m_resolution == 10
Chris@147 65 // then every point in this model will be at a multiple of 10
Chris@147 66 // sample frames and should be considered to cover a window ending
Chris@147 67 // 10 sample frames later.
Chris@929 68 virtual int getResolution() const {
Chris@147 69 return m_resolution ? m_resolution : 1;
Chris@147 70 }
Chris@929 71 virtual void setResolution(int resolution);
Chris@1466 72
Chris@1466 73 // Extend the end of the model. If this is set to something beyond
Chris@1466 74 // the end of the final point in the model, then getEndFrame()
Chris@1466 75 // will return this value. Otherwise getEndFrame() will return the
Chris@1466 76 // end of the final point. (This is used by the Tony application)
Chris@1466 77 virtual void extendEndFrame(sv_frame_t to) { m_extendTo = to; }
Chris@1466 78
Chris@147 79 typedef PointType Point;
Chris@147 80 typedef std::multiset<PointType,
Chris@1429 81 typename PointType::OrderComparator> PointList;
Chris@147 82 typedef typename PointList::iterator PointListIterator;
Chris@606 83 typedef typename PointList::const_iterator PointListConstIterator;
Chris@147 84
Chris@147 85 /**
Chris@147 86 * Return whether the model is empty or not.
Chris@147 87 */
Chris@147 88 virtual bool isEmpty() const;
Chris@147 89
Chris@147 90 /**
Chris@147 91 * Get the total number of points in the model.
Chris@147 92 */
Chris@929 93 virtual int getPointCount() const;
Chris@147 94
Chris@147 95 /**
Chris@459 96 * Get all points.
Chris@459 97 */
Chris@459 98 virtual const PointList &getPoints() const;
Chris@459 99
Chris@459 100 /**
Chris@147 101 * Get all of the points in this model between the given
Chris@147 102 * boundaries (in frames), as well as up to two points before and
Chris@147 103 * after the boundaries. If you need exact boundaries, check the
Chris@147 104 * point coordinates in the returned list.
Chris@147 105 */
Chris@1038 106 virtual PointList getPoints(sv_frame_t start, sv_frame_t end) const;
Chris@147 107
Chris@147 108 /**
Chris@147 109 * Get all points that cover the given frame number, taking the
Chris@147 110 * resolution of the model into account.
Chris@147 111 */
Chris@1038 112 virtual PointList getPoints(sv_frame_t frame) const;
Chris@147 113
Chris@147 114 /**
Chris@147 115 * Return all points that share the nearest frame number prior to
Chris@147 116 * the given one at which there are any points.
Chris@147 117 */
Chris@1038 118 virtual PointList getPreviousPoints(sv_frame_t frame) const;
Chris@147 119
Chris@147 120 /**
Chris@147 121 * Return all points that share the nearest frame number
Chris@147 122 * subsequent to the given one at which there are any points.
Chris@147 123 */
Chris@1038 124 virtual PointList getNextPoints(sv_frame_t frame) const;
Chris@147 125
Chris@147 126 /**
Chris@147 127 * Remove all points.
Chris@147 128 */
Chris@147 129 virtual void clear();
Chris@147 130
Chris@147 131 /**
Chris@147 132 * Add a point.
Chris@147 133 */
Chris@147 134 virtual void addPoint(const PointType &point);
Chris@147 135
Chris@147 136 /**
Chris@147 137 * Remove a point. Points are not necessarily unique, so this
Chris@147 138 * function will remove the first point that compares equal to the
Chris@147 139 * supplied one using Point::Comparator. Other identical points
Chris@147 140 * may remain in the model.
Chris@147 141 */
Chris@147 142 virtual void deletePoint(const PointType &point);
Chris@147 143
Chris@1113 144 /**
Chris@1113 145 * Return true if the given point is found in this model, false
Chris@1113 146 * otherwise.
Chris@1113 147 */
Chris@1113 148 virtual bool containsPoint(const PointType &point);
Chris@1113 149
Chris@297 150 virtual bool isReady(int *completion = 0) const {
Chris@297 151 bool ready = isOK() && (m_completion == 100);
Chris@297 152 if (completion) *completion = m_completion;
Chris@297 153 return ready;
Chris@297 154 }
Chris@297 155
Chris@333 156 virtual void setCompletion(int completion, bool update = true);
Chris@147 157 virtual int getCompletion() const { return m_completion; }
Chris@147 158
Chris@147 159 virtual bool hasTextLabels() const { return m_hasTextLabels; }
Chris@147 160
cannam@1452 161 virtual bool isSparse() const { return true; }
cannam@1452 162
Chris@345 163 QString getTypeName() const { return tr("Sparse"); }
Chris@345 164
Chris@407 165 virtual QString getXmlOutputType() const { return "sparse"; }
Chris@407 166
Chris@147 167 virtual void toXml(QTextStream &out,
Chris@147 168 QString indent = "",
Chris@147 169 QString extraAttributes = "") const;
Chris@147 170
Chris@1060 171 virtual QString toDelimitedDataString(QString delimiter) const {
Chris@1072 172 return toDelimitedDataStringWithOptions
Chris@1072 173 (delimiter, DataExportDefaults);
Chris@1060 174 }
Chris@1060 175
Chris@1060 176 virtual QString toDelimitedDataStringWithOptions(QString delimiter,
Chris@1064 177 DataExportOptions opts) const {
Chris@1064 178 return toDelimitedDataStringSubsetWithOptions
Chris@1064 179 (delimiter, opts,
Chris@1451 180 std::min(getStartFrame(), sv_frame_t(0)), getEndFrame());
Chris@147 181 }
Chris@147 182
Chris@1060 183 virtual QString toDelimitedDataStringSubset(QString delimiter, sv_frame_t f0, sv_frame_t f1) const {
Chris@1072 184 return toDelimitedDataStringSubsetWithOptions
Chris@1072 185 (delimiter, DataExportDefaults, f0, f1);
Chris@1060 186 }
Chris@1060 187
Chris@1064 188 virtual QString toDelimitedDataStringSubsetWithOptions(QString delimiter, DataExportOptions opts, sv_frame_t f0, sv_frame_t f1) const {
Chris@1064 189 if (opts & DataExportFillGaps) {
Chris@1064 190 return toDelimitedDataStringSubsetFilled(delimiter, opts, f0, f1);
Chris@1064 191 } else {
Chris@1064 192 QString s;
Chris@1064 193 for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@1127 194 if (i->frame >= f0 && i->frame < f1) {
Chris@1064 195 s += i->toDelimitedDataString(delimiter, opts, m_sampleRate) + "\n";
Chris@1064 196 }
Chris@838 197 }
Chris@1064 198 return s;
Chris@838 199 }
Chris@838 200 }
Chris@838 201
Chris@147 202 /**
Chris@147 203 * Command to add a point, with undo.
Chris@147 204 */
Chris@147 205 class AddPointCommand : public Command
Chris@147 206 {
Chris@147 207 public:
Chris@1429 208 AddPointCommand(SparseModel<PointType> *model,
Chris@1429 209 const PointType &point,
Chris@147 210 QString name = "") :
Chris@1429 211 m_model(model), m_point(point), m_name(name) { }
Chris@147 212
Chris@1429 213 virtual QString getName() const {
Chris@147 214 return (m_name == "" ? tr("Add Point") : m_name);
Chris@147 215 }
Chris@147 216
Chris@1429 217 virtual void execute() { m_model->addPoint(m_point); }
Chris@1429 218 virtual void unexecute() { m_model->deletePoint(m_point); }
Chris@147 219
Chris@1429 220 const PointType &getPoint() const { return m_point; }
Chris@147 221
Chris@147 222 private:
Chris@1429 223 SparseModel<PointType> *m_model;
Chris@1429 224 PointType m_point;
Chris@147 225 QString m_name;
Chris@147 226 };
Chris@147 227
Chris@147 228
Chris@147 229 /**
Chris@147 230 * Command to remove a point, with undo.
Chris@147 231 */
Chris@147 232 class DeletePointCommand : public Command
Chris@147 233 {
Chris@147 234 public:
Chris@1429 235 DeletePointCommand(SparseModel<PointType> *model,
Chris@1429 236 const PointType &point) :
Chris@1429 237 m_model(model), m_point(point) { }
Chris@147 238
Chris@1429 239 virtual QString getName() const { return tr("Delete Point"); }
Chris@147 240
Chris@1429 241 virtual void execute() { m_model->deletePoint(m_point); }
Chris@1429 242 virtual void unexecute() { m_model->addPoint(m_point); }
Chris@147 243
Chris@1429 244 const PointType &getPoint() const { return m_point; }
Chris@147 245
Chris@147 246 private:
Chris@1429 247 SparseModel<PointType> *m_model;
Chris@1429 248 PointType m_point;
Chris@147 249 };
Chris@147 250
Chris@147 251
Chris@147 252 /**
Chris@147 253 * Command to add or remove a series of points, with undo.
Chris@147 254 * Consecutive add/remove pairs for the same point are collapsed.
Chris@147 255 */
Chris@147 256 class EditCommand : public MacroCommand
Chris@147 257 {
Chris@147 258 public:
Chris@1429 259 EditCommand(SparseModel<PointType> *model, QString commandName);
Chris@147 260
Chris@1429 261 virtual void addPoint(const PointType &point);
Chris@1429 262 virtual void deletePoint(const PointType &point);
Chris@147 263
Chris@1429 264 /**
Chris@1429 265 * Stack an arbitrary other command in the same sequence.
Chris@1429 266 */
Chris@1429 267 virtual void addCommand(Command *command) { addCommand(command, true); }
Chris@147 268
Chris@1429 269 /**
Chris@1429 270 * If any points have been added or deleted, return this
Chris@1429 271 * command (so the caller can add it to the command history).
Chris@1429 272 * Otherwise delete the command and return NULL.
Chris@1429 273 */
Chris@1429 274 virtual EditCommand *finish();
Chris@147 275
Chris@147 276 protected:
Chris@1429 277 virtual void addCommand(Command *command, bool executeFirst);
Chris@147 278
Chris@1429 279 SparseModel<PointType> *m_model;
Chris@147 280 };
Chris@147 281
Chris@147 282
Chris@147 283 /**
Chris@147 284 * Command to relabel a point.
Chris@147 285 */
Chris@147 286 class RelabelCommand : public Command
Chris@147 287 {
Chris@147 288 public:
Chris@1429 289 RelabelCommand(SparseModel<PointType> *model,
Chris@1429 290 const PointType &point,
Chris@1429 291 QString newLabel) :
Chris@1429 292 m_model(model), m_oldPoint(point), m_newPoint(point) {
Chris@1429 293 m_newPoint.label = newLabel;
Chris@1429 294 }
Chris@147 295
Chris@1429 296 virtual QString getName() const { return tr("Re-Label Point"); }
Chris@147 297
Chris@1429 298 virtual void execute() {
Chris@1429 299 m_model->deletePoint(m_oldPoint);
Chris@1429 300 m_model->addPoint(m_newPoint);
Chris@1429 301 std::swap(m_oldPoint, m_newPoint);
Chris@1429 302 }
Chris@147 303
Chris@1429 304 virtual void unexecute() { execute(); }
Chris@147 305
Chris@147 306 private:
Chris@1429 307 SparseModel<PointType> *m_model;
Chris@1429 308 PointType m_oldPoint;
Chris@1429 309 PointType m_newPoint;
Chris@147 310 };
Chris@147 311
Chris@420 312 /**
Chris@420 313 * TabularModel methods.
Chris@420 314 */
Chris@420 315
Chris@420 316 virtual int getRowCount() const
Chris@420 317 {
Chris@1038 318 return int(m_points.size());
Chris@420 319 }
Chris@420 320
Chris@1038 321 virtual sv_frame_t getFrameForRow(int row) const
Chris@420 322 {
Chris@606 323 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@420 324 if (i == m_points.end()) return 0;
Chris@420 325 return i->frame;
Chris@420 326 }
Chris@420 327
Chris@1038 328 virtual int getRowForFrame(sv_frame_t frame) const
Chris@420 329 {
Chris@420 330 if (m_rows.empty()) rebuildRowVector();
Chris@1038 331 std::vector<sv_frame_t>::iterator i =
Chris@420 332 std::lower_bound(m_rows.begin(), m_rows.end(), frame);
Chris@1038 333 ssize_t row = std::distance(m_rows.begin(), i);
Chris@432 334 if (i != m_rows.begin() && (i == m_rows.end() || *i != frame)) {
Chris@432 335 --row;
Chris@432 336 }
Chris@1038 337 return int(row);
Chris@420 338 }
Chris@420 339
Chris@420 340 virtual int getColumnCount() const { return 1; }
Chris@425 341 virtual QVariant getData(int row, int column, int role) const
Chris@425 342 {
Chris@606 343 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@1254 344 if (i == m_points.end()) {
Chris@1254 345 // cerr << "no iterator for row " << row << " (have " << getRowCount() << " rows)" << endl;
Chris@1254 346 return QVariant();
Chris@1254 347 }
Chris@425 348
Chris@1254 349 // cerr << "returning data for row " << row << " col " << column << endl;
Chris@1254 350
Chris@425 351 switch (column) {
Chris@425 352 case 0: {
Chris@425 353 if (role == SortRole) return int(i->frame);
Chris@425 354 RealTime rt = RealTime::frame2RealTime(i->frame, getSampleRate());
Chris@425 355 if (role == Qt::EditRole) return rt.toString().c_str();
Chris@425 356 else return rt.toText().c_str();
Chris@425 357 }
Chris@425 358 case 1: return int(i->frame);
Chris@425 359 }
Chris@425 360
Chris@420 361 return QVariant();
Chris@420 362 }
Chris@427 363
Chris@425 364 virtual Command *getSetDataCommand(int row, int column,
Chris@425 365 const QVariant &value, int role)
Chris@425 366 {
Chris@740 367 if (role != Qt::EditRole) return 0;
Chris@425 368 PointListIterator i = getPointListIteratorForRow(row);
Chris@740 369 if (i == m_points.end()) return 0;
Chris@425 370 EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@425 371
Chris@425 372 Point point(*i);
Chris@425 373 command->deletePoint(point);
Chris@425 374
Chris@425 375 switch (column) {
Chris@425 376 case 0: point.frame = lrint(value.toDouble() * getSampleRate()); break;
Chris@425 377 case 1: point.frame = value.toInt(); break;
Chris@425 378 }
Chris@425 379
Chris@425 380 command->addPoint(point);
Chris@425 381 return command->finish();
Chris@425 382 }
Chris@425 383
Chris@427 384 virtual Command *getInsertRowCommand(int row)
Chris@427 385 {
Chris@427 386 EditCommand *command = new EditCommand(this, tr("Insert Data Point"));
Chris@427 387 Point point(0);
Chris@427 388 PointListIterator i = getPointListIteratorForRow(row);
Chris@427 389 if (i == m_points.end() && i != m_points.begin()) --i;
Chris@427 390 if (i != m_points.end()) point = *i;
Chris@427 391 command->addPoint(point);
Chris@427 392 return command->finish();
Chris@427 393 }
Chris@427 394
Chris@427 395 virtual Command *getRemoveRowCommand(int row)
Chris@427 396 {
Chris@427 397 PointListIterator i = getPointListIteratorForRow(row);
Chris@427 398 if (i == m_points.end()) return 0;
Chris@978 399 EditCommand *command = new EditCommand(this, tr("Delete Data Point"));
Chris@427 400 command->deletePoint(*i);
Chris@427 401 return command->finish();
Chris@427 402 }
Chris@427 403
Chris@147 404 protected:
Chris@1040 405 sv_samplerate_t m_sampleRate;
Chris@929 406 int m_resolution;
Chris@1466 407 sv_frame_t m_extendTo;
Chris@147 408 bool m_notifyOnAdd;
Chris@1038 409 sv_frame_t m_sinceLastNotifyMin;
Chris@1038 410 sv_frame_t m_sinceLastNotifyMax;
Chris@147 411 bool m_hasTextLabels;
Chris@147 412
Chris@147 413 PointList m_points;
Chris@929 414 int m_pointCount;
Chris@147 415 mutable QMutex m_mutex;
Chris@147 416 int m_completion;
Chris@420 417
Chris@1038 418 void getPointIterators(sv_frame_t frame,
Chris@420 419 PointListIterator &startItr,
Chris@608 420 PointListIterator &endItr);
Chris@1038 421 void getPointIterators(sv_frame_t frame,
Chris@608 422 PointListConstIterator &startItr,
Chris@608 423 PointListConstIterator &endItr) const;
Chris@420 424
Chris@420 425 // This is only used if the model is called on to act in
Chris@420 426 // TabularModel mode
Chris@1038 427 mutable std::vector<sv_frame_t> m_rows; // map from row number to frame
Chris@1254 428
Chris@420 429 void rebuildRowVector() const
Chris@420 430 {
Chris@420 431 m_rows.clear();
Chris@608 432 for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@777 433 // std::cerr << "rebuildRowVector: row " << m_rows.size() << " -> " << i->frame << std::endl;
Chris@420 434 m_rows.push_back(i->frame);
Chris@420 435 }
Chris@420 436 }
Chris@420 437
Chris@608 438 PointListIterator getPointListIteratorForRow(int row)
Chris@420 439 {
Chris@420 440 if (m_rows.empty()) rebuildRowVector();
Chris@425 441 if (row < 0 || row + 1 > int(m_rows.size())) return m_points.end();
Chris@420 442
Chris@1038 443 sv_frame_t frame = m_rows[row];
Chris@420 444 int indexAtFrame = 0;
Chris@420 445 int ri = row;
Chris@420 446 while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
Chris@420 447 int initialIndexAtFrame = indexAtFrame;
Chris@420 448
Chris@420 449 PointListIterator i0, i1;
Chris@420 450 getPointIterators(frame, i0, i1);
Chris@420 451 PointListIterator i = i0;
Chris@420 452
Chris@420 453 for (i = i0; i != i1; ++i) {
Chris@778 454 if (i->frame < (int)frame) { continue; }
Chris@420 455 if (indexAtFrame > 0) { --indexAtFrame; continue; }
Chris@420 456 return i;
Chris@420 457 }
Chris@420 458
Chris@420 459 if (indexAtFrame > 0) {
Chris@420 460 std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl;
Chris@420 461 }
Chris@420 462 return i;
Chris@420 463 }
Chris@608 464
Chris@608 465 PointListConstIterator getPointListIteratorForRow(int row) const
Chris@608 466 {
Chris@608 467 if (m_rows.empty()) rebuildRowVector();
Chris@608 468 if (row < 0 || row + 1 > int(m_rows.size())) return m_points.end();
Chris@608 469
Chris@1038 470 sv_frame_t frame = m_rows[row];
Chris@608 471 int indexAtFrame = 0;
Chris@608 472 int ri = row;
Chris@608 473 while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
Chris@608 474 int initialIndexAtFrame = indexAtFrame;
Chris@608 475
Chris@1254 476 // std::cerr << "getPointListIteratorForRow " << row << ": initialIndexAtFrame = " << initialIndexAtFrame << " for frame " << frame << std::endl;
Chris@777 477
Chris@608 478 PointListConstIterator i0, i1;
Chris@608 479 getPointIterators(frame, i0, i1);
Chris@608 480 PointListConstIterator i = i0;
Chris@608 481
Chris@608 482 for (i = i0; i != i1; ++i) {
Chris@785 483 // std::cerr << "i->frame is " << i->frame << ", wanting " << frame << std::endl;
Chris@785 484
Chris@778 485 if (i->frame < (int)frame) { continue; }
Chris@608 486 if (indexAtFrame > 0) { --indexAtFrame; continue; }
Chris@608 487 return i;
Chris@608 488 }
Chris@1254 489 /*
Chris@1254 490 if (i == m_points.end()) {
Chris@1254 491 std::cerr << "returning i at end" << std::endl;
Chris@1254 492 } else {
Chris@1254 493 std::cerr << "returning i with i->frame = " << i->frame << std::endl;
Chris@1254 494 }
Chris@1254 495 */
Chris@608 496 if (indexAtFrame > 0) {
Chris@608 497 std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl;
Chris@608 498 }
Chris@608 499 return i;
Chris@608 500 }
Chris@1064 501
Chris@1064 502 QString toDelimitedDataStringSubsetFilled(QString delimiter,
Chris@1064 503 DataExportOptions opts,
Chris@1064 504 sv_frame_t f0,
Chris@1064 505 sv_frame_t f1) const {
Chris@1064 506
Chris@1064 507 QString s;
Chris@1064 508 opts &= ~DataExportFillGaps;
Chris@1064 509
Chris@1064 510 // find frame time of first point in range (if any)
Chris@1064 511 sv_frame_t first = f0;
Chris@1064 512 for (auto &p: m_points) {
Chris@1064 513 if (p.frame >= f0) {
Chris@1064 514 first = p.frame;
Chris@1064 515 break;
Chris@1064 516 }
Chris@1064 517 }
Chris@1064 518
Chris@1064 519 // project back to first frame time in range according to
Chris@1064 520 // resolution. e.g. if f0 = 2, first = 9, resolution = 4 then
Chris@1064 521 // we start at 5 (because 1 is too early and we need to arrive
Chris@1064 522 // at 9 to match the first actual point). This method is
Chris@1064 523 // stupid but easy to understand:
Chris@1064 524 sv_frame_t f = first;
Chris@1064 525 while (f >= f0 + m_resolution) f -= m_resolution;
Chris@1064 526
Chris@1064 527 // now progress, either writing the next point (if within
Chris@1064 528 // distance) or a default point
Chris@1064 529 PointListConstIterator itr = m_points.begin();
Chris@1064 530
Chris@1064 531 while (f < f1) {
Chris@1064 532 if (itr != m_points.end() && itr->frame <= f) {
Chris@1064 533 s += itr->toDelimitedDataString(delimiter, opts, m_sampleRate);
Chris@1064 534 ++itr;
Chris@1064 535 } else {
Chris@1064 536 s += Point(f).toDelimitedDataString(delimiter, opts, m_sampleRate);
Chris@1064 537 }
Chris@1064 538 s += "\n";
Chris@1064 539 f += m_resolution;
Chris@1064 540 }
Chris@1064 541
Chris@1064 542 return s;
Chris@1064 543 }
Chris@147 544 };
Chris@147 545
Chris@147 546
Chris@147 547 template <typename PointType>
Chris@1040 548 SparseModel<PointType>::SparseModel(sv_samplerate_t sampleRate,
Chris@929 549 int resolution,
Chris@147 550 bool notifyOnAdd) :
Chris@147 551 m_sampleRate(sampleRate),
Chris@147 552 m_resolution(resolution),
Chris@1466 553 m_extendTo(0),
Chris@147 554 m_notifyOnAdd(notifyOnAdd),
Chris@147 555 m_sinceLastNotifyMin(-1),
Chris@147 556 m_sinceLastNotifyMax(-1),
Chris@147 557 m_hasTextLabels(false),
Chris@147 558 m_pointCount(0),
Chris@147 559 m_completion(100)
Chris@147 560 {
Chris@147 561 }
Chris@147 562
Chris@147 563 template <typename PointType>
Chris@1038 564 sv_frame_t
Chris@147 565 SparseModel<PointType>::getStartFrame() const
Chris@147 566 {
Chris@147 567 QMutexLocker locker(&m_mutex);
Chris@1038 568 sv_frame_t f = 0;
Chris@147 569 if (!m_points.empty()) {
Chris@1429 570 f = m_points.begin()->frame;
Chris@147 571 }
Chris@147 572 return f;
Chris@147 573 }
Chris@147 574
Chris@147 575 template <typename PointType>
Chris@1038 576 sv_frame_t
Chris@147 577 SparseModel<PointType>::getEndFrame() const
Chris@147 578 {
Chris@147 579 QMutexLocker locker(&m_mutex);
Chris@1038 580 sv_frame_t f = 0;
Chris@147 581 if (!m_points.empty()) {
Chris@1429 582 PointListConstIterator i(m_points.end());
Chris@1451 583 f = (--i)->frame + 1;
Chris@147 584 }
Chris@1466 585 if (m_extendTo > f) {
Chris@1466 586 return m_extendTo;
Chris@1466 587 } else {
Chris@1466 588 return f;
Chris@1466 589 }
Chris@147 590 }
Chris@147 591
Chris@147 592 template <typename PointType>
Chris@147 593 bool
Chris@147 594 SparseModel<PointType>::isEmpty() const
Chris@147 595 {
Chris@147 596 return m_pointCount == 0;
Chris@147 597 }
Chris@147 598
Chris@147 599 template <typename PointType>
Chris@929 600 int
Chris@147 601 SparseModel<PointType>::getPointCount() const
Chris@147 602 {
Chris@147 603 return m_pointCount;
Chris@147 604 }
Chris@147 605
Chris@147 606 template <typename PointType>
Chris@459 607 const typename SparseModel<PointType>::PointList &
Chris@459 608 SparseModel<PointType>::getPoints() const
Chris@459 609 {
Chris@459 610 return m_points;
Chris@459 611 }
Chris@459 612
Chris@459 613 template <typename PointType>
Chris@147 614 typename SparseModel<PointType>::PointList
Chris@1038 615 SparseModel<PointType>::getPoints(sv_frame_t start, sv_frame_t end) const
Chris@147 616 {
Chris@147 617 if (start > end) return PointList();
Chris@147 618 QMutexLocker locker(&m_mutex);
Chris@147 619
Chris@147 620 PointType startPoint(start), endPoint(end);
Chris@147 621
Chris@608 622 PointListConstIterator startItr = m_points.lower_bound(startPoint);
Chris@608 623 PointListConstIterator endItr = m_points.upper_bound(endPoint);
Chris@147 624
Chris@147 625 if (startItr != m_points.begin()) --startItr;
Chris@147 626 if (startItr != m_points.begin()) --startItr;
Chris@147 627 if (endItr != m_points.end()) ++endItr;
Chris@147 628 if (endItr != m_points.end()) ++endItr;
Chris@147 629
Chris@147 630 PointList rv;
Chris@147 631
Chris@608 632 for (PointListConstIterator i = startItr; i != endItr; ++i) {
Chris@1429 633 rv.insert(*i);
Chris@147 634 }
Chris@147 635
Chris@147 636 return rv;
Chris@147 637 }
Chris@147 638
Chris@147 639 template <typename PointType>
Chris@147 640 typename SparseModel<PointType>::PointList
Chris@1038 641 SparseModel<PointType>::getPoints(sv_frame_t frame) const
Chris@147 642 {
Chris@608 643 PointListConstIterator startItr, endItr;
Chris@420 644 getPointIterators(frame, startItr, endItr);
Chris@147 645
Chris@147 646 PointList rv;
Chris@147 647
Chris@608 648 for (PointListConstIterator i = startItr; i != endItr; ++i) {
Chris@1429 649 rv.insert(*i);
Chris@147 650 }
Chris@147 651
Chris@147 652 return rv;
Chris@147 653 }
Chris@147 654
Chris@147 655 template <typename PointType>
Chris@420 656 void
Chris@1038 657 SparseModel<PointType>::getPointIterators(sv_frame_t frame,
Chris@420 658 PointListIterator &startItr,
Chris@608 659 PointListIterator &endItr)
Chris@608 660 {
Chris@608 661 QMutexLocker locker(&m_mutex);
Chris@608 662
Chris@608 663 if (m_resolution == 0) {
Chris@608 664 startItr = m_points.end();
Chris@608 665 endItr = m_points.end();
Chris@608 666 return;
Chris@608 667 }
Chris@608 668
Chris@1038 669 sv_frame_t start = (frame / m_resolution) * m_resolution;
Chris@1038 670 sv_frame_t end = start + m_resolution;
Chris@608 671
Chris@608 672 PointType startPoint(start), endPoint(end);
Chris@777 673
Chris@608 674 startItr = m_points.lower_bound(startPoint);
Chris@608 675 endItr = m_points.upper_bound(endPoint);
Chris@608 676 }
Chris@608 677
Chris@608 678 template <typename PointType>
Chris@608 679 void
Chris@1038 680 SparseModel<PointType>::getPointIterators(sv_frame_t frame,
Chris@608 681 PointListConstIterator &startItr,
Chris@608 682 PointListConstIterator &endItr) const
Chris@420 683 {
Chris@420 684 QMutexLocker locker(&m_mutex);
Chris@420 685
Chris@420 686 if (m_resolution == 0) {
Chris@785 687 // std::cerr << "getPointIterators: resolution == 0, returning end()" << std::endl;
Chris@420 688 startItr = m_points.end();
Chris@420 689 endItr = m_points.end();
Chris@420 690 return;
Chris@420 691 }
Chris@420 692
Chris@1038 693 sv_frame_t start = (frame / m_resolution) * m_resolution;
Chris@1038 694 sv_frame_t end = start + m_resolution;
Chris@420 695
Chris@420 696 PointType startPoint(start), endPoint(end);
Chris@420 697
Chris@777 698 // std::cerr << "getPointIterators: start frame " << start << ", end frame " << end << ", m_resolution " << m_resolution << std::endl;
Chris@785 699
Chris@420 700 startItr = m_points.lower_bound(startPoint);
Chris@420 701 endItr = m_points.upper_bound(endPoint);
Chris@420 702 }
Chris@420 703
Chris@420 704 template <typename PointType>
Chris@147 705 typename SparseModel<PointType>::PointList
Chris@1038 706 SparseModel<PointType>::getPreviousPoints(sv_frame_t originFrame) const
Chris@147 707 {
Chris@147 708 QMutexLocker locker(&m_mutex);
Chris@147 709
Chris@147 710 PointType lookupPoint(originFrame);
Chris@147 711 PointList rv;
Chris@147 712
Chris@608 713 PointListConstIterator i = m_points.lower_bound(lookupPoint);
Chris@147 714 if (i == m_points.begin()) return rv;
Chris@147 715
Chris@147 716 --i;
Chris@1038 717 sv_frame_t frame = i->frame;
Chris@147 718 while (i->frame == frame) {
Chris@1429 719 rv.insert(*i);
Chris@1429 720 if (i == m_points.begin()) break;
Chris@1429 721 --i;
Chris@147 722 }
Chris@147 723
Chris@147 724 return rv;
Chris@147 725 }
Chris@147 726
Chris@147 727 template <typename PointType>
Chris@147 728 typename SparseModel<PointType>::PointList
Chris@1038 729 SparseModel<PointType>::getNextPoints(sv_frame_t originFrame) const
Chris@147 730 {
Chris@147 731 QMutexLocker locker(&m_mutex);
Chris@147 732
Chris@147 733 PointType lookupPoint(originFrame);
Chris@147 734 PointList rv;
Chris@147 735
Chris@608 736 PointListConstIterator i = m_points.upper_bound(lookupPoint);
Chris@147 737 if (i == m_points.end()) return rv;
Chris@147 738
Chris@1038 739 sv_frame_t frame = i->frame;
Chris@147 740 while (i != m_points.end() && i->frame == frame) {
Chris@1429 741 rv.insert(*i);
Chris@1429 742 ++i;
Chris@147 743 }
Chris@147 744
Chris@147 745 return rv;
Chris@147 746 }
Chris@147 747
Chris@147 748 template <typename PointType>
Chris@147 749 void
Chris@929 750 SparseModel<PointType>::setResolution(int resolution)
Chris@147 751 {
Chris@147 752 {
Chris@1429 753 QMutexLocker locker(&m_mutex);
Chris@1429 754 m_resolution = resolution;
Chris@1212 755 m_rows.clear();
Chris@147 756 }
Chris@147 757 emit modelChanged();
Chris@147 758 }
Chris@147 759
Chris@147 760 template <typename PointType>
Chris@147 761 void
Chris@147 762 SparseModel<PointType>::clear()
Chris@147 763 {
Chris@147 764 {
Chris@1429 765 QMutexLocker locker(&m_mutex);
Chris@1429 766 m_points.clear();
Chris@147 767 m_pointCount = 0;
Chris@1212 768 m_rows.clear();
Chris@147 769 }
Chris@147 770 emit modelChanged();
Chris@147 771 }
Chris@147 772
Chris@147 773 template <typename PointType>
Chris@147 774 void
Chris@147 775 SparseModel<PointType>::addPoint(const PointType &point)
Chris@147 776 {
Chris@1456 777 {
Chris@1456 778 QMutexLocker locker(&m_mutex);
Chris@1212 779
Chris@1456 780 m_points.insert(point);
Chris@1456 781 m_pointCount++;
Chris@1456 782 if (point.getLabel() != "") m_hasTextLabels = true;
Chris@147 783
Chris@1456 784 // Even though this model is nominally sparse, there may still
Chris@1456 785 // be too many signals going on here (especially as they'll
Chris@1456 786 // probably be queued from one thread to another), which is
Chris@1456 787 // why we need the notifyOnAdd as an option rather than a
Chris@1456 788 // necessity (the alternative is to notify on setCompletion).
Chris@1456 789
Chris@1456 790 if (m_notifyOnAdd) {
Chris@1456 791 m_rows.clear(); //!!! inefficient
Chris@1456 792 } else {
Chris@1456 793 if (m_sinceLastNotifyMin == -1 ||
Chris@1456 794 point.frame < m_sinceLastNotifyMin) {
Chris@1456 795 m_sinceLastNotifyMin = point.frame;
Chris@1456 796 }
Chris@1456 797 if (m_sinceLastNotifyMax == -1 ||
Chris@1456 798 point.frame > m_sinceLastNotifyMax) {
Chris@1456 799 m_sinceLastNotifyMax = point.frame;
Chris@1456 800 }
Chris@1456 801 }
Chris@1456 802 }
Chris@147 803
Chris@147 804 if (m_notifyOnAdd) {
Chris@1429 805 emit modelChangedWithin(point.frame, point.frame + m_resolution);
Chris@147 806 }
Chris@147 807 }
Chris@147 808
Chris@147 809 template <typename PointType>
Chris@1113 810 bool
Chris@1113 811 SparseModel<PointType>::containsPoint(const PointType &point)
Chris@1113 812 {
Chris@1212 813 QMutexLocker locker(&m_mutex);
Chris@1113 814
Chris@1212 815 PointListIterator i = m_points.lower_bound(point);
Chris@1212 816 typename PointType::Comparator comparator;
Chris@1212 817 while (i != m_points.end()) {
Chris@1212 818 if (i->frame > point.frame) break;
Chris@1212 819 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@1212 820 return true;
Chris@1212 821 }
Chris@1212 822 ++i;
Chris@1113 823 }
Chris@1113 824
Chris@1113 825 return false;
Chris@1113 826 }
Chris@1113 827
Chris@1113 828 template <typename PointType>
Chris@147 829 void
Chris@147 830 SparseModel<PointType>::deletePoint(const PointType &point)
Chris@147 831 {
Chris@1456 832 {
Chris@1456 833 QMutexLocker locker(&m_mutex);
Chris@147 834
Chris@1456 835 PointListIterator i = m_points.lower_bound(point);
Chris@1456 836 typename PointType::Comparator comparator;
Chris@1456 837 while (i != m_points.end()) {
Chris@1456 838 if (i->frame > point.frame) break;
Chris@1456 839 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@1456 840 m_points.erase(i);
Chris@1456 841 m_pointCount--;
Chris@1456 842 break;
Chris@1429 843 }
Chris@1456 844 ++i;
Chris@1456 845 }
Chris@1212 846
Chris@147 847 // std::cout << "SparseOneDimensionalModel: emit modelChanged("
Chris@1429 848 // << point.frame << ")" << std::endl;
Chris@1456 849 m_rows.clear(); //!!! inefficient
Chris@1456 850 }
Chris@1456 851
Chris@931 852 emit modelChangedWithin(point.frame, point.frame + m_resolution);
Chris@147 853 }
Chris@147 854
Chris@147 855 template <typename PointType>
Chris@147 856 void
Chris@333 857 SparseModel<PointType>::setCompletion(int completion, bool update)
Chris@147 858 {
Chris@301 859 // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl;
Chris@1456 860 bool emitCompletionChanged = true;
Chris@1456 861 bool emitGeneralModelChanged = false;
Chris@1456 862 bool emitRegionChanged = false;
Chris@191 863
Chris@1456 864 {
Chris@1456 865 QMutexLocker locker(&m_mutex);
Chris@1212 866
Chris@1456 867 if (m_completion != completion) {
Chris@1456 868 m_completion = completion;
Chris@147 869
Chris@1456 870 if (completion == 100) {
Chris@147 871
Chris@1456 872 if (m_notifyOnAdd) {
Chris@1456 873 emitCompletionChanged = false;
Chris@1456 874 }
Chris@1456 875
Chris@1456 876 m_notifyOnAdd = true; // henceforth
Chris@1456 877 m_rows.clear(); //!!! inefficient
Chris@1456 878 emitGeneralModelChanged = true;
Chris@1456 879
Chris@1456 880 } else if (!m_notifyOnAdd) {
Chris@1456 881
Chris@1456 882 if (update &&
Chris@1456 883 m_sinceLastNotifyMin >= 0 &&
Chris@1456 884 m_sinceLastNotifyMax >= 0) {
Chris@1456 885 m_rows.clear(); //!!! inefficient
Chris@1456 886 emitRegionChanged = true;
Chris@1456 887 }
Chris@297 888 }
Chris@1456 889 }
Chris@1456 890 }
Chris@297 891
Chris@1456 892 if (emitCompletionChanged) {
Chris@1456 893 emit completionChanged();
Chris@1456 894 }
Chris@1456 895 if (emitGeneralModelChanged) {
Chris@1456 896 emit modelChanged();
Chris@1456 897 }
Chris@1456 898 if (emitRegionChanged) {
Chris@1456 899 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
Chris@1456 900 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@147 901 }
Chris@147 902 }
Chris@147 903
Chris@147 904 template <typename PointType>
Chris@147 905 void
Chris@147 906 SparseModel<PointType>::toXml(QTextStream &out,
Chris@147 907 QString indent,
Chris@147 908 QString extraAttributes) const
Chris@147 909 {
Chris@777 910 // std::cerr << "SparseModel::toXml: extraAttributes = \""
Chris@777 911 // << extraAttributes.toStdString() << std::endl;
Chris@318 912
Chris@407 913 QString type = getXmlOutputType();
Chris@407 914
Chris@147 915 Model::toXml
Chris@1429 916 (out,
Chris@147 917 indent,
Chris@1429 918 QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6")
Chris@407 919 .arg(type)
Chris@1429 920 .arg(PointType(0).getDimensions())
Chris@1429 921 .arg(m_resolution)
Chris@1429 922 .arg(m_notifyOnAdd ? "true" : "false")
Chris@1429 923 .arg(getObjectExportId(&m_points))
Chris@1429 924 .arg(extraAttributes));
Chris@147 925
Chris@147 926 out << indent;
Chris@147 927 out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n")
Chris@1429 928 .arg(getObjectExportId(&m_points))
Chris@1429 929 .arg(PointType(0).getDimensions());
Chris@147 930
Chris@608 931 for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@314 932 i->toXml(out, indent + " ");
Chris@147 933 }
Chris@147 934
Chris@147 935 out << indent;
Chris@147 936 out << "</dataset>\n";
Chris@147 937 }
Chris@147 938
Chris@147 939 template <typename PointType>
Chris@147 940 SparseModel<PointType>::EditCommand::EditCommand(SparseModel *model,
Chris@147 941 QString commandName) :
Chris@147 942 MacroCommand(commandName),
Chris@147 943 m_model(model)
Chris@147 944 {
Chris@147 945 }
Chris@147 946
Chris@147 947 template <typename PointType>
Chris@147 948 void
Chris@147 949 SparseModel<PointType>::EditCommand::addPoint(const PointType &point)
Chris@147 950 {
Chris@147 951 addCommand(new AddPointCommand(m_model, point), true);
Chris@147 952 }
Chris@147 953
Chris@147 954 template <typename PointType>
Chris@147 955 void
Chris@147 956 SparseModel<PointType>::EditCommand::deletePoint(const PointType &point)
Chris@147 957 {
Chris@147 958 addCommand(new DeletePointCommand(m_model, point), true);
Chris@147 959 }
Chris@147 960
Chris@147 961 template <typename PointType>
Chris@416 962 typename SparseModel<PointType>::EditCommand *
Chris@147 963 SparseModel<PointType>::EditCommand::finish()
Chris@147 964 {
Chris@147 965 if (!m_commands.empty()) {
Chris@387 966 return this;
Chris@147 967 } else {
Chris@147 968 delete this;
Chris@389 969 return 0;
Chris@147 970 }
Chris@147 971 }
Chris@147 972
Chris@147 973 template <typename PointType>
Chris@147 974 void
Chris@147 975 SparseModel<PointType>::EditCommand::addCommand(Command *command,
Chris@1429 976 bool executeFirst)
Chris@147 977 {
Chris@147 978 if (executeFirst) command->execute();
Chris@147 979
Chris@147 980 if (!m_commands.empty()) {
Chris@1429 981 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
Chris@1429 982 if (dpc) {
Chris@1429 983 AddPointCommand *apc = dynamic_cast<AddPointCommand *>
Chris@1429 984 (m_commands[m_commands.size() - 1]);
Chris@1429 985 typename PointType::Comparator comparator;
Chris@1429 986 if (apc) {
Chris@1429 987 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
Chris@1429 988 !comparator(dpc->getPoint(), apc->getPoint())) {
Chris@1429 989 deleteCommand(apc);
Chris@1429 990 return;
Chris@1429 991 }
Chris@1429 992 }
Chris@1429 993 }
Chris@147 994 }
Chris@147 995
Chris@147 996 MacroCommand::addCommand(command);
Chris@147 997 }
Chris@147 998
Chris@147 999
Chris@147 1000 #endif
Chris@147 1001
Chris@147 1002
Chris@147 1003