annotate data/model/NoteModel.h @ 1646:b429750e64a8 single-point

Command tweaks, including executing it!
author Chris Cannam
date Thu, 14 Mar 2019 13:11:47 +0000
parents 513192aa9b03
children 29a20719796e
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@1646 195 void setName(QString name) {
Chris@1646 196 m_name = name;
Chris@1646 197 }
Chris@1646 198
Chris@1644 199 void add(Event e) {
Chris@1644 200 m_add.insert(e);
Chris@1644 201 }
Chris@1644 202
Chris@1644 203 void remove(Event e) {
Chris@1644 204 m_remove.insert(e);
Chris@1644 205 }
Chris@1643 206
Chris@1644 207 void execute() override {
Chris@1646 208 for (const Event &e: m_add) {
Chris@1646 209 m_model->add(e);
Chris@1646 210 }
Chris@1646 211 for (const Event &e: m_remove) {
Chris@1646 212 m_model->remove(e);
Chris@1646 213 }
Chris@1644 214 }
Chris@1643 215
Chris@1644 216 void unexecute() override {
Chris@1646 217 for (const Event &e: m_remove) {
Chris@1646 218 m_model->add(e);
Chris@1646 219 }
Chris@1646 220 for (const Event &e: m_add) {
Chris@1646 221 m_model->remove(e);
Chris@1646 222 }
Chris@1644 223 }
Chris@1644 224
Chris@1644 225 EditCommand *finish() {
Chris@1644 226 if (m_add.empty() && m_remove.empty()) {
Chris@1644 227 delete this;
Chris@1644 228 return nullptr;
Chris@1644 229 } else {
Chris@1646 230 execute();
Chris@1644 231 return this;
Chris@1644 232 }
Chris@1644 233 }
Chris@1644 234
Chris@1644 235 private:
Chris@1644 236 NoteModel *m_model;
Chris@1644 237 std::set<Event> m_add;
Chris@1644 238 std::set<Event> m_remove;
Chris@1644 239 QString m_name;
Chris@1644 240 };
Chris@1644 241
Chris@1644 242 void add(Event e) {
Chris@1644 243
Chris@1644 244 bool allChange = false;
Chris@1644 245
Chris@1644 246 {
Chris@1644 247 QMutexLocker locker(&m_mutex);
Chris@1644 248 m_events.add(e);
Chris@1644 249 //!!!??? if (point.getLabel() != "") m_hasTextLabels = true;
Chris@1644 250
Chris@1644 251 float v = e.getValue();
Chris@1644 252 if (!ISNAN(v) && !ISINF(v)) {
Chris@1644 253 if (!m_haveExtents || v < m_valueMinimum) {
Chris@1644 254 m_valueMinimum = v; allChange = true;
Chris@1644 255 }
Chris@1644 256 if (!m_haveExtents || v > m_valueMaximum) {
Chris@1644 257 m_valueMaximum = v; allChange = true;
Chris@1644 258 }
Chris@1644 259 m_haveExtents = true;
Chris@1644 260 }
Chris@1644 261
Chris@1644 262 sv_frame_t f = e.getFrame();
Chris@1644 263
Chris@1644 264 if (!m_notifyOnAdd) {
Chris@1644 265 if (m_sinceLastNotifyMin == -1 || f < m_sinceLastNotifyMin) {
Chris@1644 266 m_sinceLastNotifyMin = f;
Chris@1644 267 }
Chris@1644 268 if (m_sinceLastNotifyMax == -1 || f > m_sinceLastNotifyMax) {
Chris@1644 269 m_sinceLastNotifyMax = f;
Chris@1644 270 }
Chris@1644 271 }
Chris@1644 272 }
Chris@1644 273
Chris@1644 274 if (m_notifyOnAdd) {
Chris@1644 275 emit modelChangedWithin(e.getFrame(),
Chris@1644 276 e.getFrame() + e.getDuration() + m_resolution);
Chris@1644 277 }
Chris@1644 278 if (allChange) {
Chris@1644 279 emit modelChanged();
Chris@1644 280 }
Chris@1644 281 }
Chris@1644 282
Chris@1644 283 void remove(Event e) {
Chris@1644 284 {
Chris@1644 285 QMutexLocker locker(&m_mutex);
Chris@1644 286 m_events.remove(e);
Chris@1644 287 }
Chris@1644 288 emit modelChangedWithin(e.getFrame(),
Chris@1644 289 e.getFrame() + e.getDuration() + m_resolution);
Chris@147 290 }
Chris@147 291
Chris@424 292 /**
Chris@424 293 * TabularModel methods.
Chris@424 294 */
Chris@1643 295
Chris@1643 296 int getRowCount() const override {
Chris@1643 297 return m_events.count();
Chris@1643 298 }
Chris@424 299
Chris@1643 300 int getColumnCount() const override {
Chris@424 301 return 6;
Chris@424 302 }
Chris@424 303
Chris@1643 304 bool isColumnTimeValue(int column) const override {
Chris@1643 305 // NB duration is not a "time value" -- that's for columns
Chris@1643 306 // whose sort ordering is exactly that of the frame time
Chris@1643 307 return (column < 2);
Chris@1643 308 }
Chris@1643 309
Chris@1643 310 sv_frame_t getFrameForRow(int row) const override {
Chris@1643 311 if (row < 0 || row >= m_events.count()) {
Chris@1643 312 return 0;
Chris@1643 313 }
Chris@1643 314 Event e = m_events.getEventByIndex(row);
Chris@1643 315 return e.getFrame();
Chris@1643 316 }
Chris@1643 317
Chris@1643 318 int getRowForFrame(sv_frame_t frame) const override {
Chris@1643 319 return m_events.getIndexForEvent(Event(frame));
Chris@1643 320 }
Chris@1643 321
Chris@1643 322 QString getHeading(int column) const override {
Chris@424 323 switch (column) {
Chris@424 324 case 0: return tr("Time");
Chris@424 325 case 1: return tr("Frame");
Chris@424 326 case 2: return tr("Pitch");
Chris@424 327 case 3: return tr("Duration");
Chris@424 328 case 4: return tr("Level");
Chris@424 329 case 5: return tr("Label");
Chris@424 330 default: return tr("Unknown");
Chris@424 331 }
Chris@424 332 }
Chris@424 333
Chris@1643 334 QVariant getData(int row, int column, int role) const override {
Chris@1643 335
Chris@1643 336 if (row < 0 || row >= m_events.count()) {
Chris@1643 337 return QVariant();
Chris@425 338 }
Chris@425 339
Chris@1643 340 Event e = m_events.getEventByIndex(row);
Chris@424 341
Chris@424 342 switch (column) {
Chris@1643 343 case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role);
Chris@1643 344 case 1: return int(e.getFrame());
Chris@1643 345 case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role);
Chris@1643 346 case 3: return int(e.getDuration());
Chris@1643 347 case 4: return e.getLevel();
Chris@1643 348 case 5: return e.getLabel();
Chris@424 349 default: return QVariant();
Chris@424 350 }
Chris@424 351 }
Chris@424 352
Chris@1580 353 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override
Chris@424 354 {
Chris@1643 355 if (row < 0 || row >= m_events.count()) return nullptr;
Chris@1643 356 if (role != Qt::EditRole) return nullptr;
Chris@1643 357
Chris@1643 358 Event e0 = m_events.getEventByIndex(row);
Chris@1643 359 Event e1;
Chris@1643 360
Chris@1643 361 switch (column) {
Chris@1643 362 case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() *
Chris@1643 363 getSampleRate()))); break;
Chris@1643 364 case 1: e1 = e0.withFrame(value.toInt()); break;
Chris@1643 365 case 2: e1 = e0.withValue(float(value.toDouble())); break;
Chris@1643 366 case 3: e1 = e0.withDuration(value.toInt()); break;
Chris@1643 367 case 4: e1 = e0.withLevel(float(value.toDouble())); break;
Chris@1643 368 case 5: e1 = e0.withLabel(value.toString()); break;
Chris@425 369 }
Chris@425 370
Chris@424 371 EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@1644 372 command->remove(e0);
Chris@1644 373 command->add(e1);
Chris@1644 374 return command->finish();
Chris@424 375 }
Chris@424 376
Chris@1580 377 SortType getSortType(int column) const override
Chris@424 378 {
Chris@424 379 if (column == 5) return SortAlphabetical;
Chris@424 380 return SortNumeric;
Chris@424 381 }
Chris@424 382
Chris@852 383 /**
Chris@852 384 * NoteExportable methods.
Chris@852 385 */
Chris@852 386
Chris@1580 387 NoteList getNotes() const override {
Chris@1643 388 return getNotesStartingWithin(getStartFrame(),
Chris@1643 389 getEndFrame() - getStartFrame());
Chris@852 390 }
Chris@852 391
Chris@1643 392 NoteList getNotesActiveAt(sv_frame_t frame) const override {
Chris@1643 393
Chris@852 394 NoteList notes;
Chris@1643 395 EventVector ee = m_events.getEventsCovering(frame);
Chris@1643 396 for (const auto &e: ee) {
Chris@1643 397 notes.push_back(e.toNoteData(getSampleRate(),
Chris@1643 398 getScaleUnits() != "Hz"));
Chris@1643 399 }
Chris@1643 400 return notes;
Chris@1643 401 }
Chris@1643 402
Chris@1643 403 NoteList getNotesStartingWithin(sv_frame_t startFrame,
Chris@1643 404 sv_frame_t duration) const override {
Chris@852 405
Chris@1643 406 NoteList notes;
Chris@1643 407 EventVector ee = m_events.getEventsStartingWithin(startFrame, duration);
Chris@1643 408 for (const auto &e: ee) {
Chris@1643 409 notes.push_back(e.toNoteData(getSampleRate(),
Chris@1643 410 getScaleUnits() != "Hz"));
Chris@852 411 }
Chris@852 412 return notes;
Chris@852 413 }
Chris@852 414
Chris@1644 415 /**
Chris@1644 416 * XmlExportable methods.
Chris@1644 417 */
Chris@1644 418
Chris@1644 419 void toXml(QTextStream &out,
Chris@1644 420 QString indent = "",
Chris@1644 421 QString extraAttributes = "") const override {
Chris@1644 422
Chris@1644 423 //!!! what is valueQuantization used for?
Chris@1644 424
Chris@1644 425 Model::toXml
Chris@1644 426 (out,
Chris@1644 427 indent,
Chris@1644 428 QString("type=\"sparse\" dimensions=\"3\" resolution=\"%1\" "
Chris@1644 429 "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"note\" "
Chris@1644 430 "valueQuantization=\"%4\" minimum=\"%5\" maximum=\"%6\" "
Chris@1644 431 "units=\"%7\" %8")
Chris@1644 432 .arg(m_resolution)
Chris@1644 433 .arg(m_notifyOnAdd ? "true" : "false")
Chris@1644 434 .arg(getObjectExportId(&m_events))
Chris@1644 435 .arg(m_valueQuantization)
Chris@1644 436 .arg(m_valueMinimum)
Chris@1644 437 .arg(m_valueMaximum)
Chris@1644 438 .arg(m_units)
Chris@1644 439 .arg(extraAttributes));
Chris@1644 440
Chris@1644 441 m_events.toXml(out, indent, QString("dimensions=\"3\""));
Chris@1644 442 }
Chris@1644 443
Chris@147 444 protected:
Chris@1643 445 sv_samplerate_t m_sampleRate;
Chris@1643 446 int m_resolution;
Chris@1643 447
Chris@1643 448 float m_valueMinimum;
Chris@1643 449 float m_valueMaximum;
Chris@1643 450 bool m_haveExtents;
Chris@147 451 float m_valueQuantization;
Chris@1643 452 QString m_units;
Chris@1643 453
Chris@1643 454 sv_frame_t m_extendTo;
Chris@1643 455
Chris@1643 456 bool m_notifyOnAdd;
Chris@1643 457 sv_frame_t m_sinceLastNotifyMin;
Chris@1643 458 sv_frame_t m_sinceLastNotifyMax;
Chris@1643 459
Chris@1643 460 EventSeries m_events;
Chris@1643 461
Chris@1643 462 int m_completion;
Chris@1643 463
Chris@1643 464 mutable QMutex m_mutex;
Chris@1643 465
Chris@1643 466 //!!! do we have general docs for ownership and synchronisation of models?
Chris@1643 467 // this might be a good opportunity to stop using bare pointers to them
Chris@147 468 };
Chris@147 469
Chris@147 470 #endif