Chris@1611
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@1611
|
2
|
Chris@1611
|
3 /*
|
Chris@1611
|
4 Sonic Visualiser
|
Chris@1611
|
5 An audio file viewer and annotation editor.
|
Chris@1611
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@1611
|
7 This file copyright 2006 Chris Cannam.
|
Chris@1611
|
8
|
Chris@1611
|
9 This program is free software; you can redistribute it and/or
|
Chris@1611
|
10 modify it under the terms of the GNU General Public License as
|
Chris@1611
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@1611
|
12 License, or (at your option) any later version. See the file
|
Chris@1611
|
13 COPYING included with this distribution for more information.
|
Chris@1611
|
14 */
|
Chris@1611
|
15
|
Chris@1615
|
16 #ifndef SV_EVENT_H
|
Chris@1615
|
17 #define SV_EVENT_H
|
Chris@1615
|
18
|
Chris@1615
|
19 #include "BaseTypes.h"
|
Chris@1615
|
20 #include "NoteData.h"
|
Chris@1615
|
21 #include "XmlExportable.h"
|
Chris@1615
|
22
|
Chris@1615
|
23 #include <vector>
|
Chris@1615
|
24 #include <stdexcept>
|
Chris@1611
|
25
|
Chris@1611
|
26 #include <QString>
|
Chris@1611
|
27
|
Chris@1615
|
28 /**
|
Chris@1615
|
29 * An immutable type used for point and event representation in sparse
|
Chris@1615
|
30 * models, as well as for interchange within the clipboard. An event
|
Chris@1615
|
31 * always has a frame and (possibly empty) label, and optionally has
|
Chris@1615
|
32 * numerical value, level, duration in frames, and a mapped reference
|
Chris@1615
|
33 * frame. Event has an operator< defining a total ordering, by frame
|
Chris@1615
|
34 * first and then by the other properties.
|
Chris@1615
|
35 *
|
Chris@1615
|
36 * Event is based on the Clipboard::Point type up to SV v3.2.1 and is
|
Chris@1615
|
37 * intended also to replace the custom point types previously found in
|
Chris@1615
|
38 * sparse models.
|
Chris@1615
|
39 */
|
Chris@1615
|
40 class Event
|
Chris@1611
|
41 {
|
Chris@1611
|
42 public:
|
Chris@1615
|
43 Event(sv_frame_t frame) :
|
Chris@1615
|
44 m_haveValue(false), m_haveLevel(false), m_haveReferenceFrame(false),
|
Chris@1615
|
45 m_value(0.f), m_level(0.f), m_frame(frame),
|
Chris@1615
|
46 m_duration(0), m_referenceFrame(0), m_label() { }
|
Chris@1615
|
47
|
Chris@1615
|
48 Event(sv_frame_t frame, QString label) :
|
Chris@1612
|
49 m_haveValue(false), m_haveLevel(false), m_haveReferenceFrame(false),
|
Chris@1611
|
50 m_value(0.f), m_level(0.f), m_frame(frame),
|
Chris@1611
|
51 m_duration(0), m_referenceFrame(0), m_label(label) { }
|
Chris@1611
|
52
|
Chris@1615
|
53 Event(sv_frame_t frame, float value, QString label) :
|
Chris@1612
|
54 m_haveValue(true), m_haveLevel(false), m_haveReferenceFrame(false),
|
Chris@1611
|
55 m_value(value), m_level(0.f), m_frame(frame),
|
Chris@1611
|
56 m_duration(0), m_referenceFrame(0), m_label(label) { }
|
Chris@1611
|
57
|
Chris@1615
|
58 Event(sv_frame_t frame, float value, sv_frame_t duration, QString label) :
|
Chris@1612
|
59 m_haveValue(true), m_haveLevel(false), m_haveReferenceFrame(false),
|
Chris@1611
|
60 m_value(value), m_level(0.f), m_frame(frame),
|
Chris@1615
|
61 m_duration(duration), m_referenceFrame(0), m_label(label) {
|
Chris@1615
|
62 if (m_duration < 0) throw std::logic_error("duration must be >= 0");
|
Chris@1615
|
63 }
|
Chris@1611
|
64
|
Chris@1615
|
65 Event(sv_frame_t frame, float value, sv_frame_t duration,
|
Chris@1612
|
66 float level, QString label) :
|
Chris@1612
|
67 m_haveValue(true), m_haveLevel(true), m_haveReferenceFrame(false),
|
Chris@1611
|
68 m_value(value), m_level(level), m_frame(frame),
|
Chris@1615
|
69 m_duration(duration), m_referenceFrame(0), m_label(label) {
|
Chris@1615
|
70 if (m_duration < 0) throw std::logic_error("duration must be >= 0");
|
Chris@1615
|
71 }
|
Chris@1611
|
72
|
Chris@1615
|
73 Event(const Event &event) =default;
|
Chris@1615
|
74 Event &operator=(const Event &event) =default;
|
Chris@1615
|
75 Event &operator=(Event &&event) =default;
|
Chris@1611
|
76
|
Chris@1611
|
77 sv_frame_t getFrame() const { return m_frame; }
|
Chris@1611
|
78
|
Chris@1615
|
79 Event withFrame(sv_frame_t frame) const {
|
Chris@1615
|
80 Event p(*this);
|
Chris@1611
|
81 p.m_frame = frame;
|
Chris@1611
|
82 return p;
|
Chris@1611
|
83 }
|
Chris@1611
|
84
|
Chris@1615
|
85 bool hasValue() const { return m_haveValue; }
|
Chris@1611
|
86 float getValue() const { return m_value; }
|
Chris@1611
|
87
|
Chris@1615
|
88 Event withValue(float value) const {
|
Chris@1615
|
89 Event p(*this);
|
Chris@1611
|
90 p.m_haveValue = true;
|
Chris@1611
|
91 p.m_value = value;
|
Chris@1611
|
92 return p;
|
Chris@1611
|
93 }
|
Chris@1615
|
94 Event withoutValue() const {
|
Chris@1615
|
95 Event p(*this);
|
Chris@1615
|
96 p.m_haveValue = false;
|
Chris@1615
|
97 p.m_value = 0.f;
|
Chris@1615
|
98 return p;
|
Chris@1615
|
99 }
|
Chris@1611
|
100
|
Chris@1615
|
101 bool hasDuration() const { return m_duration != 0; }
|
Chris@1611
|
102 sv_frame_t getDuration() const { return m_duration; }
|
Chris@1611
|
103
|
Chris@1615
|
104 Event withDuration(sv_frame_t duration) const {
|
Chris@1615
|
105 Event p(*this);
|
Chris@1611
|
106 p.m_duration = duration;
|
Chris@1615
|
107 if (duration < 0) throw std::logic_error("duration must be >= 0");
|
Chris@1615
|
108 return p;
|
Chris@1615
|
109 }
|
Chris@1615
|
110 Event withoutDuration() const {
|
Chris@1615
|
111 Event p(*this);
|
Chris@1615
|
112 p.m_duration = 0;
|
Chris@1611
|
113 return p;
|
Chris@1611
|
114 }
|
Chris@1620
|
115
|
Chris@1620
|
116 bool hasLabel() const { return m_label != QString(); }
|
Chris@1611
|
117 QString getLabel() const { return m_label; }
|
Chris@1611
|
118
|
Chris@1615
|
119 Event withLabel(QString label) const {
|
Chris@1615
|
120 Event p(*this);
|
Chris@1611
|
121 p.m_label = label;
|
Chris@1611
|
122 return p;
|
Chris@1611
|
123 }
|
Chris@1611
|
124
|
Chris@1615
|
125 bool hasLevel() const { return m_haveLevel; }
|
Chris@1611
|
126 float getLevel() const { return m_level; }
|
Chris@1615
|
127
|
Chris@1615
|
128 Event withLevel(float level) const {
|
Chris@1615
|
129 Event p(*this);
|
Chris@1611
|
130 p.m_haveLevel = true;
|
Chris@1611
|
131 p.m_level = level;
|
Chris@1611
|
132 return p;
|
Chris@1611
|
133 }
|
Chris@1615
|
134 Event withoutLevel() const {
|
Chris@1615
|
135 Event p(*this);
|
Chris@1615
|
136 p.m_haveLevel = false;
|
Chris@1615
|
137 p.m_level = 0.f;
|
Chris@1615
|
138 return p;
|
Chris@1615
|
139 }
|
Chris@1611
|
140
|
Chris@1615
|
141 bool hasReferenceFrame() const { return m_haveReferenceFrame; }
|
Chris@1611
|
142 sv_frame_t getReferenceFrame() const { return m_referenceFrame; }
|
Chris@1611
|
143
|
Chris@1615
|
144 bool referenceFrameDiffers() const { // from event frame
|
Chris@1611
|
145 return m_haveReferenceFrame && (m_referenceFrame != m_frame);
|
Chris@1611
|
146 }
|
Chris@1611
|
147
|
Chris@1615
|
148 Event withReferenceFrame(sv_frame_t frame) const {
|
Chris@1615
|
149 Event p(*this);
|
Chris@1611
|
150 p.m_haveReferenceFrame = true;
|
Chris@1611
|
151 p.m_referenceFrame = frame;
|
Chris@1611
|
152 return p;
|
Chris@1611
|
153 }
|
Chris@1615
|
154 Event withoutReferenceFrame() const {
|
Chris@1615
|
155 Event p(*this);
|
Chris@1615
|
156 p.m_haveReferenceFrame = false;
|
Chris@1615
|
157 p.m_referenceFrame = 0;
|
Chris@1615
|
158 return p;
|
Chris@1615
|
159 }
|
Chris@1612
|
160
|
Chris@1615
|
161 bool operator==(const Event &p) const {
|
Chris@1612
|
162
|
Chris@1612
|
163 if (m_frame != p.m_frame) return false;
|
Chris@1615
|
164 if (m_duration != p.m_duration) return false;
|
Chris@1612
|
165
|
Chris@1612
|
166 if (m_haveValue != p.m_haveValue) return false;
|
Chris@1612
|
167 if (m_haveValue && (m_value != p.m_value)) return false;
|
Chris@1612
|
168
|
Chris@1612
|
169 if (m_haveLevel != p.m_haveLevel) return false;
|
Chris@1612
|
170 if (m_haveLevel && (m_level != p.m_level)) return false;
|
Chris@1612
|
171
|
Chris@1612
|
172 if (m_haveReferenceFrame != p.m_haveReferenceFrame) return false;
|
Chris@1612
|
173 if (m_haveReferenceFrame &&
|
Chris@1612
|
174 (m_referenceFrame != p.m_referenceFrame)) return false;
|
Chris@1612
|
175
|
Chris@1612
|
176 if (m_label != p.m_label) return false;
|
Chris@1612
|
177
|
Chris@1612
|
178 return true;
|
Chris@1612
|
179 }
|
Chris@1612
|
180
|
Chris@1615
|
181 bool operator<(const Event &p) const {
|
Chris@1612
|
182
|
Chris@1612
|
183 if (m_frame != p.m_frame) return m_frame < p.m_frame;
|
Chris@1615
|
184 if (m_duration != p.m_duration) return m_duration < p.m_duration;
|
Chris@1612
|
185
|
Chris@1615
|
186 // events without a property sort before events with that property
|
Chris@1612
|
187
|
Chris@1612
|
188 if (m_haveValue != p.m_haveValue) return !m_haveValue;
|
Chris@1612
|
189 if (m_haveValue && (m_value != p.m_value)) return m_value < p.m_value;
|
Chris@1612
|
190
|
Chris@1612
|
191 if (m_haveLevel != p.m_haveLevel) return !m_haveLevel;
|
Chris@1612
|
192 if (m_haveLevel && (m_level != p.m_level)) return m_level < p.m_level;
|
Chris@1612
|
193
|
Chris@1612
|
194 if (m_haveReferenceFrame != p.m_haveReferenceFrame) {
|
Chris@1612
|
195 return !m_haveReferenceFrame;
|
Chris@1612
|
196 }
|
Chris@1612
|
197 if (m_haveReferenceFrame && (m_referenceFrame != p.m_referenceFrame)) {
|
Chris@1612
|
198 return m_referenceFrame < p.m_referenceFrame;
|
Chris@1612
|
199 }
|
Chris@1612
|
200
|
Chris@1612
|
201 return m_label < p.m_label;
|
Chris@1612
|
202 }
|
Chris@1612
|
203
|
Chris@1612
|
204 void toXml(QTextStream &stream,
|
Chris@1612
|
205 QString indent = "",
|
Chris@1612
|
206 QString extraAttributes = "") const {
|
Chris@1612
|
207
|
Chris@1615
|
208 // For I/O purposes these are points, not events
|
Chris@1612
|
209 stream << indent << QString("<point frame=\"%1\" ").arg(m_frame);
|
Chris@1612
|
210 if (m_haveValue) stream << QString("value=\"%1\" ").arg(m_value);
|
Chris@1612
|
211 if (m_duration) stream << QString("duration=\"%1\" ").arg(m_duration);
|
Chris@1612
|
212 if (m_haveLevel) stream << QString("level=\"%1\" ").arg(m_level);
|
Chris@1612
|
213 if (m_haveReferenceFrame) stream << QString("referenceFrame=\"%1\" ")
|
Chris@1612
|
214 .arg(m_referenceFrame);
|
Chris@1612
|
215 stream << QString("label=\"%1\" ")
|
Chris@1612
|
216 .arg(XmlExportable::encodeEntities(m_label));
|
Chris@1612
|
217 stream << extraAttributes << ">\n";
|
Chris@1612
|
218 }
|
Chris@1612
|
219
|
Chris@1612
|
220 QString toXmlString(QString indent = "",
|
Chris@1612
|
221 QString extraAttributes = "") const {
|
Chris@1612
|
222 QString s;
|
Chris@1612
|
223 QTextStream out(&s);
|
Chris@1612
|
224 toXml(out, indent, extraAttributes);
|
Chris@1612
|
225 out.flush();
|
Chris@1612
|
226 return s;
|
Chris@1612
|
227 }
|
Chris@1615
|
228
|
Chris@1615
|
229 NoteData toNoteData(sv_samplerate_t sampleRate, bool valueIsMidiPitch) {
|
Chris@1615
|
230
|
Chris@1615
|
231 sv_frame_t duration;
|
Chris@1615
|
232 if (m_duration > 0) {
|
Chris@1615
|
233 duration = m_duration;
|
Chris@1615
|
234 } else {
|
Chris@1615
|
235 duration = sv_frame_t(sampleRate / 6); // arbitrary short duration
|
Chris@1615
|
236 }
|
Chris@1615
|
237
|
Chris@1615
|
238 int midiPitch;
|
Chris@1615
|
239 float frequency = 0.f;
|
Chris@1615
|
240 if (m_haveValue) {
|
Chris@1615
|
241 if (valueIsMidiPitch) {
|
Chris@1615
|
242 midiPitch = int(roundf(m_value));
|
Chris@1615
|
243 } else {
|
Chris@1615
|
244 frequency = m_value;
|
Chris@1615
|
245 midiPitch = Pitch::getPitchForFrequency(frequency);
|
Chris@1615
|
246 }
|
Chris@1615
|
247 } else {
|
Chris@1615
|
248 midiPitch = 64;
|
Chris@1615
|
249 valueIsMidiPitch = true;
|
Chris@1615
|
250 }
|
Chris@1615
|
251
|
Chris@1615
|
252 int velocity = 100;
|
Chris@1615
|
253 if (m_haveLevel) {
|
Chris@1615
|
254 if (m_level > 0.f && m_level <= 1.f) {
|
Chris@1615
|
255 velocity = int(roundf(m_level * 127.f));
|
Chris@1615
|
256 }
|
Chris@1615
|
257 }
|
Chris@1615
|
258
|
Chris@1615
|
259 NoteData n(m_frame, duration, midiPitch, velocity);
|
Chris@1615
|
260 n.isMidiPitchQuantized = valueIsMidiPitch;
|
Chris@1615
|
261 if (!valueIsMidiPitch) {
|
Chris@1615
|
262 n.frequency = frequency;
|
Chris@1615
|
263 }
|
Chris@1615
|
264
|
Chris@1615
|
265 return n;
|
Chris@1615
|
266 }
|
Chris@1611
|
267
|
Chris@1611
|
268 private:
|
Chris@1611
|
269 // The order of fields here is chosen to minimise overall size of struct.
|
Chris@1612
|
270 // We potentially store very many of these objects.
|
Chris@1611
|
271 // If you change something, check what difference it makes to packing.
|
Chris@1611
|
272 bool m_haveValue : 1;
|
Chris@1611
|
273 bool m_haveLevel : 1;
|
Chris@1611
|
274 bool m_haveReferenceFrame : 1;
|
Chris@1611
|
275 float m_value;
|
Chris@1611
|
276 float m_level;
|
Chris@1611
|
277 sv_frame_t m_frame;
|
Chris@1611
|
278 sv_frame_t m_duration;
|
Chris@1611
|
279 sv_frame_t m_referenceFrame;
|
Chris@1611
|
280 QString m_label;
|
Chris@1611
|
281 };
|
Chris@1611
|
282
|
Chris@1615
|
283 typedef std::vector<Event> EventVector;
|
Chris@1612
|
284
|
Chris@1611
|
285 #endif
|