NoteModel.h
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2 of the
11  License, or (at your option) any later version. See the file
12  COPYING included with this distribution for more information.
13 */
14 
15 #ifndef SV_NOTE_MODEL_H
16 #define SV_NOTE_MODEL_H
17 
18 #include "Model.h"
19 #include "TabularModel.h"
20 #include "EventCommands.h"
21 #include "DeferredNotifier.h"
22 #include "base/UnitDatabase.h"
23 #include "base/EventSeries.h"
24 #include "base/NoteData.h"
25 #include "base/NoteExportable.h"
26 #include "base/RealTime.h"
28 #include "base/Pitch.h"
29 #include "system/System.h"
30 
31 #include <QMutexLocker>
32 
33 class NoteModel : public Model,
34  public TabularModel,
35  public NoteExportable,
36  public EventEditable
37 {
38  Q_OBJECT
39 
40 public:
41  enum Subtype {
44  };
45 
47  int resolution,
48  bool notifyOnAdd = true,
49  Subtype subtype = NORMAL_NOTE) :
50  m_subtype(subtype),
51  m_sampleRate(sampleRate),
52  m_resolution(resolution),
53  m_valueMinimum(0.f),
54  m_valueMaximum(0.f),
55  m_haveExtents(false),
57  m_units(""),
58  m_notifier(this,
59  getId(),
60  notifyOnAdd ?
61  DeferredNotifier::NOTIFY_ALWAYS :
62  DeferredNotifier::NOTIFY_DEFERRED),
63  m_completion(100) {
64  if (subtype == FLEXI_NOTE) {
65  m_valueMinimum = 33.f;
66  m_valueMaximum = 88.f;
67  }
69  (getId().untyped, this);
70  }
71 
72  NoteModel(sv_samplerate_t sampleRate, int resolution,
73  float valueMinimum, float valueMaximum,
74  bool notifyOnAdd = true,
75  Subtype subtype = NORMAL_NOTE) :
76  m_subtype(subtype),
77  m_sampleRate(sampleRate),
78  m_resolution(resolution),
79  m_valueMinimum(valueMinimum),
80  m_valueMaximum(valueMaximum),
81  m_haveExtents(true),
83  m_units(""),
84  m_notifier(this,
85  getId(),
86  notifyOnAdd ?
87  DeferredNotifier::NOTIFY_ALWAYS :
88  DeferredNotifier::NOTIFY_DEFERRED),
89  m_completion(100) {
91  (getId().untyped, this);
92  }
93 
94  virtual ~NoteModel() {
96  (getId().untyped);
97  }
98 
99  QString getTypeName() const override { return tr("Note"); }
100  Subtype getSubtype() const { return m_subtype; }
101  bool isSparse() const override { return true; }
102  bool isOK() const override { return true; }
103 
104  sv_frame_t getStartFrame() const override {
105  QMutexLocker locker(&m_mutex);
106  return m_events.getStartFrame();
107  }
108  sv_frame_t getTrueEndFrame() const override {
109  QMutexLocker locker(&m_mutex);
110  if (m_events.isEmpty()) return 0;
112  if (e % m_resolution == 0) return e;
113  else return (e / m_resolution + 1) * m_resolution;
114  }
115 
116  sv_samplerate_t getSampleRate() const override { return m_sampleRate; }
117  int getResolution() const { return m_resolution; }
118 
119  bool canPlay() const override { return true; }
120  QString getDefaultPlayClipId() const override {
121  return "elecpiano";
122  }
123 
124  QString getScaleUnits() const {
125  QMutexLocker locker(&m_mutex);
126  return m_units;
127  }
128  void setScaleUnits(QString units) {
129  QMutexLocker locker(&m_mutex);
130  m_units = units;
132  }
133 
134  float getValueQuantization() const { return m_valueQuantization; }
136 
137  float getValueMinimum() const { return m_valueMinimum; }
138  float getValueMaximum() const { return m_valueMaximum; }
139 
140  int getCompletion() const override { return m_completion; }
141 
142  void setCompletion(int completion, bool update = true) {
143 
144  {
145  if (m_completion == completion) return;
146  m_completion = completion;
147  }
148 
149  if (update) {
151  }
152 
153  emit completionChanged(getId());
154 
155  if (completion == 100) {
156  // henceforth:
158  emit modelChanged(getId());
159  }
160  }
161 
166  int getEventCount() const {
167  return m_events.count();
168  }
169  bool isEmpty() const {
170  return m_events.isEmpty();
171  }
172  bool containsEvent(const Event &e) const {
173  return m_events.contains(e);
174  }
176  return m_events.getAllEvents();
177  }
179  return m_events.getEventsSpanning(f, duration);
180  }
182  return m_events.getEventsCovering(f);
183  }
185  return m_events.getEventsWithin(f, duration);
186  }
188  return m_events.getEventsStartingWithin(f, duration);
189  }
191  return m_events.getEventsStartingAt(f);
192  }
194  std::function<bool(Event)> predicate,
195  EventSeries::Direction direction,
196  Event &found) const {
198  (startSearchAt, predicate, direction, found);
199  }
200  int getIndexForEvent(const Event &e) {
201  return m_events.getIndexForEvent(e);
202  }
203 
207  void add(Event e) override {
208 
209  bool allChange = false;
210 
211  m_events.add(e);
212  float v = e.getValue();
213  if (!ISNAN(v) && !ISINF(v)) {
214  if (!m_haveExtents || v < m_valueMinimum) {
215  m_valueMinimum = v; allChange = true;
216  }
217  if (!m_haveExtents || v > m_valueMaximum) {
218  m_valueMaximum = v; allChange = true;
219  }
220  m_haveExtents = true;
221  }
222 
224 
225  if (allChange) {
226  emit modelChanged(getId());
227  }
228  }
229 
230  void remove(Event e) override {
231  m_events.remove(e);
232  emit modelChangedWithin(getId(),
233  e.getFrame(),
234  e.getFrame() + e.getDuration() + m_resolution);
235  }
236 
241  int getRowCount() const override {
242  return m_events.count();
243  }
244 
245  int getColumnCount() const override {
246  return 6;
247  }
248 
249  bool isColumnTimeValue(int column) const override {
250  // NB duration is not a "time value" -- that's for columns
251  // whose sort ordering is exactly that of the frame time
252  return (column < 2);
253  }
254 
255  sv_frame_t getFrameForRow(int row) const override {
256  if (row < 0 || row >= m_events.count()) {
257  return 0;
258  }
259  Event e = m_events.getEventByIndex(row);
260  return e.getFrame();
261  }
262 
263  int getRowForFrame(sv_frame_t frame) const override {
264  return m_events.getIndexForEvent(Event(frame));
265  }
266 
267  QString getHeading(int column) const override {
268  switch (column) {
269  case 0: return tr("Time");
270  case 1: return tr("Frame");
271  case 2: return tr("Pitch");
272  case 3: return tr("Duration");
273  case 4: return tr("Level");
274  case 5: return tr("Label");
275  default: return tr("Unknown");
276  }
277  }
278 
279  QVariant getData(int row, int column, int role) const override {
280 
281  if (row < 0 || row >= m_events.count()) {
282  return QVariant();
283  }
284 
285  Event e = m_events.getEventByIndex(row);
286 
287  switch (column) {
288  case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role);
289  case 1: return int(e.getFrame());
290  case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role);
291  case 3: return int(e.getDuration());
292  case 4: return e.getLevel();
293  case 5: return e.getLabel();
294  default: return QVariant();
295  }
296  }
297 
298  Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override {
299 
300  if (row < 0 || row >= m_events.count()) return nullptr;
301  if (role != Qt::EditRole) return nullptr;
302 
303  Event e0 = m_events.getEventByIndex(row);
304  Event e1;
305 
306  switch (column) {
307  case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() *
308  getSampleRate()))); break;
309  case 1: e1 = e0.withFrame(value.toInt()); break;
310  case 2: e1 = e0.withValue(float(value.toDouble())); break;
311  case 3: e1 = e0.withDuration(value.toInt()); break;
312  case 4: e1 = e0.withLevel(float(value.toDouble())); break;
313  case 5: e1 = e0.withLabel(value.toString()); break;
314  }
315 
316  auto command = new ChangeEventsCommand(getId().untyped, tr("Edit Data"));
317  command->remove(e0);
318  command->add(e1);
319  return command->finish();
320  }
321 
322  SortType getSortType(int column) const override
323  {
324  if (column == 5) return SortAlphabetical;
325  return SortNumeric;
326  }
327 
328  bool isEditable() const override { return true; }
329 
330  Command *getInsertRowCommand(int row) override {
331  if (row < 0 || row >= m_events.count()) return nullptr;
332  auto command = new ChangeEventsCommand(getId().untyped,
333  tr("Add Note"));
334  Event e = m_events.getEventByIndex(row);
335  command->add(e);
336  return command->finish();
337  }
338 
339  Command *getRemoveRowCommand(int row) override {
340  if (row < 0 || row >= m_events.count()) return nullptr;
341  auto command = new ChangeEventsCommand(getId().untyped,
342  tr("Delete Note"));
343  Event e = m_events.getEventByIndex(row);
344  command->remove(e);
345  return command->finish();
346  }
347 
352  NoteList getNotes() const override {
354  getEndFrame() - getStartFrame());
355  }
356 
357  NoteList getNotesActiveAt(sv_frame_t frame) const override {
358 
359  NoteList notes;
361  for (const auto &e: ee) {
362  notes.push_back(e.toNoteData(getSampleRate(),
363  getScaleUnits() != "Hz"));
364  }
365  return notes;
366  }
367 
369  sv_frame_t duration) const override {
370 
371  NoteList notes;
372  EventVector ee = m_events.getEventsStartingWithin(startFrame, duration);
373  for (const auto &e: ee) {
374  notes.push_back(e.toNoteData(getSampleRate(),
375  getScaleUnits() != "Hz"));
376  }
377  return notes;
378  }
379 
384  void toXml(QTextStream &out,
385  QString indent = "",
386  QString extraAttributes = "") const override {
387 
389 
391  (out,
392  indent,
393  QString("type=\"sparse\" dimensions=\"3\" resolution=\"%1\" "
394  "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"%4\" "
395  "valueQuantization=\"%5\" minimum=\"%6\" maximum=\"%7\" "
396  "units=\"%8\" %9")
397  .arg(m_resolution)
398  .arg("true") // always true after model reaches 100% -
399  // subsequent events are always notified
400  .arg(m_events.getExportId())
401  .arg(m_subtype == FLEXI_NOTE ? "flexinote" : "note")
402  .arg(m_valueQuantization)
403  .arg(m_valueMinimum)
404  .arg(m_valueMaximum)
405  .arg(encodeEntities(m_units))
406  .arg(extraAttributes));
407 
408  m_events.toXml(out, indent, QString("dimensions=\"3\""));
409  }
410 
411  QVector<QString>
412  getStringExportHeaders(DataExportOptions options) const override {
413  return m_events.getStringExportHeaders(options, {});
414  }
415 
416  QVector<QVector<QString>>
418  sv_frame_t startFrame,
419  sv_frame_t duration) const override {
421  (options,
422  startFrame,
423  duration,
424  m_sampleRate,
425  m_resolution,
426  Event().withValue(0.f).withDuration(0.f).withLevel(0.f));
427  }
428 
429 protected:
433 
434  std::atomic<float> m_valueMinimum;
435  std::atomic<float> m_valueMaximum;
436  std::atomic<bool> m_haveExtents;
438  QString m_units;
440  std::atomic<int> m_completion;
441 
443 };
444 
445 #endif
double sv_samplerate_t
Sample rate.
Definition: BaseTypes.h:51
EventVector getEventsCovering(sv_frame_t f) const
Definition: NoteModel.h:181
QVector< QString > getStringExportHeaders(DataExportOptions options) const override
Return a label for each column that would be written by toStringExportRows.
Definition: NoteModel.h:412
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
Stream this exportable object out to XML on a text stream.
Definition: Model.cpp:204
bool isEmpty() const
Definition: NoteModel.h:169
Command * getSetDataCommand(int row, int column, const QVariant &value, int role) override
Return a command to set the value in the given cell, for the given role, to the contents of the suppl...
Definition: NoteModel.h:298
static PlayParameterRepository * getInstance()
Subtype getSubtype() const
Definition: NoteModel.h:100
sv_samplerate_t getSampleRate() const override
Return the frame rate in frames per second.
Definition: NoteModel.h:116
bool contains(const Event &e) const
float getValue() const
Definition: Event.h:122
int count() const
Definition: EventSeries.cpp:79
int64_t sv_frame_t
Frame index, the unit of our time axis.
Definition: BaseTypes.h:31
bool containsEvent(const Event &e) const
Definition: NoteModel.h:172
void add(const Event &e)
Definition: EventSeries.cpp:89
EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration) const
Definition: NoteModel.h:184
void registerUnit(QString unit)
EventVector getEventsStartingAt(sv_frame_t frame) const
Retrieve all events starting at exactly the given frame.
Definition: EventSeries.h:143
void toXml(QTextStream &out, QString indent="", QString extraAttributes="") const override
XmlExportable methods.
Definition: NoteModel.h:384
static QVariant adaptFrameForRole(sv_frame_t frame, sv_samplerate_t rate, int role)
Definition: TabularModel.h:124
QString m_units
Definition: NoteModel.h:438
NoteList getNotesStartingWithin(sv_frame_t startFrame, sv_frame_t duration) const override
Get notes that start within the range in frames defined by the given start frame and duration...
Definition: NoteModel.h:368
Event getEventByIndex(int index) const
Return the event at the given numerical index in the series, where 0 = the first event and count()-1 ...
void update(sv_frame_t frame, sv_frame_t duration)
EventVector getAllEvents() const
Retrieve all events, in their natural order.
DeferredNotifier m_notifier
Definition: NoteModel.h:439
sv_samplerate_t m_sampleRate
Definition: NoteModel.h:431
#define ISNAN
Definition: System.h:103
bool getNearestEventMatching(sv_frame_t startSearchAt, std::function< bool(Event)> predicate, EventSeries::Direction direction, Event &found) const
Definition: NoteModel.h:193
QVariant getData(int row, int column, int role) const override
Get the value in the given cell, for the given role.
Definition: NoteModel.h:279
bool isColumnTimeValue(int column) const override
Return true if the column is the frame time of the item, or an alternative representation of it (i...
Definition: NoteModel.h:249
sv_frame_t getTrueEndFrame() const override
Return the audio frame at the end of the model.
Definition: NoteModel.h:108
sv_frame_t getStartFrame() const
Return the frame of the first event in the series.
bool isSparse() const override
Return true if this is a sparse model.
Definition: NoteModel.h:101
int getColumnCount() const override
Return the number of columns (values/labels/etc per item).
Definition: NoteModel.h:245
QVector< QVector< QString > > toStringExportRows(DataExportOptions options, sv_frame_t startFrame, sv_frame_t duration, sv_samplerate_t sampleRate, sv_frame_t resolution, Event fillEvent) const
Emit events starting within the given range as string rows ready for conversion to an e...
int getRowCount() const override
TabularModel methods.
Definition: NoteModel.h:241
Command * getRemoveRowCommand(int row) override
Return a command to delete the row with the given index.
Definition: NoteModel.h:339
void makeDeferredNotifications()
bool isEditable() const override
Return true if the model is user-editable, false otherwise.
Definition: NoteModel.h:328
QVector< QString > getStringExportHeaders(DataExportOptions options, Event::ExportNameOptions) const
Return a label for each column that would be written by toStringExportRows.
NoteList getNotes() const override
NoteExportable methods.
Definition: NoteModel.h:352
float getValueMaximum() const
Definition: NoteModel.h:138
int getResolution() const
Definition: NoteModel.h:117
Id getId() const
Return an id for this object.
Definition: ById.h:193
int getIndexForEvent(const Event &e) const
Return the index of the first event in the series that does not compare inferior to the given event...
sv_frame_t getEndFrame() const
Return the audio frame at the end of the model, i.e.
Definition: Model.h:87
sv_frame_t getFrameForRow(int row) const override
Return the frame time for the given row.
Definition: NoteModel.h:255
std::atomic< float > m_valueMinimum
Definition: NoteModel.h:434
Event withLabel(QString label) const
Definition: Event.h:160
void toXml(QTextStream &out, QString indent, QString extraAttributes) const override
Emit to XML as a dataset element.
static QString encodeEntities(QString)
bool canPlay() const override
Definition: NoteModel.h:119
int m_resolution
Definition: NoteModel.h:432
sv_frame_t getDuration() const
Definition: Event.h:138
int getEventCount() const
Query methods.
Definition: NoteModel.h:166
bool isEmpty() const
Definition: EventSeries.cpp:72
sv_frame_t getStartFrame() const override
Return the first audio frame spanned by the model.
Definition: NoteModel.h:104
EventVector getEventsCovering(sv_frame_t frame) const
Retrieve all events that cover the given frame.
void setValueQuantization(float q)
Definition: NoteModel.h:135
static QVariant adaptValueForRole(float value, QString unit, int role)
Definition: TabularModel.h:133
static UnitDatabase * getInstance()
EventVector getEventsSpanning(sv_frame_t frame, sv_frame_t duration) const
Retrieve all events any part of which falls within the range in frames defined by the given frame f a...
Event withDuration(sv_frame_t duration) const
Definition: Event.h:140
static QString notes[]
Definition: Pitch.cpp:91
Model is the base class for all data models that represent any sort of data on a time scale based on ...
Definition: Model.h:51
TabularModel is an abstract base class for models that support direct access to data in a tabular for...
Definition: TabularModel.h:35
void completionChanged(ModelId myId)
Emitted when some internal processing has advanced a stage, but the model has not changed externally...
std::atomic< bool > m_haveExtents
Definition: NoteModel.h:436
QMutex m_mutex
Definition: Model.h:337
Event withValue(float value) const
Definition: Event.h:124
int getRowForFrame(sv_frame_t frame) const override
Return the number of the first row whose frame time is not less than the given one.
Definition: NoteModel.h:263
QVector< QVector< QString > > toStringExportRows(DataExportOptions options, sv_frame_t startFrame, sv_frame_t duration) const override
Emit events starting within the given range as string rows ready for conversion to an e...
Definition: NoteModel.h:417
sv_frame_t getEndFrame() const
Return the frame plus duration of the event in the series that ends last.
void removePlayable(int id)
Unregister a playable.
Subtype m_subtype
Definition: NoteModel.h:430
EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const
Definition: NoteModel.h:178
Event withFrame(sv_frame_t frame) const
Definition: Event.h:115
void remove(const Event &e)
QString getHeading(int column) const override
Return the heading for a given column, e.g.
Definition: NoteModel.h:267
EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const
Definition: NoteModel.h:187
std::atomic< int > m_completion
Definition: NoteModel.h:440
SortType getSortType(int column) const override
Return the sort type (numeric or alphabetical) for the column.
Definition: NoteModel.h:322
ExportId getExportId() const
Return the numerical export identifier for this object.
Command * getInsertRowCommand(int row) override
Return a command to insert a new row before the row with the given index.
Definition: NoteModel.h:330
void setCompletion(int completion, bool update=true)
Definition: NoteModel.h:142
bool getNearestEventMatching(sv_frame_t startSearchAt, std::function< bool(const Event &)> predicate, Direction direction, Event &found) const
Return the first event for which the given predicate returns true, searching events with start frames...
EventSeries m_events
Definition: NoteModel.h:442
EventVector getAllEvents() const
Definition: NoteModel.h:175
bool isOK() const override
Return true if the model was constructed successfully.
Definition: NoteModel.h:102
NoteModel(sv_samplerate_t sampleRate, int resolution, float valueMinimum, float valueMaximum, bool notifyOnAdd=true, Subtype subtype=NORMAL_NOTE)
Definition: NoteModel.h:72
void modelChangedWithin(ModelId myId, sv_frame_t startFrame, sv_frame_t endFrame)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
int getCompletion() const override
Return an estimated percentage value showing how far through any background operation used to calcula...
Definition: NoteModel.h:140
EventVector getEventsWithin(sv_frame_t frame, sv_frame_t duration, int overspill=0) const
Retrieve all events falling wholly within the range in frames defined by the given frame f and durati...
void setScaleUnits(QString units)
Definition: NoteModel.h:128
Event withLevel(float level) const
Definition: Event.h:178
int getIndexForEvent(const Event &e)
Definition: NoteModel.h:200
#define ISINF
Definition: System.h:104
std::vector< NoteData > NoteList
Definition: NoteData.h:48
NoteModel(sv_samplerate_t sampleRate, int resolution, bool notifyOnAdd=true, Subtype subtype=NORMAL_NOTE)
Definition: NoteModel.h:46
An immutable(-ish) type used for point and event representation in sparse models, as well as for inte...
Definition: Event.h:55
virtual ~NoteModel()
Definition: NoteModel.h:94
float getLevel() const
Definition: Event.h:176
float m_valueQuantization
Definition: NoteModel.h:437
void addPlayable(int id, const Playable *)
Register a playable.
QString getScaleUnits() const
Definition: NoteModel.h:124
EventVector getEventsStartingAt(sv_frame_t f) const
Definition: NoteModel.h:190
sv_frame_t getFrame() const
Definition: Event.h:113
Container storing a series of events, with or without durations, and supporting the ability to query ...
Definition: EventSeries.h:49
void switchMode(Mode newMode)
QString getTypeName() const override
Return the type of the model.
Definition: NoteModel.h:99
std::vector< Event > EventVector
Definition: Event.h:494
float getValueMinimum() const
Definition: NoteModel.h:137
void add(Event e) override
Editing methods.
Definition: NoteModel.h:207
NoteList getNotesActiveAt(sv_frame_t frame) const override
Get notes that are active at the given frame, i.e.
Definition: NoteModel.h:357
Interface for classes that can be modified through these commands.
Definition: EventCommands.h:26
QString getLabel() const
Definition: Event.h:158
EventVector getEventsStartingWithin(sv_frame_t frame, sv_frame_t duration) const
Retrieve all events starting within the range in frames defined by the given frame f and duration d...
QString getDefaultPlayClipId() const override
Definition: NoteModel.h:120
void modelChanged(ModelId myId)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
float getValueQuantization() const
Definition: NoteModel.h:134
Command to add or remove a series of events to or from an editable, with undo.
int DataExportOptions
std::atomic< float > m_valueMaximum
Definition: NoteModel.h:435