comparison data/model/SparseOneDimensionalModel.h @ 1658:5b7b01da430a single-point

Start updating SparseOneDimensionalModel
author Chris Cannam
date Wed, 20 Mar 2019 16:22:13 +0000
parents 7a23dfe65d66
children 8bf3a52a1604
comparison
equal deleted inserted replaced
1657:31b46a5647db 1658:5b7b01da430a
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_ONE_DIMENSIONAL_MODEL_H 15 #ifndef SV_SPARSE_ONE_DIMENSIONAL_MODEL_H
17 #define SV_SPARSE_ONE_DIMENSIONAL_MODEL_H 16 #define SV_SPARSE_ONE_DIMENSIONAL_MODEL_H
18 17
19 #include "SparseModel.h" 18 #include "EventCommands.h"
19 #include "TabularModel.h"
20 #include "Model.h"
21 #include "DeferredNotifier.h"
22
20 #include "base/NoteData.h" 23 #include "base/NoteData.h"
24 #include "base/EventSeries.h"
21 #include "base/NoteExportable.h" 25 #include "base/NoteExportable.h"
22 #include "base/PlayParameterRepository.h" 26 #include "base/PlayParameterRepository.h"
23 #include "base/RealTime.h" 27 #include "base/RealTime.h"
24 28
29 #include "system/System.h"
30
25 #include <QStringList> 31 #include <QStringList>
26 32
27 struct OneDimensionalPoint 33 /**
28 { 34 * A model representing a series of time instants with optional labels
29 public: 35 * but without values.
30 OneDimensionalPoint(sv_frame_t _frame) : frame(_frame) { } 36 */
31 OneDimensionalPoint(sv_frame_t _frame, QString _label) : frame(_frame), label(_label) { } 37 class SparseOneDimensionalModel : public Model,
32 38 public TabularModel,
33 int getDimensions() const { return 1; } 39 public EventEditable,
34
35 sv_frame_t frame;
36 QString label;
37
38 QString getLabel() const { return label; }
39
40 void toXml(QTextStream &stream,
41 QString indent = "",
42 QString extraAttributes = "") const
43 {
44 stream << QString("%1<point frame=\"%2\" label=\"%3\" %4/>\n")
45 .arg(indent).arg(frame).arg(XmlExportable::encodeEntities(label))
46 .arg(extraAttributes);
47 }
48
49 QString toDelimitedDataString(QString delimiter, DataExportOptions, sv_samplerate_t sampleRate) const
50 {
51 QStringList list;
52 list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str();
53 if (label != "") list << label;
54 return list.join(delimiter);
55 }
56
57 struct Comparator {
58 bool operator()(const OneDimensionalPoint &p1,
59 const OneDimensionalPoint &p2) const {
60 if (p1.frame != p2.frame) return p1.frame < p2.frame;
61 return p1.label < p2.label;
62 }
63 };
64
65 struct OrderComparator {
66 bool operator()(const OneDimensionalPoint &p1,
67 const OneDimensionalPoint &p2) const {
68 return p1.frame < p2.frame;
69 }
70 };
71
72 bool operator==(const OneDimensionalPoint &p) const {
73 return (frame == p.frame && label == p.label);
74 }
75 };
76
77
78 class SparseOneDimensionalModel : public SparseModel<OneDimensionalPoint>,
79 public NoteExportable 40 public NoteExportable
80 { 41 {
81 Q_OBJECT 42 Q_OBJECT
82 43
83 public: 44 public:
84 SparseOneDimensionalModel(sv_samplerate_t sampleRate, int resolution, 45 SparseOneDimensionalModel(sv_samplerate_t sampleRate,
46 int resolution,
85 bool notifyOnAdd = true) : 47 bool notifyOnAdd = true) :
86 SparseModel<OneDimensionalPoint>(sampleRate, resolution, notifyOnAdd) 48 m_sampleRate(sampleRate),
87 { 49 m_resolution(resolution),
50 m_notifier(this,
51 notifyOnAdd ?
52 DeferredNotifier::NOTIFY_ALWAYS :
53 DeferredNotifier::NOTIFY_DEFERRED),
54 m_completion(100) {
88 PlayParameterRepository::getInstance()->addPlayable(this); 55 PlayParameterRepository::getInstance()->addPlayable(this);
89 } 56 }
90 57
91 virtual ~SparseOneDimensionalModel() 58 virtual ~SparseOneDimensionalModel() {
92 {
93 PlayParameterRepository::getInstance()->removePlayable(this); 59 PlayParameterRepository::getInstance()->removePlayable(this);
94 } 60 }
95 61
62 QString getTypeName() const override { return tr("Sparse 1-D"); }
63
64 bool isOK() const override { return true; }
65 sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); }
66 sv_frame_t getEndFrame() const override { return m_events.getEndFrame(); }
67 sv_samplerate_t getSampleRate() const override { return m_sampleRate; }
68 int getResolution() const { return m_resolution; }
69
96 bool canPlay() const override { return true; } 70 bool canPlay() const override { return true; }
97 71 QString getDefaultPlayClipId() const override { return "tap"; }
98 QString getDefaultPlayClipId() const override 72
99 { 73 int getCompletion() const { return m_completion; }
100 return "tap"; 74
101 } 75 void setCompletion(int completion, bool update = true) {
102 76
103 int getIndexOf(const Point &point) 77 { QMutexLocker locker(&m_mutex);
104 { 78 if (m_completion == completion) return;
105 // slow 79 m_completion = completion;
106 int i = 0; 80 }
107 Point::Comparator comparator; 81
108 for (PointList::const_iterator j = m_points.begin(); 82 if (update) {
109 j != m_points.end(); ++j, ++i) { 83 m_notifier.makeDeferredNotifications();
110 if (!comparator(*j, point) && !comparator(point, *j)) return i; 84 }
111 } 85
112 return -1; 86 emit completionChanged();
113 } 87
114 88 if (completion == 100) {
115 QString getTypeName() const override { return tr("Sparse 1-D"); } 89 // henceforth:
116 90 m_notifier.switchMode(DeferredNotifier::NOTIFY_ALWAYS);
91 emit modelChanged();
92 }
93 }
94
95 /**
96 * Query methods.
97 */
98
99 int getEventCount() const {
100 return m_events.count();
101 }
102 bool isEmpty() const {
103 return m_events.isEmpty();
104 }
105 bool containsEvent(const Event &e) const {
106 return m_events.contains(e);
107 }
108 EventVector getAllEvents() const {
109 return m_events.getAllEvents();
110 }
111 EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const {
112 return m_events.getEventsSpanning(f, duration);
113 }
114 EventVector getEventsCovering(sv_frame_t f) const {
115 return m_events.getEventsCovering(f);
116 }
117 EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration,
118 int overspill = 0) const {
119 return m_events.getEventsWithin(f, duration, overspill);
120 }
121 EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const {
122 return m_events.getEventsStartingWithin(f, duration);
123 }
124 EventVector getEventsStartingAt(sv_frame_t f) const {
125 return m_events.getEventsStartingAt(f);
126 }
127 bool getNearestEventMatching(sv_frame_t startSearchAt,
128 std::function<bool(Event)> predicate,
129 EventSeries::Direction direction,
130 Event &found) const {
131 return m_events.getNearestEventMatching
132 (startSearchAt, predicate, direction, found);
133 }
134
135 /**
136 * Editing methods.
137 */
138 void add(Event e) override {
139
140 { QMutexLocker locker(&m_mutex);
141 m_events.add(e.withoutValue().withoutDuration());
142 }
143
144 m_notifier.update(e.getFrame(), m_resolution);
145 }
146
147 void remove(Event e) override {
148 { QMutexLocker locker(&m_mutex);
149 m_events.remove(e);
150 }
151 emit modelChangedWithin(e.getFrame(), e.getFrame() + m_resolution);
152 }
153
117 /** 154 /**
118 * TabularModel methods. 155 * TabularModel methods.
119 */ 156 */
120 157
121 int getColumnCount() const override 158 int getRowCount() const override {
122 { 159 return m_events.count();
160 }
161
162 int getColumnCount() const override {
123 return 3; 163 return 3;
124 } 164 }
125 165
126 QString getHeading(int column) const override 166 bool isColumnTimeValue(int column) const override {
127 { 167 // NB duration is not a "time value" -- that's for columns
168 // whose sort ordering is exactly that of the frame time
169 return (column < 2);
170 }
171
172 sv_frame_t getFrameForRow(int row) const override {
173 if (row < 0 || row >= m_events.count()) {
174 return 0;
175 }
176 Event e = m_events.getEventByIndex(row);
177 return e.getFrame();
178 }
179
180 int getRowForFrame(sv_frame_t frame) const override {
181 return m_events.getIndexForEvent(Event(frame));
182 }
183
184 QString getHeading(int column) const override {
128 switch (column) { 185 switch (column) {
129 case 0: return tr("Time"); 186 case 0: return tr("Time");
130 case 1: return tr("Frame"); 187 case 1: return tr("Frame");
131 case 2: return tr("Label"); 188 case 2: return tr("Label");
132 default: return tr("Unknown"); 189 default: return tr("Unknown");
133 } 190 }
134 } 191 }
135 192
136 QVariant getData(int row, int column, int role) const override 193 SortType getSortType(int column) const override {
137 {
138 if (column < 2) {
139 return SparseModel<OneDimensionalPoint>::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: return i->label;
148 default: return QVariant();
149 }
150 }
151
152 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override
153 {
154 if (column < 2) {
155 return SparseModel<OneDimensionalPoint>::getSetDataCommand
156 (row, column, value, role);
157 }
158
159 if (role != Qt::EditRole) return 0;
160 PointListConstIterator i = getPointListIteratorForRow(row);
161 if (i == m_points.end()) return 0;
162 EditCommand *command = new EditCommand(this, tr("Edit Data"));
163
164 Point point(*i);
165 command->deletePoint(point);
166
167 switch (column) {
168 case 2: point.label = value.toString(); break;
169 }
170
171 command->addPoint(point);
172 return command->finish();
173 }
174
175
176 bool isColumnTimeValue(int column) const override
177 {
178 return (column < 2);
179 }
180
181 SortType getSortType(int column) const override
182 {
183 if (column == 2) return SortAlphabetical; 194 if (column == 2) return SortAlphabetical;
184 return SortNumeric; 195 return SortNumeric;
196 }
197
198 QVariant getData(int row, int column, int role) const override {
199
200 if (row < 0 || row >= m_events.count()) {
201 return QVariant();
202 }
203
204 Event e = m_events.getEventByIndex(row);
205
206 switch (column) {
207 case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role);
208 case 1: return int(e.getFrame());
209 case 2: return e.getLabel();
210 default: return QVariant();
211 }
212 }
213
214 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override {
215 if (row < 0 || row >= m_events.count()) return nullptr;
216 if (role != Qt::EditRole) return nullptr;
217
218 Event e0 = m_events.getEventByIndex(row);
219 Event e1;
220
221 switch (column) {
222 case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() *
223 getSampleRate()))); break;
224 case 1: e1 = e0.withFrame(value.toInt()); break;
225 case 2: e1 = e0.withLabel(value.toString()); break;
226 }
227
228 ChangeEventsCommand *command =
229 new ChangeEventsCommand(this, tr("Edit Data"));
230 command->remove(e0);
231 command->add(e1);
232 return command->finish();
185 } 233 }
186 234
187 /** 235 /**
188 * NoteExportable methods. 236 * NoteExportable methods.
189 */ 237 */
198 } 246 }
199 247
200 NoteList getNotesStartingWithin(sv_frame_t startFrame, 248 NoteList getNotesStartingWithin(sv_frame_t startFrame,
201 sv_frame_t duration) const override { 249 sv_frame_t duration) const override {
202 250
203 PointList points = getPoints(startFrame, startFrame + duration);
204 NoteList notes; 251 NoteList notes;
205 252 EventVector ee = m_events.getEventsStartingWithin(startFrame, duration);
206 for (PointList::iterator pli = 253 for (const auto &e: ee) {
207 points.begin(); pli != points.end(); ++pli) { 254 notes.push_back(e.toNoteData(getSampleRate(), true));
208 255 }
209 notes.push_back
210 (NoteData(pli->frame,
211 sv_frame_t(getSampleRate() / 6), // arbitrary short duration
212 64, // default pitch
213 100)); // default velocity
214 }
215
216 return notes; 256 return notes;
217 } 257 }
258
259 /**
260 * XmlExportable methods.
261 */
262 void toXml(QTextStream &out,
263 QString indent = "",
264 QString extraAttributes = "") const override {
265
266 Model::toXml
267 (out,
268 indent,
269 QString("type=\"sparse\" dimensions=\"1\" resolution=\"%1\" "
270 "notifyOnAdd=\"%2\" dataset=\"%3\" %4")
271 .arg(m_resolution)
272 .arg("true") // always true after model reaches 100% -
273 // subsequent events are always notified
274 .arg(getObjectExportId(&m_events))
275 .arg(extraAttributes));
276
277 m_events.toXml(out, indent, QString("dimensions=\"1\""));
278 }
279
280 protected:
281 sv_samplerate_t m_sampleRate;
282 int m_resolution;
283
284 DeferredNotifier m_notifier;
285 int m_completion;
286
287 EventSeries m_events;
288
289 mutable QMutex m_mutex;
218 }; 290 };
219 291
220 #endif 292 #endif
221 293
222 294