annotate data/model/SparseModel.h @ 1196:c7b9c902642f spectrogram-minor-refactor

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