annotate data/model/NoteModel.h @ 1644:513192aa9b03 single-point

Further API updates
author Chris Cannam
date Wed, 13 Mar 2019 16:00:13 +0000
parents 7a23dfe65d66
children b429750e64a8
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@1495 16 #ifndef SV_NOTE_MODEL_H
Chris@1495 17 #define SV_NOTE_MODEL_H
Chris@147 18
Chris@1643 19 #include "Model.h"
Chris@1643 20 #include "TabularModel.h"
Chris@1643 21 #include "base/UnitDatabase.h"
Chris@1643 22 #include "base/EventSeries.h"
Chris@1615 23 #include "base/NoteData.h"
Chris@1643 24 #include "base/NoteExportable.h"
Chris@391 25 #include "base/RealTime.h"
Chris@150 26 #include "base/PlayParameterRepository.h"
Chris@852 27 #include "base/Pitch.h"
Chris@1643 28 #include "system/System.h"
Chris@147 29
Chris@1643 30 #include <QMutex>
Chris@1643 31 #include <QMutexLocker>
Chris@441 32
Chris@1643 33 class NoteModel : public Model,
Chris@1643 34 public TabularModel,
Chris@1643 35 public NoteExportable
Chris@147 36 {
Chris@423 37 Q_OBJECT
Chris@423 38
Chris@147 39 public:
Chris@1644 40 NoteModel(sv_samplerate_t sampleRate,
Chris@1644 41 int resolution,
Chris@1429 42 bool notifyOnAdd = true) :
Chris@1643 43 m_sampleRate(sampleRate),
Chris@1643 44 m_resolution(resolution),
Chris@1643 45 m_valueMinimum(0.f),
Chris@1643 46 m_valueMaximum(0.f),
Chris@1643 47 m_haveExtents(false),
Chris@1643 48 m_valueQuantization(0),
Chris@1643 49 m_units(""),
Chris@1643 50 m_extendTo(0),
Chris@1643 51 m_notifyOnAdd(notifyOnAdd),
Chris@1643 52 m_sinceLastNotifyMin(-1),
Chris@1643 53 m_sinceLastNotifyMax(-1),
Chris@1643 54 m_completion(0) {
Chris@1429 55 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@256 56 }
Chris@256 57
Chris@1040 58 NoteModel(sv_samplerate_t sampleRate, int resolution,
Chris@1429 59 float valueMinimum, float valueMaximum,
Chris@1429 60 bool notifyOnAdd = true) :
Chris@1643 61 m_sampleRate(sampleRate),
Chris@1643 62 m_resolution(resolution),
Chris@1643 63 m_valueMinimum(valueMinimum),
Chris@1643 64 m_valueMaximum(valueMaximum),
Chris@1643 65 m_haveExtents(true),
Chris@1643 66 m_valueQuantization(0),
Chris@1643 67 m_units(""),
Chris@1643 68 m_extendTo(0),
Chris@1643 69 m_notifyOnAdd(notifyOnAdd),
Chris@1643 70 m_sinceLastNotifyMin(-1),
Chris@1643 71 m_sinceLastNotifyMax(-1),
Chris@1643 72 m_completion(0) {
Chris@1429 73 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@391 74 }
Chris@391 75
Chris@1643 76 virtual ~NoteModel() {
Chris@391 77 PlayParameterRepository::getInstance()->removePlayable(this);
Chris@147 78 }
Chris@1643 79
Chris@1643 80 QString getTypeName() const override { return tr("Note"); }
Chris@1643 81
Chris@1643 82 bool isOK() const override { return true; }
Chris@1643 83 sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); }
Chris@1643 84 sv_frame_t getEndFrame() const override { return m_events.getEndFrame(); }
Chris@1643 85 sv_samplerate_t getSampleRate() const override { return m_sampleRate; }
Chris@1644 86 int getResolution() const { return m_resolution; }
Chris@1643 87
Chris@1643 88 bool canPlay() const override { return true; }
Chris@1643 89 QString getDefaultPlayClipId() const override {
Chris@1643 90 return "elecpiano";
Chris@1643 91 }
Chris@1643 92
Chris@1643 93 QString getScaleUnits() const { return m_units; }
Chris@1643 94 void setScaleUnits(QString units) {
Chris@1643 95 m_units = units;
Chris@1643 96 UnitDatabase::getInstance()->registerUnit(units);
Chris@1643 97 }
Chris@147 98
Chris@147 99 float getValueQuantization() const { return m_valueQuantization; }
Chris@147 100 void setValueQuantization(float q) { m_valueQuantization = q; }
Chris@147 101
Chris@1643 102 float getValueMinimum() const { return m_valueMinimum; }
Chris@1643 103 float getValueMaximum() const { return m_valueMaximum; }
Chris@1643 104
Chris@1643 105 int getCompletion() const { return m_completion; }
Chris@345 106
Chris@1643 107 void setCompletion(int completion, bool update = true) {
Chris@391 108
Chris@1643 109 bool emitCompletionChanged = true;
Chris@1643 110 bool emitGeneralModelChanged = false;
Chris@1643 111 bool emitRegionChanged = false;
Chris@1643 112
Chris@1643 113 {
Chris@1643 114 QMutexLocker locker(&m_mutex);
Chris@1643 115
Chris@1643 116 if (m_completion != completion) {
Chris@1643 117 m_completion = completion;
Chris@1643 118
Chris@1643 119 if (completion == 100) {
Chris@1643 120
Chris@1643 121 if (m_notifyOnAdd) {
Chris@1643 122 emitCompletionChanged = false;
Chris@1643 123 }
Chris@1643 124
Chris@1643 125 m_notifyOnAdd = true; // henceforth
Chris@1643 126 emitGeneralModelChanged = true;
Chris@1643 127
Chris@1643 128 } else if (!m_notifyOnAdd) {
Chris@1643 129
Chris@1643 130 if (update &&
Chris@1643 131 m_sinceLastNotifyMin >= 0 &&
Chris@1643 132 m_sinceLastNotifyMax >= 0) {
Chris@1643 133 emitRegionChanged = true;
Chris@1643 134 }
Chris@1643 135 }
Chris@1643 136 }
Chris@1643 137 }
Chris@1643 138
Chris@1643 139 if (emitCompletionChanged) {
Chris@1643 140 emit completionChanged();
Chris@1643 141 }
Chris@1643 142 if (emitGeneralModelChanged) {
Chris@1643 143 emit modelChanged();
Chris@1643 144 }
Chris@1643 145 if (emitRegionChanged) {
Chris@1643 146 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
Chris@1643 147 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@1643 148 }
Chris@391 149 }
Chris@1644 150
Chris@1644 151 /**
Chris@1644 152 * Query methods.
Chris@1644 153 */
Chris@1644 154
Chris@1644 155 int getEventCount() const {
Chris@1644 156 return m_events.count();
Chris@1644 157 }
Chris@1644 158 bool isEmpty() const {
Chris@1644 159 return m_events.isEmpty();
Chris@1644 160 }
Chris@1644 161 bool containsEvent(const Event &e) const {
Chris@1644 162 return m_events.contains(e);
Chris@1644 163 }
Chris@1644 164 EventVector getAllEvents() const {
Chris@1644 165 return m_events.getAllEvents();
Chris@1644 166 }
Chris@1644 167 EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const {
Chris@1644 168 return m_events.getEventsSpanning(f, duration);
Chris@1644 169 }
Chris@1644 170 EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration) const {
Chris@1644 171 return m_events.getEventsWithin(f, duration);
Chris@1644 172 }
Chris@1644 173 EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const {
Chris@1644 174 return m_events.getEventsStartingWithin(f, duration);
Chris@1644 175 }
Chris@1644 176 EventVector getEventsCovering(sv_frame_t f) const {
Chris@1644 177 return m_events.getEventsCovering(f);
Chris@1644 178 }
Chris@1644 179
Chris@1644 180 /**
Chris@1644 181 * Editing commands and methods.
Chris@1644 182 */
Chris@1643 183
Chris@1644 184 class EditCommand : public Command
Chris@1644 185 {
Chris@1644 186 public:
Chris@1644 187 //!!! borrowed ptr
Chris@1644 188 EditCommand(NoteModel *model, QString name) :
Chris@1644 189 m_model(model), m_name(name) { }
Chris@391 190
Chris@1644 191 QString getName() const override {
Chris@1644 192 return m_name;
Chris@1644 193 }
Chris@1644 194
Chris@1644 195 void add(Event e) {
Chris@1644 196 m_add.insert(e);
Chris@1644 197 }
Chris@1644 198
Chris@1644 199 void remove(Event e) {
Chris@1644 200 m_remove.insert(e);
Chris@1644 201 }
Chris@1643 202
Chris@1644 203 void execute() override {
Chris@1644 204 for (const Event &e: m_add) m_model->add(e);
Chris@1644 205 for (const Event &e: m_remove) m_model->remove(e);
Chris@1644 206 }
Chris@1643 207
Chris@1644 208 void unexecute() override {
Chris@1644 209 for (const Event &e: m_remove) m_model->add(e);
Chris@1644 210 for (const Event &e: m_add) m_model->remove(e);
Chris@1644 211 }
Chris@1644 212
Chris@1644 213 EditCommand *finish() {
Chris@1644 214 if (m_add.empty() && m_remove.empty()) {
Chris@1644 215 delete this;
Chris@1644 216 return nullptr;
Chris@1644 217 } else {
Chris@1644 218 return this;
Chris@1644 219 }
Chris@1644 220 }
Chris@1644 221
Chris@1644 222 private:
Chris@1644 223 NoteModel *m_model;
Chris@1644 224 std::set<Event> m_add;
Chris@1644 225 std::set<Event> m_remove;
Chris@1644 226 QString m_name;
Chris@1644 227 };
Chris@1644 228
Chris@1644 229 void add(Event e) {
Chris@1644 230
Chris@1644 231 bool allChange = false;
Chris@1644 232
Chris@1644 233 {
Chris@1644 234 QMutexLocker locker(&m_mutex);
Chris@1644 235 m_events.add(e);
Chris@1644 236 //!!!??? if (point.getLabel() != "") m_hasTextLabels = true;
Chris@1644 237
Chris@1644 238 float v = e.getValue();
Chris@1644 239 if (!ISNAN(v) && !ISINF(v)) {
Chris@1644 240 if (!m_haveExtents || v < m_valueMinimum) {
Chris@1644 241 m_valueMinimum = v; allChange = true;
Chris@1644 242 }
Chris@1644 243 if (!m_haveExtents || v > m_valueMaximum) {
Chris@1644 244 m_valueMaximum = v; allChange = true;
Chris@1644 245 }
Chris@1644 246 m_haveExtents = true;
Chris@1644 247 }
Chris@1644 248
Chris@1644 249 sv_frame_t f = e.getFrame();
Chris@1644 250
Chris@1644 251 if (!m_notifyOnAdd) {
Chris@1644 252 if (m_sinceLastNotifyMin == -1 || f < m_sinceLastNotifyMin) {
Chris@1644 253 m_sinceLastNotifyMin = f;
Chris@1644 254 }
Chris@1644 255 if (m_sinceLastNotifyMax == -1 || f > m_sinceLastNotifyMax) {
Chris@1644 256 m_sinceLastNotifyMax = f;
Chris@1644 257 }
Chris@1644 258 }
Chris@1644 259 }
Chris@1644 260
Chris@1644 261 if (m_notifyOnAdd) {
Chris@1644 262 emit modelChangedWithin(e.getFrame(),
Chris@1644 263 e.getFrame() + e.getDuration() + m_resolution);
Chris@1644 264 }
Chris@1644 265 if (allChange) {
Chris@1644 266 emit modelChanged();
Chris@1644 267 }
Chris@1644 268 }
Chris@1644 269
Chris@1644 270 void remove(Event e) {
Chris@1644 271 {
Chris@1644 272 QMutexLocker locker(&m_mutex);
Chris@1644 273 m_events.remove(e);
Chris@1644 274 }
Chris@1644 275 emit modelChangedWithin(e.getFrame(),
Chris@1644 276 e.getFrame() + e.getDuration() + m_resolution);
Chris@147 277 }
Chris@147 278
Chris@424 279 /**
Chris@424 280 * TabularModel methods.
Chris@424 281 */
Chris@1643 282
Chris@1643 283 int getRowCount() const override {
Chris@1643 284 return m_events.count();
Chris@1643 285 }
Chris@424 286
Chris@1643 287 int getColumnCount() const override {
Chris@424 288 return 6;
Chris@424 289 }
Chris@424 290
Chris@1643 291 bool isColumnTimeValue(int column) const override {
Chris@1643 292 // NB duration is not a "time value" -- that's for columns
Chris@1643 293 // whose sort ordering is exactly that of the frame time
Chris@1643 294 return (column < 2);
Chris@1643 295 }
Chris@1643 296
Chris@1643 297 sv_frame_t getFrameForRow(int row) const override {
Chris@1643 298 if (row < 0 || row >= m_events.count()) {
Chris@1643 299 return 0;
Chris@1643 300 }
Chris@1643 301 Event e = m_events.getEventByIndex(row);
Chris@1643 302 return e.getFrame();
Chris@1643 303 }
Chris@1643 304
Chris@1643 305 int getRowForFrame(sv_frame_t frame) const override {
Chris@1643 306 return m_events.getIndexForEvent(Event(frame));
Chris@1643 307 }
Chris@1643 308
Chris@1643 309 QString getHeading(int column) const override {
Chris@424 310 switch (column) {
Chris@424 311 case 0: return tr("Time");
Chris@424 312 case 1: return tr("Frame");
Chris@424 313 case 2: return tr("Pitch");
Chris@424 314 case 3: return tr("Duration");
Chris@424 315 case 4: return tr("Level");
Chris@424 316 case 5: return tr("Label");
Chris@424 317 default: return tr("Unknown");
Chris@424 318 }
Chris@424 319 }
Chris@424 320
Chris@1643 321 QVariant getData(int row, int column, int role) const override {
Chris@1643 322
Chris@1643 323 if (row < 0 || row >= m_events.count()) {
Chris@1643 324 return QVariant();
Chris@425 325 }
Chris@425 326
Chris@1643 327 Event e = m_events.getEventByIndex(row);
Chris@424 328
Chris@424 329 switch (column) {
Chris@1643 330 case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role);
Chris@1643 331 case 1: return int(e.getFrame());
Chris@1643 332 case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role);
Chris@1643 333 case 3: return int(e.getDuration());
Chris@1643 334 case 4: return e.getLevel();
Chris@1643 335 case 5: return e.getLabel();
Chris@424 336 default: return QVariant();
Chris@424 337 }
Chris@424 338 }
Chris@424 339
Chris@1580 340 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override
Chris@424 341 {
Chris@1643 342 if (row < 0 || row >= m_events.count()) return nullptr;
Chris@1643 343 if (role != Qt::EditRole) return nullptr;
Chris@1643 344
Chris@1643 345 Event e0 = m_events.getEventByIndex(row);
Chris@1643 346 Event e1;
Chris@1643 347
Chris@1643 348 switch (column) {
Chris@1643 349 case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() *
Chris@1643 350 getSampleRate()))); break;
Chris@1643 351 case 1: e1 = e0.withFrame(value.toInt()); break;
Chris@1643 352 case 2: e1 = e0.withValue(float(value.toDouble())); break;
Chris@1643 353 case 3: e1 = e0.withDuration(value.toInt()); break;
Chris@1643 354 case 4: e1 = e0.withLevel(float(value.toDouble())); break;
Chris@1643 355 case 5: e1 = e0.withLabel(value.toString()); break;
Chris@425 356 }
Chris@425 357
Chris@424 358 EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@1644 359 command->remove(e0);
Chris@1644 360 command->add(e1);
Chris@1644 361 return command->finish();
Chris@424 362 }
Chris@424 363
Chris@1580 364 SortType getSortType(int column) const override
Chris@424 365 {
Chris@424 366 if (column == 5) return SortAlphabetical;
Chris@424 367 return SortNumeric;
Chris@424 368 }
Chris@424 369
Chris@852 370 /**
Chris@852 371 * NoteExportable methods.
Chris@852 372 */
Chris@852 373
Chris@1580 374 NoteList getNotes() const override {
Chris@1643 375 return getNotesStartingWithin(getStartFrame(),
Chris@1643 376 getEndFrame() - getStartFrame());
Chris@852 377 }
Chris@852 378
Chris@1643 379 NoteList getNotesActiveAt(sv_frame_t frame) const override {
Chris@1643 380
Chris@852 381 NoteList notes;
Chris@1643 382 EventVector ee = m_events.getEventsCovering(frame);
Chris@1643 383 for (const auto &e: ee) {
Chris@1643 384 notes.push_back(e.toNoteData(getSampleRate(),
Chris@1643 385 getScaleUnits() != "Hz"));
Chris@1643 386 }
Chris@1643 387 return notes;
Chris@1643 388 }
Chris@1643 389
Chris@1643 390 NoteList getNotesStartingWithin(sv_frame_t startFrame,
Chris@1643 391 sv_frame_t duration) const override {
Chris@852 392
Chris@1643 393 NoteList notes;
Chris@1643 394 EventVector ee = m_events.getEventsStartingWithin(startFrame, duration);
Chris@1643 395 for (const auto &e: ee) {
Chris@1643 396 notes.push_back(e.toNoteData(getSampleRate(),
Chris@1643 397 getScaleUnits() != "Hz"));
Chris@852 398 }
Chris@852 399 return notes;
Chris@852 400 }
Chris@852 401
Chris@1644 402 /**
Chris@1644 403 * XmlExportable methods.
Chris@1644 404 */
Chris@1644 405
Chris@1644 406 void toXml(QTextStream &out,
Chris@1644 407 QString indent = "",
Chris@1644 408 QString extraAttributes = "") const override {
Chris@1644 409
Chris@1644 410 //!!! what is valueQuantization used for?
Chris@1644 411
Chris@1644 412 Model::toXml
Chris@1644 413 (out,
Chris@1644 414 indent,
Chris@1644 415 QString("type=\"sparse\" dimensions=\"3\" resolution=\"%1\" "
Chris@1644 416 "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"note\" "
Chris@1644 417 "valueQuantization=\"%4\" minimum=\"%5\" maximum=\"%6\" "
Chris@1644 418 "units=\"%7\" %8")
Chris@1644 419 .arg(m_resolution)
Chris@1644 420 .arg(m_notifyOnAdd ? "true" : "false")
Chris@1644 421 .arg(getObjectExportId(&m_events))
Chris@1644 422 .arg(m_valueQuantization)
Chris@1644 423 .arg(m_valueMinimum)
Chris@1644 424 .arg(m_valueMaximum)
Chris@1644 425 .arg(m_units)
Chris@1644 426 .arg(extraAttributes));
Chris@1644 427
Chris@1644 428 m_events.toXml(out, indent, QString("dimensions=\"3\""));
Chris@1644 429 }
Chris@1644 430
Chris@147 431 protected:
Chris@1643 432 sv_samplerate_t m_sampleRate;
Chris@1643 433 int m_resolution;
Chris@1643 434
Chris@1643 435 float m_valueMinimum;
Chris@1643 436 float m_valueMaximum;
Chris@1643 437 bool m_haveExtents;
Chris@147 438 float m_valueQuantization;
Chris@1643 439 QString m_units;
Chris@1643 440
Chris@1643 441 sv_frame_t m_extendTo;
Chris@1643 442
Chris@1643 443 bool m_notifyOnAdd;
Chris@1643 444 sv_frame_t m_sinceLastNotifyMin;
Chris@1643 445 sv_frame_t m_sinceLastNotifyMax;
Chris@1643 446
Chris@1643 447 EventSeries m_events;
Chris@1643 448
Chris@1643 449 int m_completion;
Chris@1643 450
Chris@1643 451 mutable QMutex m_mutex;
Chris@1643 452
Chris@1643 453 //!!! do we have general docs for ownership and synchronisation of models?
Chris@1643 454 // this might be a good opportunity to stop using bare pointers to them
Chris@147 455 };
Chris@147 456
Chris@147 457 #endif