Mercurial > hg > svcore
comparison 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 |
comparison
equal
deleted
inserted
replaced
1643:7a23dfe65d66 | 1644:513192aa9b03 |
---|---|
35 public NoteExportable | 35 public NoteExportable |
36 { | 36 { |
37 Q_OBJECT | 37 Q_OBJECT |
38 | 38 |
39 public: | 39 public: |
40 NoteModel(sv_samplerate_t sampleRate, int resolution, | 40 NoteModel(sv_samplerate_t sampleRate, |
41 int resolution, | |
41 bool notifyOnAdd = true) : | 42 bool notifyOnAdd = true) : |
42 m_sampleRate(sampleRate), | 43 m_sampleRate(sampleRate), |
43 m_resolution(resolution), | 44 m_resolution(resolution), |
44 m_valueMinimum(0.f), | 45 m_valueMinimum(0.f), |
45 m_valueMaximum(0.f), | 46 m_valueMaximum(0.f), |
80 | 81 |
81 bool isOK() const override { return true; } | 82 bool isOK() const override { return true; } |
82 sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } | 83 sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } |
83 sv_frame_t getEndFrame() const override { return m_events.getEndFrame(); } | 84 sv_frame_t getEndFrame() const override { return m_events.getEndFrame(); } |
84 sv_samplerate_t getSampleRate() const override { return m_sampleRate; } | 85 sv_samplerate_t getSampleRate() const override { return m_sampleRate; } |
86 int getResolution() const { return m_resolution; } | |
85 | 87 |
86 bool canPlay() const override { return true; } | 88 bool canPlay() const override { return true; } |
87 QString getDefaultPlayClipId() const override { | 89 QString getDefaultPlayClipId() const override { |
88 return "elecpiano"; | 90 return "elecpiano"; |
89 } | 91 } |
143 if (emitRegionChanged) { | 145 if (emitRegionChanged) { |
144 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax); | 146 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax); |
145 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; | 147 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; |
146 } | 148 } |
147 } | 149 } |
150 | |
151 /** | |
152 * Query methods. | |
153 */ | |
154 | |
155 int getEventCount() const { | |
156 return m_events.count(); | |
157 } | |
158 bool isEmpty() const { | |
159 return m_events.isEmpty(); | |
160 } | |
161 bool containsEvent(const Event &e) const { | |
162 return m_events.contains(e); | |
163 } | |
164 EventVector getAllEvents() const { | |
165 return m_events.getAllEvents(); | |
166 } | |
167 EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const { | |
168 return m_events.getEventsSpanning(f, duration); | |
169 } | |
170 EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration) const { | |
171 return m_events.getEventsWithin(f, duration); | |
172 } | |
173 EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const { | |
174 return m_events.getEventsStartingWithin(f, duration); | |
175 } | |
176 EventVector getEventsCovering(sv_frame_t f) const { | |
177 return m_events.getEventsCovering(f); | |
178 } | |
179 | |
180 /** | |
181 * Editing commands and methods. | |
182 */ | |
183 | |
184 class EditCommand : public Command | |
185 { | |
186 public: | |
187 //!!! borrowed ptr | |
188 EditCommand(NoteModel *model, QString name) : | |
189 m_model(model), m_name(name) { } | |
190 | |
191 QString getName() const override { | |
192 return m_name; | |
193 } | |
194 | |
195 void add(Event e) { | |
196 m_add.insert(e); | |
197 } | |
198 | |
199 void remove(Event e) { | |
200 m_remove.insert(e); | |
201 } | |
202 | |
203 void execute() override { | |
204 for (const Event &e: m_add) m_model->add(e); | |
205 for (const Event &e: m_remove) m_model->remove(e); | |
206 } | |
207 | |
208 void unexecute() override { | |
209 for (const Event &e: m_remove) m_model->add(e); | |
210 for (const Event &e: m_add) m_model->remove(e); | |
211 } | |
212 | |
213 EditCommand *finish() { | |
214 if (m_add.empty() && m_remove.empty()) { | |
215 delete this; | |
216 return nullptr; | |
217 } else { | |
218 return this; | |
219 } | |
220 } | |
221 | |
222 private: | |
223 NoteModel *m_model; | |
224 std::set<Event> m_add; | |
225 std::set<Event> m_remove; | |
226 QString m_name; | |
227 }; | |
228 | |
229 void add(Event e) { | |
230 | |
231 bool allChange = false; | |
232 | |
233 { | |
234 QMutexLocker locker(&m_mutex); | |
235 m_events.add(e); | |
236 //!!!??? if (point.getLabel() != "") m_hasTextLabels = true; | |
237 | |
238 float v = e.getValue(); | |
239 if (!ISNAN(v) && !ISINF(v)) { | |
240 if (!m_haveExtents || v < m_valueMinimum) { | |
241 m_valueMinimum = v; allChange = true; | |
242 } | |
243 if (!m_haveExtents || v > m_valueMaximum) { | |
244 m_valueMaximum = v; allChange = true; | |
245 } | |
246 m_haveExtents = true; | |
247 } | |
248 | |
249 sv_frame_t f = e.getFrame(); | |
250 | |
251 if (!m_notifyOnAdd) { | |
252 if (m_sinceLastNotifyMin == -1 || f < m_sinceLastNotifyMin) { | |
253 m_sinceLastNotifyMin = f; | |
254 } | |
255 if (m_sinceLastNotifyMax == -1 || f > m_sinceLastNotifyMax) { | |
256 m_sinceLastNotifyMax = f; | |
257 } | |
258 } | |
259 } | |
260 | |
261 if (m_notifyOnAdd) { | |
262 emit modelChangedWithin(e.getFrame(), | |
263 e.getFrame() + e.getDuration() + m_resolution); | |
264 } | |
265 if (allChange) { | |
266 emit modelChanged(); | |
267 } | |
268 } | |
269 | |
270 void remove(Event e) { | |
271 { | |
272 QMutexLocker locker(&m_mutex); | |
273 m_events.remove(e); | |
274 } | |
275 emit modelChangedWithin(e.getFrame(), | |
276 e.getFrame() + e.getDuration() + m_resolution); | |
277 } | |
278 | |
279 /** | |
280 * TabularModel methods. | |
281 */ | |
282 | |
283 int getRowCount() const override { | |
284 return m_events.count(); | |
285 } | |
286 | |
287 int getColumnCount() const override { | |
288 return 6; | |
289 } | |
290 | |
291 bool isColumnTimeValue(int column) const override { | |
292 // NB duration is not a "time value" -- that's for columns | |
293 // whose sort ordering is exactly that of the frame time | |
294 return (column < 2); | |
295 } | |
296 | |
297 sv_frame_t getFrameForRow(int row) const override { | |
298 if (row < 0 || row >= m_events.count()) { | |
299 return 0; | |
300 } | |
301 Event e = m_events.getEventByIndex(row); | |
302 return e.getFrame(); | |
303 } | |
304 | |
305 int getRowForFrame(sv_frame_t frame) const override { | |
306 return m_events.getIndexForEvent(Event(frame)); | |
307 } | |
308 | |
309 QString getHeading(int column) const override { | |
310 switch (column) { | |
311 case 0: return tr("Time"); | |
312 case 1: return tr("Frame"); | |
313 case 2: return tr("Pitch"); | |
314 case 3: return tr("Duration"); | |
315 case 4: return tr("Level"); | |
316 case 5: return tr("Label"); | |
317 default: return tr("Unknown"); | |
318 } | |
319 } | |
320 | |
321 QVariant getData(int row, int column, int role) const override { | |
322 | |
323 if (row < 0 || row >= m_events.count()) { | |
324 return QVariant(); | |
325 } | |
326 | |
327 Event e = m_events.getEventByIndex(row); | |
328 | |
329 switch (column) { | |
330 case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role); | |
331 case 1: return int(e.getFrame()); | |
332 case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role); | |
333 case 3: return int(e.getDuration()); | |
334 case 4: return e.getLevel(); | |
335 case 5: return e.getLabel(); | |
336 default: return QVariant(); | |
337 } | |
338 } | |
339 | |
340 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override | |
341 { | |
342 if (row < 0 || row >= m_events.count()) return nullptr; | |
343 if (role != Qt::EditRole) return nullptr; | |
344 | |
345 Event e0 = m_events.getEventByIndex(row); | |
346 Event e1; | |
347 | |
348 switch (column) { | |
349 case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() * | |
350 getSampleRate()))); break; | |
351 case 1: e1 = e0.withFrame(value.toInt()); break; | |
352 case 2: e1 = e0.withValue(float(value.toDouble())); break; | |
353 case 3: e1 = e0.withDuration(value.toInt()); break; | |
354 case 4: e1 = e0.withLevel(float(value.toDouble())); break; | |
355 case 5: e1 = e0.withLabel(value.toString()); break; | |
356 } | |
357 | |
358 EditCommand *command = new EditCommand(this, tr("Edit Data")); | |
359 command->remove(e0); | |
360 command->add(e1); | |
361 return command->finish(); | |
362 } | |
363 | |
364 SortType getSortType(int column) const override | |
365 { | |
366 if (column == 5) return SortAlphabetical; | |
367 return SortNumeric; | |
368 } | |
369 | |
370 /** | |
371 * NoteExportable methods. | |
372 */ | |
373 | |
374 NoteList getNotes() const override { | |
375 return getNotesStartingWithin(getStartFrame(), | |
376 getEndFrame() - getStartFrame()); | |
377 } | |
378 | |
379 NoteList getNotesActiveAt(sv_frame_t frame) const override { | |
380 | |
381 NoteList notes; | |
382 EventVector ee = m_events.getEventsCovering(frame); | |
383 for (const auto &e: ee) { | |
384 notes.push_back(e.toNoteData(getSampleRate(), | |
385 getScaleUnits() != "Hz")); | |
386 } | |
387 return notes; | |
388 } | |
389 | |
390 NoteList getNotesStartingWithin(sv_frame_t startFrame, | |
391 sv_frame_t duration) const override { | |
392 | |
393 NoteList notes; | |
394 EventVector ee = m_events.getEventsStartingWithin(startFrame, duration); | |
395 for (const auto &e: ee) { | |
396 notes.push_back(e.toNoteData(getSampleRate(), | |
397 getScaleUnits() != "Hz")); | |
398 } | |
399 return notes; | |
400 } | |
401 | |
402 /** | |
403 * XmlExportable methods. | |
404 */ | |
148 | 405 |
149 void toXml(QTextStream &out, | 406 void toXml(QTextStream &out, |
150 QString indent = "", | 407 QString indent = "", |
151 QString extraAttributes = "") const override { | 408 QString extraAttributes = "") const override { |
152 | 409 |
169 .arg(extraAttributes)); | 426 .arg(extraAttributes)); |
170 | 427 |
171 m_events.toXml(out, indent, QString("dimensions=\"3\"")); | 428 m_events.toXml(out, indent, QString("dimensions=\"3\"")); |
172 } | 429 } |
173 | 430 |
174 /** | |
175 * TabularModel methods. | |
176 */ | |
177 | |
178 int getRowCount() const override { | |
179 return m_events.count(); | |
180 } | |
181 | |
182 int getColumnCount() const override { | |
183 return 6; | |
184 } | |
185 | |
186 bool isColumnTimeValue(int column) const override { | |
187 // NB duration is not a "time value" -- that's for columns | |
188 // whose sort ordering is exactly that of the frame time | |
189 return (column < 2); | |
190 } | |
191 | |
192 sv_frame_t getFrameForRow(int row) const override { | |
193 if (row < 0 || row >= m_events.count()) { | |
194 return 0; | |
195 } | |
196 Event e = m_events.getEventByIndex(row); | |
197 return e.getFrame(); | |
198 } | |
199 | |
200 int getRowForFrame(sv_frame_t frame) const override { | |
201 return m_events.getIndexForEvent(Event(frame)); | |
202 } | |
203 | |
204 QString getHeading(int column) const override { | |
205 switch (column) { | |
206 case 0: return tr("Time"); | |
207 case 1: return tr("Frame"); | |
208 case 2: return tr("Pitch"); | |
209 case 3: return tr("Duration"); | |
210 case 4: return tr("Level"); | |
211 case 5: return tr("Label"); | |
212 default: return tr("Unknown"); | |
213 } | |
214 } | |
215 | |
216 QVariant getData(int row, int column, int role) const override { | |
217 | |
218 if (row < 0 || row >= m_events.count()) { | |
219 return QVariant(); | |
220 } | |
221 | |
222 Event e = m_events.getEventByIndex(row); | |
223 | |
224 switch (column) { | |
225 case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role); | |
226 case 1: return int(e.getFrame()); | |
227 case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role); | |
228 case 3: return int(e.getDuration()); | |
229 case 4: return e.getLevel(); | |
230 case 5: return e.getLabel(); | |
231 default: return QVariant(); | |
232 } | |
233 } | |
234 | |
235 class EditCommand : public Command | |
236 { | |
237 public: | |
238 //!!! borrowed ptr | |
239 EditCommand(NoteModel *model, QString name) : | |
240 m_model(model), m_name(name) { } | |
241 | |
242 QString getName() const override { | |
243 return m_name; | |
244 } | |
245 | |
246 void addPoint(Event e) { | |
247 m_add.insert(e); | |
248 } | |
249 void deletePoint(Event e) { | |
250 m_remove.insert(e); | |
251 } | |
252 | |
253 void execute() override { | |
254 for (const Event &e: m_add) m_model->addPoint(e); | |
255 for (const Event &e: m_remove) m_model->deletePoint(e); | |
256 } | |
257 | |
258 void unexecute() override { | |
259 for (const Event &e: m_remove) m_model->addPoint(e); | |
260 for (const Event &e: m_add) m_model->deletePoint(e); | |
261 } | |
262 | |
263 private: | |
264 NoteModel *m_model; | |
265 std::set<Event> m_add; | |
266 std::set<Event> m_remove; | |
267 QString m_name; | |
268 }; | |
269 | |
270 //!!! rename Point to Note throughout? Just because we can now? | |
271 void addPoint(Event e) { | |
272 | |
273 bool allChange = false; | |
274 | |
275 { | |
276 QMutexLocker locker(&m_mutex); | |
277 m_events.add(e); | |
278 //!!!??? if (point.getLabel() != "") m_hasTextLabels = true; | |
279 | |
280 float v = e.getValue(); | |
281 if (!ISNAN(v) && !ISINF(v)) { | |
282 if (!m_haveExtents || v < m_valueMinimum) { | |
283 m_valueMinimum = v; allChange = true; | |
284 } | |
285 if (!m_haveExtents || v > m_valueMaximum) { | |
286 m_valueMaximum = v; allChange = true; | |
287 } | |
288 m_haveExtents = true; | |
289 } | |
290 | |
291 sv_frame_t f = e.getFrame(); | |
292 | |
293 if (!m_notifyOnAdd) { | |
294 if (m_sinceLastNotifyMin == -1 || f < m_sinceLastNotifyMin) { | |
295 m_sinceLastNotifyMin = f; | |
296 } | |
297 if (m_sinceLastNotifyMax == -1 || f > m_sinceLastNotifyMax) { | |
298 m_sinceLastNotifyMax = f; | |
299 } | |
300 } | |
301 } | |
302 | |
303 if (m_notifyOnAdd) { | |
304 emit modelChangedWithin(e.getFrame(), | |
305 e.getFrame() + e.getDuration() + m_resolution); | |
306 } | |
307 if (allChange) { | |
308 emit modelChanged(); | |
309 } | |
310 } | |
311 | |
312 void deletePoint(Event e) { | |
313 { | |
314 QMutexLocker locker(&m_mutex); | |
315 m_events.remove(e); | |
316 } | |
317 emit modelChangedWithin(e.getFrame(), | |
318 e.getFrame() + e.getDuration() + m_resolution); | |
319 } | |
320 | |
321 EventVector getPoints() const /*!!! override? - and/or rename? */ { | |
322 EventVector ee; | |
323 for (int i = 0; i < m_events.count(); ++i) { | |
324 ee.push_back(m_events.getEventByIndex(i)); | |
325 } | |
326 return ee; | |
327 } | |
328 | |
329 //!!! bleah | |
330 EventVector getPoints(sv_frame_t start, sv_frame_t end) const { | |
331 return m_events.getEventsSpanning(start, end - start); | |
332 } | |
333 | |
334 int getPointCount() const { | |
335 return m_events.count(); | |
336 } | |
337 | |
338 bool isEmpty() const { | |
339 return m_events.isEmpty(); | |
340 } | |
341 | |
342 bool containsPoint(const Event &e) const { | |
343 return m_events.contains(e); | |
344 } | |
345 | |
346 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override | |
347 { | |
348 if (row < 0 || row >= m_events.count()) return nullptr; | |
349 if (role != Qt::EditRole) return nullptr; | |
350 | |
351 Event e0 = m_events.getEventByIndex(row); | |
352 Event e1; | |
353 | |
354 switch (column) { | |
355 case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() * | |
356 getSampleRate()))); break; | |
357 case 1: e1 = e0.withFrame(value.toInt()); break; | |
358 case 2: e1 = e0.withValue(float(value.toDouble())); break; | |
359 case 3: e1 = e0.withDuration(value.toInt()); break; | |
360 case 4: e1 = e0.withLevel(float(value.toDouble())); break; | |
361 case 5: e1 = e0.withLabel(value.toString()); break; | |
362 } | |
363 | |
364 EditCommand *command = new EditCommand(this, tr("Edit Data")); | |
365 command->deletePoint(e0); | |
366 command->addPoint(e1); | |
367 return command; | |
368 } | |
369 | |
370 SortType getSortType(int column) const override | |
371 { | |
372 if (column == 5) return SortAlphabetical; | |
373 return SortNumeric; | |
374 } | |
375 | |
376 /** | |
377 * NoteExportable methods. | |
378 */ | |
379 | |
380 NoteList getNotes() const override { | |
381 return getNotesStartingWithin(getStartFrame(), | |
382 getEndFrame() - getStartFrame()); | |
383 } | |
384 | |
385 NoteList getNotesActiveAt(sv_frame_t frame) const override { | |
386 | |
387 NoteList notes; | |
388 EventVector ee = m_events.getEventsCovering(frame); | |
389 for (const auto &e: ee) { | |
390 notes.push_back(e.toNoteData(getSampleRate(), | |
391 getScaleUnits() != "Hz")); | |
392 } | |
393 return notes; | |
394 } | |
395 | |
396 NoteList getNotesStartingWithin(sv_frame_t startFrame, | |
397 sv_frame_t duration) const override { | |
398 | |
399 NoteList notes; | |
400 EventVector ee = m_events.getEventsStartingWithin(startFrame, duration); | |
401 for (const auto &e: ee) { | |
402 notes.push_back(e.toNoteData(getSampleRate(), | |
403 getScaleUnits() != "Hz")); | |
404 } | |
405 return notes; | |
406 } | |
407 | |
408 protected: | 431 protected: |
409 sv_samplerate_t m_sampleRate; | 432 sv_samplerate_t m_sampleRate; |
410 int m_resolution; | 433 int m_resolution; |
411 | 434 |
412 float m_valueMinimum; | 435 float m_valueMinimum; |