annotate base/Event.h @ 1630:73bda079567a single-point

Add operator!=
author Chris Cannam
date Tue, 12 Mar 2019 14:11:06 +0000
parents abc188026a48
children 687a2453cc15
rev   line source
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@1629 22 #include "DataExportOptions.h"
Chris@1615 23
Chris@1615 24 #include <vector>
Chris@1615 25 #include <stdexcept>
Chris@1611 26
Chris@1611 27 #include <QString>
Chris@1611 28
Chris@1615 29 /**
Chris@1615 30 * An immutable type used for point and event representation in sparse
Chris@1615 31 * models, as well as for interchange within the clipboard. An event
Chris@1615 32 * always has a frame and (possibly empty) label, and optionally has
Chris@1615 33 * numerical value, level, duration in frames, and a mapped reference
Chris@1615 34 * frame. Event has an operator< defining a total ordering, by frame
Chris@1615 35 * first and then by the other properties.
Chris@1615 36 *
Chris@1615 37 * Event is based on the Clipboard::Point type up to SV v3.2.1 and is
Chris@1615 38 * intended also to replace the custom point types previously found in
Chris@1615 39 * sparse models.
Chris@1615 40 */
Chris@1615 41 class Event
Chris@1611 42 {
Chris@1611 43 public:
Chris@1623 44 Event() :
Chris@1629 45 m_haveValue(false), m_haveLevel(false),
Chris@1629 46 m_haveDuration(false), m_haveReferenceFrame(false),
Chris@1623 47 m_value(0.f), m_level(0.f), m_frame(0),
Chris@1623 48 m_duration(0), m_referenceFrame(0), m_label() { }
Chris@1623 49
Chris@1615 50 Event(sv_frame_t frame) :
Chris@1629 51 m_haveValue(false), m_haveLevel(false),
Chris@1629 52 m_haveDuration(false), m_haveReferenceFrame(false),
Chris@1615 53 m_value(0.f), m_level(0.f), m_frame(frame),
Chris@1615 54 m_duration(0), m_referenceFrame(0), m_label() { }
Chris@1615 55
Chris@1615 56 Event(sv_frame_t frame, QString label) :
Chris@1629 57 m_haveValue(false), m_haveLevel(false),
Chris@1629 58 m_haveDuration(false), m_haveReferenceFrame(false),
Chris@1611 59 m_value(0.f), m_level(0.f), m_frame(frame),
Chris@1611 60 m_duration(0), m_referenceFrame(0), m_label(label) { }
Chris@1611 61
Chris@1615 62 Event(sv_frame_t frame, float value, QString label) :
Chris@1629 63 m_haveValue(true), m_haveLevel(false),
Chris@1629 64 m_haveDuration(false), m_haveReferenceFrame(false),
Chris@1611 65 m_value(value), m_level(0.f), m_frame(frame),
Chris@1611 66 m_duration(0), m_referenceFrame(0), m_label(label) { }
Chris@1611 67
Chris@1615 68 Event(sv_frame_t frame, float value, sv_frame_t duration, QString label) :
Chris@1629 69 m_haveValue(true), m_haveLevel(false),
Chris@1629 70 m_haveDuration(true), m_haveReferenceFrame(false),
Chris@1611 71 m_value(value), m_level(0.f), m_frame(frame),
Chris@1615 72 m_duration(duration), m_referenceFrame(0), m_label(label) {
Chris@1615 73 if (m_duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 74 }
Chris@1611 75
Chris@1615 76 Event(sv_frame_t frame, float value, sv_frame_t duration,
Chris@1612 77 float level, QString label) :
Chris@1629 78 m_haveValue(true), m_haveLevel(true),
Chris@1629 79 m_haveDuration(true), m_haveReferenceFrame(false),
Chris@1611 80 m_value(value), m_level(level), m_frame(frame),
Chris@1615 81 m_duration(duration), m_referenceFrame(0), m_label(label) {
Chris@1615 82 if (m_duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 83 }
Chris@1611 84
Chris@1615 85 Event(const Event &event) =default;
Chris@1615 86 Event &operator=(const Event &event) =default;
Chris@1615 87 Event &operator=(Event &&event) =default;
Chris@1611 88
Chris@1611 89 sv_frame_t getFrame() const { return m_frame; }
Chris@1611 90
Chris@1615 91 Event withFrame(sv_frame_t frame) const {
Chris@1615 92 Event p(*this);
Chris@1611 93 p.m_frame = frame;
Chris@1611 94 return p;
Chris@1611 95 }
Chris@1611 96
Chris@1615 97 bool hasValue() const { return m_haveValue; }
Chris@1611 98 float getValue() const { return m_value; }
Chris@1611 99
Chris@1615 100 Event withValue(float value) const {
Chris@1615 101 Event p(*this);
Chris@1611 102 p.m_haveValue = true;
Chris@1611 103 p.m_value = value;
Chris@1611 104 return p;
Chris@1611 105 }
Chris@1615 106 Event withoutValue() const {
Chris@1615 107 Event p(*this);
Chris@1615 108 p.m_haveValue = false;
Chris@1615 109 p.m_value = 0.f;
Chris@1615 110 return p;
Chris@1615 111 }
Chris@1611 112
Chris@1629 113 bool hasDuration() const { return m_haveDuration; }
Chris@1611 114 sv_frame_t getDuration() const { return m_duration; }
Chris@1611 115
Chris@1615 116 Event withDuration(sv_frame_t duration) const {
Chris@1615 117 Event p(*this);
Chris@1611 118 p.m_duration = duration;
Chris@1629 119 p.m_haveDuration = true;
Chris@1615 120 if (duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 121 return p;
Chris@1615 122 }
Chris@1615 123 Event withoutDuration() const {
Chris@1615 124 Event p(*this);
Chris@1629 125 p.m_haveDuration = false;
Chris@1615 126 p.m_duration = 0;
Chris@1611 127 return p;
Chris@1611 128 }
Chris@1620 129
Chris@1620 130 bool hasLabel() const { return m_label != QString(); }
Chris@1611 131 QString getLabel() const { return m_label; }
Chris@1611 132
Chris@1615 133 Event withLabel(QString label) const {
Chris@1615 134 Event p(*this);
Chris@1611 135 p.m_label = label;
Chris@1611 136 return p;
Chris@1611 137 }
Chris@1611 138
Chris@1615 139 bool hasLevel() const { return m_haveLevel; }
Chris@1611 140 float getLevel() const { return m_level; }
Chris@1615 141
Chris@1615 142 Event withLevel(float level) const {
Chris@1615 143 Event p(*this);
Chris@1611 144 p.m_haveLevel = true;
Chris@1611 145 p.m_level = level;
Chris@1611 146 return p;
Chris@1611 147 }
Chris@1615 148 Event withoutLevel() const {
Chris@1615 149 Event p(*this);
Chris@1615 150 p.m_haveLevel = false;
Chris@1615 151 p.m_level = 0.f;
Chris@1615 152 return p;
Chris@1615 153 }
Chris@1611 154
Chris@1615 155 bool hasReferenceFrame() const { return m_haveReferenceFrame; }
Chris@1611 156 sv_frame_t getReferenceFrame() const { return m_referenceFrame; }
Chris@1611 157
Chris@1615 158 bool referenceFrameDiffers() const { // from event frame
Chris@1611 159 return m_haveReferenceFrame && (m_referenceFrame != m_frame);
Chris@1611 160 }
Chris@1611 161
Chris@1615 162 Event withReferenceFrame(sv_frame_t frame) const {
Chris@1615 163 Event p(*this);
Chris@1611 164 p.m_haveReferenceFrame = true;
Chris@1611 165 p.m_referenceFrame = frame;
Chris@1611 166 return p;
Chris@1611 167 }
Chris@1615 168 Event withoutReferenceFrame() const {
Chris@1615 169 Event p(*this);
Chris@1615 170 p.m_haveReferenceFrame = false;
Chris@1615 171 p.m_referenceFrame = 0;
Chris@1615 172 return p;
Chris@1615 173 }
Chris@1612 174
Chris@1615 175 bool operator==(const Event &p) const {
Chris@1612 176
Chris@1612 177 if (m_frame != p.m_frame) return false;
Chris@1629 178
Chris@1629 179 if (m_haveDuration != p.m_haveDuration) return false;
Chris@1629 180 if (m_haveDuration && (m_duration != p.m_duration)) return false;
Chris@1612 181
Chris@1612 182 if (m_haveValue != p.m_haveValue) return false;
Chris@1612 183 if (m_haveValue && (m_value != p.m_value)) return false;
Chris@1612 184
Chris@1612 185 if (m_haveLevel != p.m_haveLevel) return false;
Chris@1612 186 if (m_haveLevel && (m_level != p.m_level)) return false;
Chris@1612 187
Chris@1612 188 if (m_haveReferenceFrame != p.m_haveReferenceFrame) return false;
Chris@1612 189 if (m_haveReferenceFrame &&
Chris@1612 190 (m_referenceFrame != p.m_referenceFrame)) return false;
Chris@1612 191
Chris@1612 192 if (m_label != p.m_label) return false;
Chris@1612 193
Chris@1612 194 return true;
Chris@1612 195 }
Chris@1612 196
Chris@1630 197 bool operator!=(const Event &p) const {
Chris@1630 198 return !operator==(p);
Chris@1630 199 }
Chris@1630 200
Chris@1615 201 bool operator<(const Event &p) const {
Chris@1612 202
Chris@1629 203 if (m_frame != p.m_frame) {
Chris@1629 204 return m_frame < p.m_frame;
Chris@1629 205 }
Chris@1612 206
Chris@1615 207 // events without a property sort before events with that property
Chris@1612 208
Chris@1629 209 if (m_haveDuration != p.m_haveDuration) {
Chris@1629 210 return !m_haveDuration;
Chris@1629 211 }
Chris@1629 212 if (m_haveDuration && (m_duration != p.m_duration)) {
Chris@1629 213 return m_duration < p.m_duration;
Chris@1629 214 }
Chris@1629 215
Chris@1629 216 if (m_haveValue != p.m_haveValue) {
Chris@1629 217 return !m_haveValue;
Chris@1629 218 }
Chris@1629 219 if (m_haveValue && (m_value != p.m_value)) {
Chris@1629 220 return m_value < p.m_value;
Chris@1629 221 }
Chris@1612 222
Chris@1629 223 if (m_haveLevel != p.m_haveLevel) {
Chris@1629 224 return !m_haveLevel;
Chris@1629 225 }
Chris@1629 226 if (m_haveLevel && (m_level != p.m_level)) {
Chris@1629 227 return m_level < p.m_level;
Chris@1629 228 }
Chris@1612 229
Chris@1612 230 if (m_haveReferenceFrame != p.m_haveReferenceFrame) {
Chris@1612 231 return !m_haveReferenceFrame;
Chris@1612 232 }
Chris@1612 233 if (m_haveReferenceFrame && (m_referenceFrame != p.m_referenceFrame)) {
Chris@1612 234 return m_referenceFrame < p.m_referenceFrame;
Chris@1612 235 }
Chris@1612 236
Chris@1612 237 return m_label < p.m_label;
Chris@1612 238 }
Chris@1612 239
Chris@1612 240 void toXml(QTextStream &stream,
Chris@1612 241 QString indent = "",
Chris@1612 242 QString extraAttributes = "") const {
Chris@1612 243
Chris@1615 244 // For I/O purposes these are points, not events
Chris@1612 245 stream << indent << QString("<point frame=\"%1\" ").arg(m_frame);
Chris@1612 246 if (m_haveValue) stream << QString("value=\"%1\" ").arg(m_value);
Chris@1629 247 if (m_haveDuration) stream << QString("duration=\"%1\" ").arg(m_duration);
Chris@1612 248 if (m_haveLevel) stream << QString("level=\"%1\" ").arg(m_level);
Chris@1612 249 if (m_haveReferenceFrame) stream << QString("referenceFrame=\"%1\" ")
Chris@1612 250 .arg(m_referenceFrame);
Chris@1612 251 stream << QString("label=\"%1\" ")
Chris@1612 252 .arg(XmlExportable::encodeEntities(m_label));
Chris@1612 253 stream << extraAttributes << ">\n";
Chris@1612 254 }
Chris@1612 255
Chris@1612 256 QString toXmlString(QString indent = "",
Chris@1612 257 QString extraAttributes = "") const {
Chris@1612 258 QString s;
Chris@1612 259 QTextStream out(&s);
Chris@1612 260 toXml(out, indent, extraAttributes);
Chris@1612 261 out.flush();
Chris@1612 262 return s;
Chris@1612 263 }
Chris@1615 264
Chris@1629 265 NoteData toNoteData(sv_samplerate_t sampleRate,
Chris@1629 266 bool valueIsMidiPitch) {
Chris@1615 267
Chris@1615 268 sv_frame_t duration;
Chris@1629 269 if (m_haveDuration && m_duration > 0) {
Chris@1615 270 duration = m_duration;
Chris@1615 271 } else {
Chris@1615 272 duration = sv_frame_t(sampleRate / 6); // arbitrary short duration
Chris@1615 273 }
Chris@1615 274
Chris@1615 275 int midiPitch;
Chris@1615 276 float frequency = 0.f;
Chris@1615 277 if (m_haveValue) {
Chris@1615 278 if (valueIsMidiPitch) {
Chris@1615 279 midiPitch = int(roundf(m_value));
Chris@1615 280 } else {
Chris@1615 281 frequency = m_value;
Chris@1615 282 midiPitch = Pitch::getPitchForFrequency(frequency);
Chris@1615 283 }
Chris@1615 284 } else {
Chris@1615 285 midiPitch = 64;
Chris@1615 286 valueIsMidiPitch = true;
Chris@1615 287 }
Chris@1615 288
Chris@1615 289 int velocity = 100;
Chris@1615 290 if (m_haveLevel) {
Chris@1615 291 if (m_level > 0.f && m_level <= 1.f) {
Chris@1615 292 velocity = int(roundf(m_level * 127.f));
Chris@1615 293 }
Chris@1615 294 }
Chris@1615 295
Chris@1615 296 NoteData n(m_frame, duration, midiPitch, velocity);
Chris@1615 297 n.isMidiPitchQuantized = valueIsMidiPitch;
Chris@1615 298 if (!valueIsMidiPitch) {
Chris@1615 299 n.frequency = frequency;
Chris@1615 300 }
Chris@1615 301
Chris@1615 302 return n;
Chris@1615 303 }
Chris@1623 304
Chris@1629 305 QString toDelimitedDataString(QString delimiter,
Chris@1629 306 DataExportOptions opts,
Chris@1629 307 sv_samplerate_t sampleRate) const {
Chris@1629 308 QStringList list;
Chris@1629 309
Chris@1629 310 list << RealTime::frame2RealTime(m_frame, sampleRate)
Chris@1629 311 .toString().c_str();
Chris@1629 312
Chris@1629 313 if (m_haveValue) {
Chris@1629 314 list << QString("%1").arg(m_value);
Chris@1629 315 }
Chris@1629 316
Chris@1629 317 if (m_haveDuration) {
Chris@1629 318 list << RealTime::frame2RealTime(m_duration, sampleRate)
Chris@1629 319 .toString().c_str();
Chris@1629 320 }
Chris@1629 321
Chris@1629 322 if (m_haveLevel) {
Chris@1629 323 if (!(opts & DataExportOmitLevels)) {
Chris@1629 324 list << QString("%1").arg(m_level);
Chris@1629 325 }
Chris@1629 326 }
Chris@1629 327
Chris@1629 328 if (m_label != "") list << m_label;
Chris@1629 329
Chris@1629 330 return list.join(delimiter);
Chris@1629 331 }
Chris@1629 332
Chris@1623 333 uint hash(uint seed = 0) const {
Chris@1623 334 uint h = qHash(m_label, seed);
Chris@1623 335 if (m_haveValue) h ^= qHash(m_value);
Chris@1623 336 if (m_haveLevel) h ^= qHash(m_level);
Chris@1623 337 h ^= qHash(m_frame);
Chris@1629 338 if (m_haveDuration) h ^= qHash(m_duration);
Chris@1623 339 if (m_haveReferenceFrame) h ^= qHash(m_referenceFrame);
Chris@1623 340 return h;
Chris@1623 341 }
Chris@1611 342
Chris@1611 343 private:
Chris@1611 344 // The order of fields here is chosen to minimise overall size of struct.
Chris@1612 345 // We potentially store very many of these objects.
Chris@1611 346 // If you change something, check what difference it makes to packing.
Chris@1611 347 bool m_haveValue : 1;
Chris@1611 348 bool m_haveLevel : 1;
Chris@1629 349 bool m_haveDuration : 1;
Chris@1611 350 bool m_haveReferenceFrame : 1;
Chris@1611 351 float m_value;
Chris@1611 352 float m_level;
Chris@1611 353 sv_frame_t m_frame;
Chris@1611 354 sv_frame_t m_duration;
Chris@1611 355 sv_frame_t m_referenceFrame;
Chris@1611 356 QString m_label;
Chris@1611 357 };
Chris@1611 358
Chris@1623 359 inline uint qHash(const Event &e, uint seed = 0) {
Chris@1623 360 return e.hash(seed);
Chris@1623 361 }
Chris@1623 362
Chris@1615 363 typedef std::vector<Event> EventVector;
Chris@1612 364
Chris@1611 365 #endif