annotate data/model/SparseModel.h @ 1288:5ef9b4d4bbdb 3.0-integration

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