comparison data/model/SparseTimeValueModel.h @ 1651:7a56bb85030f single-point

Introduce deferred notifier, + start converting sparse time-value model (perhaps we should rename it too)
author Chris Cannam
date Mon, 18 Mar 2019 14:17:20 +0000
parents ad5f892c0c4d
children 0cfb882155a6
comparison
equal deleted inserted replaced
1650:bbfb5a1e4b84 1651:7a56bb85030f
2 2
3 /* 3 /*
4 Sonic Visualiser 4 Sonic Visualiser
5 An audio file viewer and annotation editor. 5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London. 6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006 Chris Cannam.
8 7
9 This program is free software; you can redistribute it and/or 8 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as 9 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the 10 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file 11 License, or (at your option) any later version. See the file
14 */ 13 */
15 14
16 #ifndef SV_SPARSE_TIME_VALUE_MODEL_H 15 #ifndef SV_SPARSE_TIME_VALUE_MODEL_H
17 #define SV_SPARSE_TIME_VALUE_MODEL_H 16 #define SV_SPARSE_TIME_VALUE_MODEL_H
18 17
19 #include "SparseValueModel.h" 18 #include "EventCommands.h"
19 #include "TabularModel.h"
20 #include "Model.h"
21 #include "DeferredNotifier.h"
22
23 #include "base/RealTime.h"
24 #include "base/EventSeries.h"
25 #include "base/UnitDatabase.h"
20 #include "base/PlayParameterRepository.h" 26 #include "base/PlayParameterRepository.h"
21 #include "base/RealTime.h" 27
28 #include "system/System.h"
22 29
23 /** 30 /**
24 * Time/value point type for use in a SparseModel or SparseValueModel. 31 * A model representing a wiggly-line plot with points at arbitrary
25 * With this point type, the model basically represents a wiggly-line 32 * intervals of the model resolution.
26 * plot with points at arbitrary intervals of the model resolution.
27 */ 33 */
28 34 class SparseTimeValueModel : public Model,
29 struct TimeValuePoint 35 public TabularModel,
30 { 36 public EventEditable
31 public:
32 TimeValuePoint(sv_frame_t _frame) : frame(_frame), value(0.0f) { }
33 TimeValuePoint(sv_frame_t _frame, float _value, QString _label) :
34 frame(_frame), value(_value), label(_label) { }
35
36 int getDimensions() const { return 2; }
37
38 sv_frame_t frame;
39 float value;
40 QString label;
41
42 QString getLabel() const { return label; }
43
44 void toXml(QTextStream &stream, QString indent = "",
45 QString extraAttributes = "") const
46 {
47 stream << QString("%1<point frame=\"%2\" value=\"%3\" label=\"%4\" %5/>\n")
48 .arg(indent).arg(frame).arg(value).arg(XmlExportable::encodeEntities(label))
49 .arg(extraAttributes);
50 }
51
52 QString toDelimitedDataString(QString delimiter, DataExportOptions, sv_samplerate_t sampleRate) const
53 {
54 QStringList list;
55 list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str();
56 list << QString("%1").arg(value);
57 if (label != "") list << label;
58 return list.join(delimiter);
59 }
60
61 struct Comparator {
62 bool operator()(const TimeValuePoint &p1,
63 const TimeValuePoint &p2) const {
64 if (p1.frame != p2.frame) return p1.frame < p2.frame;
65 if (p1.value != p2.value) return p1.value < p2.value;
66 return p1.label < p2.label;
67 }
68 };
69
70 struct OrderComparator {
71 bool operator()(const TimeValuePoint &p1,
72 const TimeValuePoint &p2) const {
73 return p1.frame < p2.frame;
74 }
75 };
76 };
77
78
79 class SparseTimeValueModel : public SparseValueModel<TimeValuePoint>
80 { 37 {
81 Q_OBJECT 38 Q_OBJECT
82 39
83 public: 40 public:
84 SparseTimeValueModel(sv_samplerate_t sampleRate, int resolution, 41 SparseTimeValueModel(sv_samplerate_t sampleRate,
42 int resolution,
85 bool notifyOnAdd = true) : 43 bool notifyOnAdd = true) :
86 SparseValueModel<TimeValuePoint>(sampleRate, resolution, 44 m_sampleRate(sampleRate),
87 notifyOnAdd) 45 m_resolution(resolution),
88 { 46 m_valueMinimum(0.f),
47 m_valueMaximum(0.f),
48 m_haveExtents(false),
49 m_haveTextLabels(false),
50 m_notifier(this,
51 notifyOnAdd ?
52 DeferredNotifier::NOTIFY_ALWAYS :
53 DeferredNotifier::NOTIFY_DEFERRED),
54 m_completion(0) {
89 // Model is playable, but may not sound (if units not Hz or 55 // Model is playable, but may not sound (if units not Hz or
90 // range unsuitable) 56 // range unsuitable)
91 PlayParameterRepository::getInstance()->addPlayable(this); 57 PlayParameterRepository::getInstance()->addPlayable(this);
92 } 58 }
93 59
94 SparseTimeValueModel(sv_samplerate_t sampleRate, int resolution, 60 SparseTimeValueModel(sv_samplerate_t sampleRate, int resolution,
95 float valueMinimum, float valueMaximum, 61 float valueMinimum, float valueMaximum,
96 bool notifyOnAdd = true) : 62 bool notifyOnAdd = true) :
97 SparseValueModel<TimeValuePoint>(sampleRate, resolution, 63 m_sampleRate(sampleRate),
98 valueMinimum, valueMaximum, 64 m_resolution(resolution),
99 notifyOnAdd) 65 m_valueMinimum(valueMinimum),
100 { 66 m_valueMaximum(valueMaximum),
67 m_haveExtents(false),
68 m_haveTextLabels(false),
69 m_notifier(this,
70 notifyOnAdd ?
71 DeferredNotifier::NOTIFY_ALWAYS :
72 DeferredNotifier::NOTIFY_DEFERRED),
73 m_completion(0) {
101 // Model is playable, but may not sound (if units not Hz or 74 // Model is playable, but may not sound (if units not Hz or
102 // range unsuitable) 75 // range unsuitable)
103 PlayParameterRepository::getInstance()->addPlayable(this); 76 PlayParameterRepository::getInstance()->addPlayable(this);
104 } 77 }
105 78
106 virtual ~SparseTimeValueModel() 79 virtual ~SparseTimeValueModel() {
107 {
108 PlayParameterRepository::getInstance()->removePlayable(this); 80 PlayParameterRepository::getInstance()->removePlayable(this);
109 } 81 }
110 82
111 QString getTypeName() const override { return tr("Sparse Time-Value"); } 83 QString getTypeName() const override { return tr("Sparse Time-Value"); }
84
85 bool isOK() const override { return true; }
86 sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); }
87 sv_frame_t getEndFrame() const override { return m_events.getEndFrame(); }
88 sv_samplerate_t getSampleRate() const override { return m_sampleRate; }
89 int getResolution() const { return m_resolution; }
112 90
113 bool canPlay() const override { return true; } 91 bool canPlay() const override { return true; }
114 bool getDefaultPlayAudible() const override { return false; } // user must unmute 92 bool getDefaultPlayAudible() const override { return false; } // user must unmute
115 93
94 QString getScaleUnits() const { return m_units; }
95 void setScaleUnits(QString units) {
96 m_units = units;
97 UnitDatabase::getInstance()->registerUnit(units);
98 }
99
100 bool hasTextLabels() const { return m_haveTextLabels; }
101
102 float getValueMinimum() const { return m_valueMinimum; }
103 float getValueMaximum() const { return m_valueMaximum; }
104
105 int getCompletion() const { return m_completion; }
106
107 void setCompletion(int completion, bool update = true) {
108
109 { QMutexLocker locker(&m_mutex);
110 if (m_completion == completion) return;
111 m_completion = completion;
112 }
113
114 if (update) {
115 m_notifier.makeDeferredNotifications();
116 }
117
118 emit completionChanged();
119
120 if (completion == 100) {
121 // henceforth:
122 m_notifier.switchMode(DeferredNotifier::NOTIFY_ALWAYS);
123 emit modelChanged();
124 }
125 }
126
127 /**
128 * Query methods.
129 */
130
131 int getEventCount() const {
132 return m_events.count();
133 }
134 bool isEmpty() const {
135 return m_events.isEmpty();
136 }
137 bool containsEvent(const Event &e) const {
138 return m_events.contains(e);
139 }
140 EventVector getAllEvents() const {
141 return m_events.getAllEvents();
142 }
143 EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const {
144 return m_events.getEventsSpanning(f, duration);
145 }
146 EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration) const {
147 return m_events.getEventsWithin(f, duration);
148 }
149 EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const {
150 return m_events.getEventsStartingWithin(f, duration);
151 }
152 EventVector getEventsCovering(sv_frame_t f) const {
153 return m_events.getEventsCovering(f);
154 }
155
156 /**
157 * Editing methods.
158 */
159 void add(Event e) override {
160
161 bool allChange = false;
162
163 {
164 QMutexLocker locker(&m_mutex);
165 m_events.add(e);
166
167 if (e.getLabel() != "") {
168 m_haveTextLabels = true;
169 }
170
171 float v = e.getValue();
172 if (!ISNAN(v) && !ISINF(v)) {
173 if (!m_haveExtents || v < m_valueMinimum) {
174 m_valueMinimum = v; allChange = true;
175 }
176 if (!m_haveExtents || v > m_valueMaximum) {
177 m_valueMaximum = v; allChange = true;
178 }
179 m_haveExtents = true;
180 }
181 }
182
183 m_notifier.update(e.getFrame(), m_resolution);
184
185 if (allChange) {
186 emit modelChanged();
187 }
188 }
189
190 void remove(Event e) override {
191 {
192 QMutexLocker locker(&m_mutex);
193 m_events.remove(e);
194 }
195 emit modelChangedWithin(e.getFrame(), e.getFrame() + m_resolution);
196 }
197
116 /** 198 /**
117 * TabularModel methods. 199 * TabularModel methods.
118 */ 200 */
119 201
120 int getColumnCount() const override 202 int getRowCount() const override {
121 { 203 return m_events.count();
204 }
205
206 int getColumnCount() const override {
122 return 4; 207 return 4;
123 } 208 }
124 209
125 QString getHeading(int column) const override 210 bool isColumnTimeValue(int column) const override {
126 { 211 // NB duration is not a "time value" -- that's for columns
212 // whose sort ordering is exactly that of the frame time
213 return (column < 2);
214 }
215
216 sv_frame_t getFrameForRow(int row) const override {
217 if (row < 0 || row >= m_events.count()) {
218 return 0;
219 }
220 Event e = m_events.getEventByIndex(row);
221 return e.getFrame();
222 }
223
224 int getRowForFrame(sv_frame_t frame) const override {
225 return m_events.getIndexForEvent(Event(frame));
226 }
227
228 QString getHeading(int column) const override {
127 switch (column) { 229 switch (column) {
128 case 0: return tr("Time"); 230 case 0: return tr("Time");
129 case 1: return tr("Frame"); 231 case 1: return tr("Frame");
130 case 2: return tr("Value"); 232 case 2: return tr("Value");
131 case 3: return tr("Label"); 233 case 3: return tr("Label");
132 default: return tr("Unknown"); 234 default: return tr("Unknown");
133 } 235 }
134 } 236 }
135 237
136 QVariant getData(int row, int column, int role) const override 238 SortType getSortType(int column) const override {
137 {
138 if (column < 2) {
139 return SparseValueModel<TimeValuePoint>::getData
140 (row, column, role);
141 }
142
143 PointListConstIterator i = getPointListIteratorForRow(row);
144 if (i == m_points.end()) return QVariant();
145
146 switch (column) {
147 case 2:
148 if (role == Qt::EditRole || role == SortRole) return i->value;
149 else return QString("%1 %2").arg(i->value).arg(getScaleUnits());
150 case 3: return i->label;
151 default: return QVariant();
152 }
153 }
154
155 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override
156 {
157 if (column < 2) {
158 return SparseValueModel<TimeValuePoint>::getSetDataCommand
159 (row, column, value, role);
160 }
161
162 if (role != Qt::EditRole) return 0;
163 PointListConstIterator i = getPointListIteratorForRow(row);
164 if (i == m_points.end()) return 0;
165 EditCommand *command = new EditCommand(this, tr("Edit Data"));
166
167 Point point(*i);
168 command->deletePoint(point);
169
170 switch (column) {
171 case 2: point.value = float(value.toDouble()); break;
172 case 3: point.label = value.toString(); break;
173 }
174
175 command->addPoint(point);
176 return command->finish();
177 }
178
179 bool isColumnTimeValue(int column) const override
180 {
181 return (column < 2);
182 }
183
184 SortType getSortType(int column) const override
185 {
186 if (column == 3) return SortAlphabetical; 239 if (column == 3) return SortAlphabetical;
187 return SortNumeric; 240 return SortNumeric;
188 } 241 }
242
243 QVariant getData(int row, int column, int role) const override {
244
245 if (row < 0 || row >= m_events.count()) {
246 return QVariant();
247 }
248
249 Event e = m_events.getEventByIndex(row);
250
251 switch (column) {
252 case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role);
253 case 1: return int(e.getFrame());
254 case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role);
255 case 3: return e.getLabel();
256 default: return QVariant();
257 }
258 }
259
260 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override {
261 if (row < 0 || row >= m_events.count()) return nullptr;
262 if (role != Qt::EditRole) return nullptr;
263
264 Event e0 = m_events.getEventByIndex(row);
265 Event e1;
266
267 switch (column) {
268 case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() *
269 getSampleRate()))); break;
270 case 1: e1 = e0.withFrame(value.toInt()); break;
271 case 2: e1 = e0.withValue(float(value.toDouble())); break;
272 case 3: e1 = e0.withLabel(value.toString()); break;
273 }
274
275 ChangeEventsCommand *command =
276 new ChangeEventsCommand(this, tr("Edit Data"));
277 command->remove(e0);
278 command->add(e1);
279 return command->finish();
280 }
281
282 /**
283 * XmlExportable methods.
284 */
285 void toXml(QTextStream &out,
286 QString indent = "",
287 QString extraAttributes = "") const override {
288
289 Model::toXml
290 (out,
291 indent,
292 QString("type=\"sparse\" dimensions=\"2\" resolution=\"%1\" "
293 "notifyOnAdd=\"%2\" dataset=\"%3\" "
294 "minimum=\"%4\" maximum=\"%5\" "
295 "units=\"%6\" %7")
296 .arg(m_resolution)
297 .arg("true") // always true after model reaches 100% -
298 // subsequent events are always notified
299 .arg(getObjectExportId(&m_events))
300 .arg(m_valueMinimum)
301 .arg(m_valueMaximum)
302 .arg(encodeEntities(m_units))
303 .arg(extraAttributes));
304
305 m_events.toXml(out, indent, QString("dimensions=\"2\""));
306 }
307
308 protected:
309 sv_samplerate_t m_sampleRate;
310 int m_resolution;
311
312 float m_valueMinimum;
313 float m_valueMaximum;
314 bool m_haveExtents;
315 bool m_haveTextLabels;
316 QString m_units;
317 DeferredNotifier m_notifier;
318 int m_completion;
319
320 EventSeries m_events;
321
322 mutable QMutex m_mutex;
189 }; 323 };
190 324
191 325
192 #endif 326 #endif
193 327