Mercurial > hg > svcore
comparison data/model/RegionModel.h @ 1649:1cc9a0d4b1b6 single-point
Update RegionModel following NoteModel, er, model. They have quite a bit in common that we should now pull out some of
author | Chris Cannam |
---|---|
date | Fri, 15 Mar 2019 14:23:50 +0000 |
parents | ad5f892c0c4d |
children | 7a56bb85030f |
comparison
equal
deleted
inserted
replaced
1648:86bbccb79c9b | 1649:1cc9a0d4b1b6 |
---|---|
14 */ | 14 */ |
15 | 15 |
16 #ifndef SV_REGION_MODEL_H | 16 #ifndef SV_REGION_MODEL_H |
17 #define SV_REGION_MODEL_H | 17 #define SV_REGION_MODEL_H |
18 | 18 |
19 #include "IntervalModel.h" | 19 #include "EventCommands.h" |
20 #include "TabularModel.h" | |
21 #include "Model.h" | |
22 | |
20 #include "base/RealTime.h" | 23 #include "base/RealTime.h" |
24 #include "base/EventSeries.h" | |
25 #include "base/UnitDatabase.h" | |
26 | |
27 #include "system/System.h" | |
28 | |
29 #include <QMutex> | |
21 | 30 |
22 /** | 31 /** |
23 * RegionModel -- a concrete IntervalModel for intervals associated | 32 * RegionModel -- a model for intervals associated with a value, which |
24 * with a value, which we call regions for no very compelling reason. | 33 * we call regions for no very compelling reason. |
25 */ | 34 */ |
26 | 35 class RegionModel : public Model, |
27 /** | 36 public TabularModel, |
28 * Region "point" type. A region is something that has an onset time, | 37 public EventEditable |
29 * a single value, and a duration. Like other points, it can also | |
30 * have a label. | |
31 * | |
32 * This is called RegionRec instead of Region to avoid name collisions | |
33 * with the X11 Region struct. Bah. | |
34 */ | |
35 | |
36 struct RegionRec | |
37 { | |
38 public: | |
39 RegionRec() : frame(0), value(0.f), duration(0) { } | |
40 RegionRec(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0) { } | |
41 RegionRec(sv_frame_t _frame, float _value, sv_frame_t _duration, QString _label) : | |
42 frame(_frame), value(_value), duration(_duration), label(_label) { } | |
43 | |
44 int getDimensions() const { return 3; } | |
45 | |
46 sv_frame_t frame; | |
47 float value; | |
48 sv_frame_t duration; | |
49 QString label; | |
50 | |
51 QString getLabel() const { return label; } | |
52 | |
53 void toXml(QTextStream &stream, | |
54 QString indent = "", | |
55 QString extraAttributes = "") const | |
56 { | |
57 stream << | |
58 QString("%1<point frame=\"%2\" value=\"%3\" duration=\"%4\" label=\"%5\" %6/>\n") | |
59 .arg(indent).arg(frame).arg(value).arg(duration) | |
60 .arg(XmlExportable::encodeEntities(label)).arg(extraAttributes); | |
61 } | |
62 | |
63 QString toDelimitedDataString(QString delimiter, DataExportOptions, sv_samplerate_t sampleRate) const | |
64 { | |
65 QStringList list; | |
66 list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str(); | |
67 list << QString("%1").arg(value); | |
68 list << RealTime::frame2RealTime(duration, sampleRate).toString().c_str(); | |
69 if (label != "") list << label; | |
70 return list.join(delimiter); | |
71 } | |
72 | |
73 struct Comparator { | |
74 bool operator()(const RegionRec &p1, | |
75 const RegionRec &p2) const { | |
76 if (p1.frame != p2.frame) return p1.frame < p2.frame; | |
77 if (p1.value != p2.value) return p1.value < p2.value; | |
78 if (p1.duration != p2.duration) return p1.duration < p2.duration; | |
79 return p1.label < p2.label; | |
80 } | |
81 }; | |
82 | |
83 struct OrderComparator { | |
84 bool operator()(const RegionRec &p1, | |
85 const RegionRec &p2) const { | |
86 return p1.frame < p2.frame; | |
87 } | |
88 }; | |
89 }; | |
90 | |
91 | |
92 class RegionModel : public IntervalModel<RegionRec> | |
93 { | 38 { |
94 Q_OBJECT | 39 Q_OBJECT |
95 | 40 |
96 public: | 41 public: |
97 RegionModel(sv_samplerate_t sampleRate, int resolution, | 42 RegionModel(sv_samplerate_t sampleRate, |
43 int resolution, | |
98 bool notifyOnAdd = true) : | 44 bool notifyOnAdd = true) : |
99 IntervalModel<RegionRec>(sampleRate, resolution, notifyOnAdd), | 45 m_sampleRate(sampleRate), |
46 m_resolution(resolution), | |
47 m_valueMinimum(0.f), | |
48 m_valueMaximum(0.f), | |
49 m_haveExtents(false), | |
100 m_valueQuantization(0), | 50 m_valueQuantization(0), |
101 m_haveDistinctValues(false) | 51 m_haveDistinctValues(false), |
102 { | 52 m_notifyOnAdd(notifyOnAdd), |
53 m_sinceLastNotifyMin(-1), | |
54 m_sinceLastNotifyMax(-1), | |
55 m_completion(0) { | |
103 } | 56 } |
104 | 57 |
105 RegionModel(sv_samplerate_t sampleRate, int resolution, | 58 RegionModel(sv_samplerate_t sampleRate, int resolution, |
106 float valueMinimum, float valueMaximum, | 59 float valueMinimum, float valueMaximum, |
107 bool notifyOnAdd = true) : | 60 bool notifyOnAdd = true) : |
108 IntervalModel<RegionRec>(sampleRate, resolution, | 61 m_sampleRate(sampleRate), |
109 valueMinimum, valueMaximum, | 62 m_resolution(resolution), |
110 notifyOnAdd), | 63 m_valueMinimum(valueMinimum), |
64 m_valueMaximum(valueMaximum), | |
65 m_haveExtents(false), | |
111 m_valueQuantization(0), | 66 m_valueQuantization(0), |
112 m_haveDistinctValues(false) | 67 m_haveDistinctValues(false), |
113 { | 68 m_notifyOnAdd(notifyOnAdd), |
114 } | 69 m_sinceLastNotifyMin(-1), |
115 | 70 m_sinceLastNotifyMax(-1), |
116 virtual ~RegionModel() | 71 m_completion(0) { |
117 { | 72 } |
73 | |
74 virtual ~RegionModel() { | |
75 } | |
76 | |
77 QString getTypeName() const override { return tr("Region"); } | |
78 | |
79 bool isOK() const override { return true; } | |
80 sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } | |
81 sv_frame_t getEndFrame() const override { return m_events.getEndFrame(); } | |
82 sv_samplerate_t getSampleRate() const override { return m_sampleRate; } | |
83 int getResolution() const { return m_resolution; } | |
84 | |
85 QString getScaleUnits() const { return m_units; } | |
86 void setScaleUnits(QString units) { | |
87 m_units = units; | |
88 UnitDatabase::getInstance()->registerUnit(units); | |
118 } | 89 } |
119 | 90 |
120 float getValueQuantization() const { return m_valueQuantization; } | 91 float getValueQuantization() const { return m_valueQuantization; } |
121 void setValueQuantization(float q) { m_valueQuantization = q; } | 92 void setValueQuantization(float q) { m_valueQuantization = q; } |
122 | 93 |
123 bool haveDistinctValues() const { return m_haveDistinctValues; } | 94 bool haveDistinctValues() const { return m_haveDistinctValues; } |
124 | 95 |
125 QString getTypeName() const override { return tr("Region"); } | 96 float getValueMinimum() const { return m_valueMinimum; } |
126 | 97 float getValueMaximum() const { return m_valueMaximum; } |
127 void toXml(QTextStream &out, | 98 |
128 QString indent = "", | 99 int getCompletion() const { return m_completion; } |
129 QString extraAttributes = "") const override | 100 |
130 { | 101 void setCompletion(int completion, bool update = true) { |
131 std::cerr << "RegionModel::toXml: extraAttributes = \"" | 102 |
132 << extraAttributes.toStdString() << std::endl; | 103 bool emitCompletionChanged = true; |
133 | 104 bool emitGeneralModelChanged = false; |
134 IntervalModel<RegionRec>::toXml | 105 bool emitRegionChanged = false; |
135 (out, | 106 |
136 indent, | 107 { |
137 QString("%1 subtype=\"region\" valueQuantization=\"%2\"") | 108 QMutexLocker locker(&m_mutex); |
138 .arg(extraAttributes).arg(m_valueQuantization)); | 109 |
139 } | 110 if (m_completion != completion) { |
140 | 111 m_completion = completion; |
112 | |
113 if (completion == 100) { | |
114 | |
115 if (m_notifyOnAdd) { | |
116 emitCompletionChanged = false; | |
117 } | |
118 | |
119 m_notifyOnAdd = true; // henceforth | |
120 emitGeneralModelChanged = true; | |
121 | |
122 } else if (!m_notifyOnAdd) { | |
123 | |
124 if (update && | |
125 m_sinceLastNotifyMin >= 0 && | |
126 m_sinceLastNotifyMax >= 0) { | |
127 emitRegionChanged = true; | |
128 } | |
129 } | |
130 } | |
131 } | |
132 | |
133 if (emitCompletionChanged) { | |
134 emit completionChanged(); | |
135 } | |
136 if (emitGeneralModelChanged) { | |
137 emit modelChanged(); | |
138 } | |
139 if (emitRegionChanged) { | |
140 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax); | |
141 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; | |
142 } | |
143 } | |
144 | |
145 /** | |
146 * Query methods. | |
147 */ | |
148 | |
149 int getEventCount() const { | |
150 return m_events.count(); | |
151 } | |
152 bool isEmpty() const { | |
153 return m_events.isEmpty(); | |
154 } | |
155 bool containsEvent(const Event &e) const { | |
156 return m_events.contains(e); | |
157 } | |
158 EventVector getAllEvents() const { | |
159 return m_events.getAllEvents(); | |
160 } | |
161 EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const { | |
162 return m_events.getEventsSpanning(f, duration); | |
163 } | |
164 EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration) const { | |
165 return m_events.getEventsWithin(f, duration); | |
166 } | |
167 EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const { | |
168 return m_events.getEventsStartingWithin(f, duration); | |
169 } | |
170 EventVector getEventsCovering(sv_frame_t f) const { | |
171 return m_events.getEventsCovering(f); | |
172 } | |
173 | |
174 /** | |
175 * Editing methods. | |
176 */ | |
177 void add(Event e) override { | |
178 | |
179 bool allChange = false; | |
180 | |
181 { | |
182 QMutexLocker locker(&m_mutex); | |
183 m_events.add(e); | |
184 //!!!??? if (point.getLabel() != "") m_hasTextLabels = true; | |
185 | |
186 float v = e.getValue(); | |
187 if (!ISNAN(v) && !ISINF(v)) { | |
188 if (!m_haveExtents || v < m_valueMinimum) { | |
189 m_valueMinimum = v; allChange = true; | |
190 } | |
191 if (!m_haveExtents || v > m_valueMaximum) { | |
192 m_valueMaximum = v; allChange = true; | |
193 } | |
194 m_haveExtents = true; | |
195 } | |
196 | |
197 if (e.hasValue() && e.getValue() != 0.f) { | |
198 m_haveDistinctValues = true; | |
199 } | |
200 | |
201 sv_frame_t f = e.getFrame(); | |
202 | |
203 if (!m_notifyOnAdd) { | |
204 if (m_sinceLastNotifyMin == -1 || f < m_sinceLastNotifyMin) { | |
205 m_sinceLastNotifyMin = f; | |
206 } | |
207 if (m_sinceLastNotifyMax == -1 || f > m_sinceLastNotifyMax) { | |
208 m_sinceLastNotifyMax = f; | |
209 } | |
210 } | |
211 } | |
212 | |
213 if (m_notifyOnAdd) { | |
214 emit modelChangedWithin(e.getFrame(), | |
215 e.getFrame() + e.getDuration() + m_resolution); | |
216 } | |
217 if (allChange) { | |
218 emit modelChanged(); | |
219 } | |
220 } | |
221 | |
222 void remove(Event e) override { | |
223 { | |
224 QMutexLocker locker(&m_mutex); | |
225 m_events.remove(e); | |
226 } | |
227 emit modelChangedWithin(e.getFrame(), | |
228 e.getFrame() + e.getDuration() + m_resolution); | |
229 } | |
230 | |
141 /** | 231 /** |
142 * TabularModel methods. | 232 * TabularModel methods. |
143 */ | 233 */ |
144 | 234 |
145 int getColumnCount() const override | 235 int getRowCount() const override { |
146 { | 236 return m_events.count(); |
237 } | |
238 | |
239 int getColumnCount() const override { | |
147 return 5; | 240 return 5; |
148 } | 241 } |
149 | 242 |
150 QString getHeading(int column) const override | 243 bool isColumnTimeValue(int column) const override { |
151 { | 244 // NB duration is not a "time value" -- that's for columns |
245 // whose sort ordering is exactly that of the frame time | |
246 return (column < 2); | |
247 } | |
248 | |
249 sv_frame_t getFrameForRow(int row) const override { | |
250 if (row < 0 || row >= m_events.count()) { | |
251 return 0; | |
252 } | |
253 Event e = m_events.getEventByIndex(row); | |
254 return e.getFrame(); | |
255 } | |
256 | |
257 int getRowForFrame(sv_frame_t frame) const override { | |
258 return m_events.getIndexForEvent(Event(frame)); | |
259 } | |
260 | |
261 QString getHeading(int column) const override { | |
152 switch (column) { | 262 switch (column) { |
153 case 0: return tr("Time"); | 263 case 0: return tr("Time"); |
154 case 1: return tr("Frame"); | 264 case 1: return tr("Frame"); |
155 case 2: return tr("Value"); | 265 case 2: return tr("Value"); |
156 case 3: return tr("Duration"); | 266 case 3: return tr("Duration"); |
157 case 4: return tr("Label"); | 267 case 4: return tr("Label"); |
158 default: return tr("Unknown"); | 268 default: return tr("Unknown"); |
159 } | 269 } |
160 } | 270 } |
161 | 271 |
162 QVariant getData(int row, int column, int role) const override | 272 QVariant getData(int row, int column, int role) const override { |
163 { | 273 |
164 if (column < 4) { | 274 if (row < 0 || row >= m_events.count()) { |
165 return IntervalModel<RegionRec>::getData(row, column, role); | 275 return QVariant(); |
166 } | 276 } |
167 | 277 |
168 PointListConstIterator i = getPointListIteratorForRow(row); | 278 Event e = m_events.getEventByIndex(row); |
169 if (i == m_points.end()) return QVariant(); | |
170 | 279 |
171 switch (column) { | 280 switch (column) { |
172 case 4: return i->label; | 281 case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role); |
282 case 1: return int(e.getFrame()); | |
283 case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role); | |
284 case 3: return int(e.getDuration()); | |
285 case 4: return e.getLabel(); | |
173 default: return QVariant(); | 286 default: return QVariant(); |
174 } | 287 } |
175 } | 288 } |
176 | 289 |
177 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override | 290 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override { |
178 { | 291 |
179 if (column < 4) { | 292 if (row < 0 || row >= m_events.count()) return nullptr; |
180 return IntervalModel<RegionRec>::getSetDataCommand | 293 if (role != Qt::EditRole) return nullptr; |
181 (row, column, value, role); | 294 |
182 } | 295 Event e0 = m_events.getEventByIndex(row); |
183 | 296 Event e1; |
184 if (role != Qt::EditRole) return 0; | |
185 PointListIterator i = getPointListIteratorForRow(row); | |
186 if (i == m_points.end()) return 0; | |
187 EditCommand *command = new EditCommand(this, tr("Edit Data")); | |
188 | |
189 Point point(*i); | |
190 command->deletePoint(point); | |
191 | 297 |
192 switch (column) { | 298 switch (column) { |
193 case 4: point.label = value.toString(); break; | 299 case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() * |
194 } | 300 getSampleRate()))); break; |
195 | 301 case 1: e1 = e0.withFrame(value.toInt()); break; |
196 command->addPoint(point); | 302 case 2: e1 = e0.withValue(float(value.toDouble())); break; |
303 case 3: e1 = e0.withDuration(value.toInt()); break; | |
304 case 4: e1 = e0.withLabel(value.toString()); break; | |
305 } | |
306 | |
307 ChangeEventsCommand *command = | |
308 new ChangeEventsCommand(this, tr("Edit Data")); | |
309 command->remove(e0); | |
310 command->add(e1); | |
197 return command->finish(); | 311 return command->finish(); |
198 } | 312 } |
199 | 313 |
200 SortType getSortType(int column) const override | 314 SortType getSortType(int column) const override |
201 { | 315 { |
202 if (column == 4) return SortAlphabetical; | 316 if (column == 4) return SortAlphabetical; |
203 return SortNumeric; | 317 return SortNumeric; |
204 } | 318 } |
205 | 319 |
206 void addPoint(const Point &point) override | 320 |
207 { | 321 /** |
208 if (point.value != 0.f) m_haveDistinctValues = true; | 322 * XmlExportable methods. |
209 IntervalModel<RegionRec>::addPoint(point); | 323 */ |
324 void toXml(QTextStream &out, | |
325 QString indent = "", | |
326 QString extraAttributes = "") const override { | |
327 | |
328 Model::toXml | |
329 (out, | |
330 indent, | |
331 QString("type=\"sparse\" dimensions=\"3\" resolution=\"%1\" " | |
332 "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"%4\" " | |
333 "valueQuantization=\"%5\" minimum=\"%6\" maximum=\"%7\" " | |
334 "%8") | |
335 .arg(m_resolution) | |
336 .arg(m_notifyOnAdd ? "true" : "false") | |
337 .arg(getObjectExportId(&m_events)) | |
338 .arg("region") | |
339 .arg(m_valueQuantization) | |
340 .arg(m_valueMinimum) | |
341 .arg(m_valueMaximum) | |
342 .arg(extraAttributes)); | |
343 | |
344 m_events.toXml(out, indent, QString("dimensions=\"3\"")); | |
210 } | 345 } |
211 | 346 |
212 protected: | 347 protected: |
348 sv_samplerate_t m_sampleRate; | |
349 int m_resolution; | |
350 | |
351 float m_valueMinimum; | |
352 float m_valueMaximum; | |
353 bool m_haveExtents; | |
213 float m_valueQuantization; | 354 float m_valueQuantization; |
214 bool m_haveDistinctValues; | 355 bool m_haveDistinctValues; |
356 QString m_units; | |
357 | |
358 bool m_notifyOnAdd; | |
359 sv_frame_t m_sinceLastNotifyMin; | |
360 sv_frame_t m_sinceLastNotifyMax; | |
361 | |
362 EventSeries m_events; | |
363 | |
364 int m_completion; | |
365 | |
366 mutable QMutex m_mutex; | |
215 }; | 367 }; |
216 | 368 |
217 #endif | 369 #endif |