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_SPARSE_TIME_VALUE_MODEL_H
16 #define SV_SPARSE_TIME_VALUE_MODEL_H
17 
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"
26 #include "base/PlayParameterRepository.h"
27 
28 #include "system/System.h"
29 
30 /**
31  * A model representing a wiggly-line plot with points at arbitrary
32  * intervals of the model resolution.
33  */
34 class SparseTimeValueModel : public Model,
35                              public TabularModel,
36                              public EventEditable
37 {
38     Q_OBJECT
39 
40 public:
41     SparseTimeValueModel(sv_samplerate_t sampleRate,
42                          int resolution,
43                          bool notifyOnAdd = true) :
m_sampleRate(sampleRate)44         m_sampleRate(sampleRate),
45         m_resolution(resolution),
46         m_valueMinimum(0.f),
47         m_valueMaximum(0.f),
48         m_haveExtents(false),
49         m_haveTextLabels(false),
50         m_notifier(this,
51                    getId(),
52                    notifyOnAdd ?
53                    DeferredNotifier::NOTIFY_ALWAYS :
54                    DeferredNotifier::NOTIFY_DEFERRED),
55         m_completion(100) {
56         // Model is playable, but may not sound (if units not Hz or
57         // range unsuitable)
58         PlayParameterRepository::getInstance()->addPlayable
59             (getId().untyped, this);
60     }
61 
62     SparseTimeValueModel(sv_samplerate_t sampleRate, int resolution,
63                          float valueMinimum, float valueMaximum,
64                          bool notifyOnAdd = true) :
m_sampleRate(sampleRate)65         m_sampleRate(sampleRate),
66         m_resolution(resolution),
67         m_valueMinimum(valueMinimum),
68         m_valueMaximum(valueMaximum),
69         m_haveExtents(true),
70         m_haveTextLabels(false),
71         m_notifier(this,
72                    getId(),
73                    notifyOnAdd ?
74                    DeferredNotifier::NOTIFY_ALWAYS :
75                    DeferredNotifier::NOTIFY_DEFERRED),
76         m_completion(100) {
77         // Model is playable, but may not sound (if units not Hz or
78         // range unsuitable)
79         PlayParameterRepository::getInstance()->addPlayable
80             (getId().untyped, this);
81     }
82 
~SparseTimeValueModel()83     virtual ~SparseTimeValueModel() {
84         PlayParameterRepository::getInstance()->removePlayable
85             (getId().untyped);
86     }
87 
getTypeName()88     QString getTypeName() const override { return tr("Sparse Time-Value"); }
isSparse()89     bool isSparse() const override { return true; }
isOK()90     bool isOK() const override { return true; }
91 
getStartFrame()92     sv_frame_t getStartFrame() const override {
93         return m_events.getStartFrame();
94     }
getTrueEndFrame()95     sv_frame_t getTrueEndFrame() const override {
96         if (m_events.isEmpty()) return 0;
97         sv_frame_t e = m_events.getEndFrame() + 1;
98         if (e % m_resolution == 0) return e;
99         else return (e / m_resolution + 1) * m_resolution;
100     }
101 
getSampleRate()102     sv_samplerate_t getSampleRate() const override { return m_sampleRate; }
getResolution()103     int getResolution() const { return m_resolution; }
104 
canPlay()105     bool canPlay() const override { return true; }
getDefaultPlayAudible()106     bool getDefaultPlayAudible() const override { return false; } // user must unmute
107 
getScaleUnits()108     QString getScaleUnits() const {
109         QMutexLocker locker(&m_mutex);
110         return m_units;
111     }
setScaleUnits(QString units)112     void setScaleUnits(QString units) {
113         QMutexLocker locker(&m_mutex);
114         m_units = units;
115         UnitDatabase::getInstance()->registerUnit(units);
116     }
117 
hasTextLabels()118     bool hasTextLabels() const { return m_haveTextLabels; }
119 
getValueMinimum()120     float getValueMinimum() const { return m_valueMinimum; }
getValueMaximum()121     float getValueMaximum() const { return m_valueMaximum; }
122 
getCompletion()123     int getCompletion() const override { return m_completion; }
124 
125     void setCompletion(int completion, bool update = true) {
126 
127         {
128             if (m_completion == completion) return;
129             m_completion = completion;
130         }
131 
132         if (update) {
133             m_notifier.makeDeferredNotifications();
134         }
135 
136         emit completionChanged(getId());
137 
138         if (completion == 100) {
139             // henceforth:
140             m_notifier.switchMode(DeferredNotifier::NOTIFY_ALWAYS);
141             emit modelChanged(getId());
142         }
143     }
144 
145     /**
146      * Query methods.
147      */
148 
getEventCount()149     int getEventCount() const {
150         return m_events.count();
151     }
isEmpty()152     bool isEmpty() const {
153         return m_events.isEmpty();
154     }
containsEvent(const Event & e)155     bool containsEvent(const Event &e) const {
156         return m_events.contains(e);
157     }
getAllEvents()158     EventVector getAllEvents() const {
159         return m_events.getAllEvents();
160     }
getEventsSpanning(sv_frame_t f,sv_frame_t duration)161     EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const {
162         return m_events.getEventsSpanning(f, duration);
163     }
getEventsCovering(sv_frame_t f)164     EventVector getEventsCovering(sv_frame_t f) const {
165         return m_events.getEventsCovering(f);
166     }
167     EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration,
168                                 int overspill = 0) const {
169         return m_events.getEventsWithin(f, duration, overspill);
170     }
getEventsStartingWithin(sv_frame_t f,sv_frame_t duration)171     EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const {
172         return m_events.getEventsStartingWithin(f, duration);
173     }
getEventsStartingAt(sv_frame_t f)174     EventVector getEventsStartingAt(sv_frame_t f) const {
175         return m_events.getEventsStartingAt(f);
176     }
getNearestEventMatching(sv_frame_t startSearchAt,std::function<bool (Event)> predicate,EventSeries::Direction direction,Event & found)177     bool getNearestEventMatching(sv_frame_t startSearchAt,
178                                  std::function<bool(Event)> predicate,
179                                  EventSeries::Direction direction,
180                                  Event &found) const {
181         return m_events.getNearestEventMatching
182             (startSearchAt, predicate, direction, found);
183     }
184 
185     /**
186      * Editing methods.
187      */
add(Event e)188     void add(Event e) override {
189 
190         bool allChange = false;
191 
192         m_events.add(e.withoutDuration()); // can't have duration here
193 
194         if (e.getLabel() != "") {
195             m_haveTextLabels = true;
196         }
197 
198         float v = e.getValue();
199         if (!ISNAN(v) && !ISINF(v)) {
200             if (!m_haveExtents || v < m_valueMinimum) {
201                 m_valueMinimum = v; allChange = true;
202             }
203             if (!m_haveExtents || v > m_valueMaximum) {
204                 m_valueMaximum = v; allChange = true;
205             }
206             m_haveExtents = true;
207         }
208 
209         m_notifier.update(e.getFrame(), m_resolution);
210 
211         if (allChange) {
212             emit modelChanged(getId());
213         }
214     }
215 
remove(Event e)216     void remove(Event e) override {
217         m_events.remove(e);
218         emit modelChangedWithin(getId(),
219                                 e.getFrame(), e.getFrame() + m_resolution);
220     }
221 
222     /**
223      * TabularModel methods.
224      */
225 
getRowCount()226     int getRowCount() const override {
227         return m_events.count();
228     }
229 
getColumnCount()230     int getColumnCount() const override {
231         return 4;
232     }
233 
isColumnTimeValue(int column)234     bool isColumnTimeValue(int column) const override {
235         return (column < 2);
236     }
237 
getFrameForRow(int row)238     sv_frame_t getFrameForRow(int row) const override {
239         if (row < 0 || row >= m_events.count()) {
240             return 0;
241         }
242         Event e = m_events.getEventByIndex(row);
243         return e.getFrame();
244     }
245 
getRowForFrame(sv_frame_t frame)246     int getRowForFrame(sv_frame_t frame) const override {
247         return m_events.getIndexForEvent(Event(frame));
248     }
249 
getHeading(int column)250     QString getHeading(int column) const override {
251         switch (column) {
252         case 0: return tr("Time");
253         case 1: return tr("Frame");
254         case 2: return tr("Value");
255         case 3: return tr("Label");
256         default: return tr("Unknown");
257         }
258     }
259 
getSortType(int column)260     SortType getSortType(int column) const override {
261         if (column == 3) return SortAlphabetical;
262         return SortNumeric;
263     }
264 
getData(int row,int column,int role)265     QVariant getData(int row, int column, int role) const override {
266 
267         if (row < 0 || row >= m_events.count()) {
268             return QVariant();
269         }
270 
271         Event e = m_events.getEventByIndex(row);
272 
273         switch (column) {
274         case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role);
275         case 1: return int(e.getFrame());
276         case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role);
277         case 3: return e.getLabel();
278         default: return QVariant();
279         }
280     }
281 
isEditable()282     bool isEditable() const override { return true; }
283 
getSetDataCommand(int row,int column,const QVariant & value,int role)284     Command *getSetDataCommand(int row, int column, const QVariant &value,
285                                int role) override {
286         if (row < 0 || row >= m_events.count()) return nullptr;
287         if (role != Qt::EditRole) return nullptr;
288 
289         Event e0 = m_events.getEventByIndex(row);
290         Event e1;
291 
292         switch (column) {
293         case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() *
294                                                    getSampleRate()))); break;
295         case 1: e1 = e0.withFrame(value.toInt()); break;
296         case 2: e1 = e0.withValue(float(value.toDouble())); break;
297         case 3: e1 = e0.withLabel(value.toString()); break;
298         }
299 
300         auto command = new ChangeEventsCommand(getId().untyped, tr("Edit Data"));
301         command->remove(e0);
302         command->add(e1);
303         return command->finish();
304     }
305 
getInsertRowCommand(int row)306     Command *getInsertRowCommand(int row) override {
307         if (row < 0 || row >= m_events.count()) return nullptr;
308         auto command = new ChangeEventsCommand(getId().untyped,
309                                                tr("Add Point"));
310         Event e = m_events.getEventByIndex(row);
311         command->add(e);
312         return command->finish();
313     }
314 
getRemoveRowCommand(int row)315     Command *getRemoveRowCommand(int row) override {
316         if (row < 0 || row >= m_events.count()) return nullptr;
317         auto command = new ChangeEventsCommand(getId().untyped,
318                                                tr("Delete Point"));
319         Event e = m_events.getEventByIndex(row);
320         command->remove(e);
321         return command->finish();
322     }
323 
324     /**
325      * XmlExportable methods.
326      */
327     void toXml(QTextStream &out,
328                QString indent = "",
329                QString extraAttributes = "") const override {
330 
331         Model::toXml
332             (out,
333              indent,
334              QString("type=\"sparse\" dimensions=\"2\" resolution=\"%1\" "
335                      "notifyOnAdd=\"%2\" dataset=\"%3\" "
336                      "minimum=\"%4\" maximum=\"%5\" "
337                      "units=\"%6\" %7")
338              .arg(m_resolution)
339              .arg("true") // always true after model reaches 100% -
340                           // subsequent events are always notified
341              .arg(m_events.getExportId())
342              .arg(m_valueMinimum)
343              .arg(m_valueMaximum)
344              .arg(encodeEntities(m_units))
345              .arg(extraAttributes));
346 
347         m_events.toXml(out, indent, QString("dimensions=\"2\""));
348     }
349 
toDelimitedDataString(QString delimiter,DataExportOptions options,sv_frame_t startFrame,sv_frame_t duration)350     QString toDelimitedDataString(QString delimiter,
351                                   DataExportOptions options,
352                                   sv_frame_t startFrame,
353                                   sv_frame_t duration) const override {
354         return m_events.toDelimitedDataString(delimiter,
355                                               options,
356                                               startFrame,
357                                               duration,
358                                               m_sampleRate,
359                                               m_resolution,
360                                               Event().withValue(0.f));
361     }
362 
363 protected:
364     sv_samplerate_t m_sampleRate;
365     int m_resolution;
366 
367     std::atomic<float> m_valueMinimum;
368     std::atomic<float> m_valueMaximum;
369     std::atomic<bool> m_haveExtents;
370     std::atomic<bool> m_haveTextLabels;
371     QString m_units;
372     DeferredNotifier m_notifier;
373     std::atomic<int> m_completion;
374 
375     EventSeries m_events;
376 };
377 
378 #endif
379 
380 
381 
382