annotate base/Event.h @ 1623:676e32fa403e single-point

Permit hashing
author Chris Cannam
date Mon, 11 Mar 2019 11:17:18 +0000
parents 2f9deb8d3295
children 895186c43fce
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@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@1623 43 Event() :
Chris@1623 44 m_haveValue(false), m_haveLevel(false), m_haveReferenceFrame(false),
Chris@1623 45 m_value(0.f), m_level(0.f), m_frame(0),
Chris@1623 46 m_duration(0), m_referenceFrame(0), m_label() { }
Chris@1623 47
Chris@1615 48 Event(sv_frame_t frame) :
Chris@1615 49 m_haveValue(false), m_haveLevel(false), m_haveReferenceFrame(false),
Chris@1615 50 m_value(0.f), m_level(0.f), m_frame(frame),
Chris@1615 51 m_duration(0), m_referenceFrame(0), m_label() { }
Chris@1615 52
Chris@1615 53 Event(sv_frame_t frame, QString label) :
Chris@1612 54 m_haveValue(false), m_haveLevel(false), m_haveReferenceFrame(false),
Chris@1611 55 m_value(0.f), 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, 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@1611 61 m_duration(0), m_referenceFrame(0), m_label(label) { }
Chris@1611 62
Chris@1615 63 Event(sv_frame_t frame, float value, sv_frame_t duration, QString label) :
Chris@1612 64 m_haveValue(true), m_haveLevel(false), m_haveReferenceFrame(false),
Chris@1611 65 m_value(value), m_level(0.f), m_frame(frame),
Chris@1615 66 m_duration(duration), m_referenceFrame(0), m_label(label) {
Chris@1615 67 if (m_duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 68 }
Chris@1611 69
Chris@1615 70 Event(sv_frame_t frame, float value, sv_frame_t duration,
Chris@1612 71 float level, QString label) :
Chris@1612 72 m_haveValue(true), m_haveLevel(true), m_haveReferenceFrame(false),
Chris@1611 73 m_value(value), m_level(level), m_frame(frame),
Chris@1615 74 m_duration(duration), m_referenceFrame(0), m_label(label) {
Chris@1615 75 if (m_duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 76 }
Chris@1611 77
Chris@1615 78 Event(const Event &event) =default;
Chris@1615 79 Event &operator=(const Event &event) =default;
Chris@1615 80 Event &operator=(Event &&event) =default;
Chris@1611 81
Chris@1611 82 sv_frame_t getFrame() const { return m_frame; }
Chris@1611 83
Chris@1615 84 Event withFrame(sv_frame_t frame) const {
Chris@1615 85 Event p(*this);
Chris@1611 86 p.m_frame = frame;
Chris@1611 87 return p;
Chris@1611 88 }
Chris@1611 89
Chris@1615 90 bool hasValue() const { return m_haveValue; }
Chris@1611 91 float getValue() const { return m_value; }
Chris@1611 92
Chris@1615 93 Event withValue(float value) const {
Chris@1615 94 Event p(*this);
Chris@1611 95 p.m_haveValue = true;
Chris@1611 96 p.m_value = value;
Chris@1611 97 return p;
Chris@1611 98 }
Chris@1615 99 Event withoutValue() const {
Chris@1615 100 Event p(*this);
Chris@1615 101 p.m_haveValue = false;
Chris@1615 102 p.m_value = 0.f;
Chris@1615 103 return p;
Chris@1615 104 }
Chris@1611 105
Chris@1615 106 bool hasDuration() const { return m_duration != 0; }
Chris@1611 107 sv_frame_t getDuration() const { return m_duration; }
Chris@1611 108
Chris@1615 109 Event withDuration(sv_frame_t duration) const {
Chris@1615 110 Event p(*this);
Chris@1611 111 p.m_duration = duration;
Chris@1615 112 if (duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 113 return p;
Chris@1615 114 }
Chris@1615 115 Event withoutDuration() const {
Chris@1615 116 Event p(*this);
Chris@1615 117 p.m_duration = 0;
Chris@1611 118 return p;
Chris@1611 119 }
Chris@1620 120
Chris@1620 121 bool hasLabel() const { return m_label != QString(); }
Chris@1611 122 QString getLabel() const { return m_label; }
Chris@1611 123
Chris@1615 124 Event withLabel(QString label) const {
Chris@1615 125 Event p(*this);
Chris@1611 126 p.m_label = label;
Chris@1611 127 return p;
Chris@1611 128 }
Chris@1611 129
Chris@1615 130 bool hasLevel() const { return m_haveLevel; }
Chris@1611 131 float getLevel() const { return m_level; }
Chris@1615 132
Chris@1615 133 Event withLevel(float level) const {
Chris@1615 134 Event p(*this);
Chris@1611 135 p.m_haveLevel = true;
Chris@1611 136 p.m_level = level;
Chris@1611 137 return p;
Chris@1611 138 }
Chris@1615 139 Event withoutLevel() const {
Chris@1615 140 Event p(*this);
Chris@1615 141 p.m_haveLevel = false;
Chris@1615 142 p.m_level = 0.f;
Chris@1615 143 return p;
Chris@1615 144 }
Chris@1611 145
Chris@1615 146 bool hasReferenceFrame() const { return m_haveReferenceFrame; }
Chris@1611 147 sv_frame_t getReferenceFrame() const { return m_referenceFrame; }
Chris@1611 148
Chris@1615 149 bool referenceFrameDiffers() const { // from event frame
Chris@1611 150 return m_haveReferenceFrame && (m_referenceFrame != m_frame);
Chris@1611 151 }
Chris@1611 152
Chris@1615 153 Event withReferenceFrame(sv_frame_t frame) const {
Chris@1615 154 Event p(*this);
Chris@1611 155 p.m_haveReferenceFrame = true;
Chris@1611 156 p.m_referenceFrame = frame;
Chris@1611 157 return p;
Chris@1611 158 }
Chris@1615 159 Event withoutReferenceFrame() const {
Chris@1615 160 Event p(*this);
Chris@1615 161 p.m_haveReferenceFrame = false;
Chris@1615 162 p.m_referenceFrame = 0;
Chris@1615 163 return p;
Chris@1615 164 }
Chris@1612 165
Chris@1615 166 bool operator==(const Event &p) const {
Chris@1612 167
Chris@1612 168 if (m_frame != p.m_frame) return false;
Chris@1615 169 if (m_duration != p.m_duration) return false;
Chris@1612 170
Chris@1612 171 if (m_haveValue != p.m_haveValue) return false;
Chris@1612 172 if (m_haveValue && (m_value != p.m_value)) return false;
Chris@1612 173
Chris@1612 174 if (m_haveLevel != p.m_haveLevel) return false;
Chris@1612 175 if (m_haveLevel && (m_level != p.m_level)) return false;
Chris@1612 176
Chris@1612 177 if (m_haveReferenceFrame != p.m_haveReferenceFrame) return false;
Chris@1612 178 if (m_haveReferenceFrame &&
Chris@1612 179 (m_referenceFrame != p.m_referenceFrame)) return false;
Chris@1612 180
Chris@1612 181 if (m_label != p.m_label) return false;
Chris@1612 182
Chris@1612 183 return true;
Chris@1612 184 }
Chris@1612 185
Chris@1615 186 bool operator<(const Event &p) const {
Chris@1612 187
Chris@1612 188 if (m_frame != p.m_frame) return m_frame < p.m_frame;
Chris@1615 189 if (m_duration != p.m_duration) return m_duration < p.m_duration;
Chris@1612 190
Chris@1615 191 // events without a property sort before events with that property
Chris@1612 192
Chris@1612 193 if (m_haveValue != p.m_haveValue) return !m_haveValue;
Chris@1612 194 if (m_haveValue && (m_value != p.m_value)) return m_value < p.m_value;
Chris@1612 195
Chris@1612 196 if (m_haveLevel != p.m_haveLevel) return !m_haveLevel;
Chris@1612 197 if (m_haveLevel && (m_level != p.m_level)) return m_level < p.m_level;
Chris@1612 198
Chris@1612 199 if (m_haveReferenceFrame != p.m_haveReferenceFrame) {
Chris@1612 200 return !m_haveReferenceFrame;
Chris@1612 201 }
Chris@1612 202 if (m_haveReferenceFrame && (m_referenceFrame != p.m_referenceFrame)) {
Chris@1612 203 return m_referenceFrame < p.m_referenceFrame;
Chris@1612 204 }
Chris@1612 205
Chris@1612 206 return m_label < p.m_label;
Chris@1612 207 }
Chris@1612 208
Chris@1612 209 void toXml(QTextStream &stream,
Chris@1612 210 QString indent = "",
Chris@1612 211 QString extraAttributes = "") const {
Chris@1612 212
Chris@1615 213 // For I/O purposes these are points, not events
Chris@1612 214 stream << indent << QString("<point frame=\"%1\" ").arg(m_frame);
Chris@1612 215 if (m_haveValue) stream << QString("value=\"%1\" ").arg(m_value);
Chris@1612 216 if (m_duration) stream << QString("duration=\"%1\" ").arg(m_duration);
Chris@1612 217 if (m_haveLevel) stream << QString("level=\"%1\" ").arg(m_level);
Chris@1612 218 if (m_haveReferenceFrame) stream << QString("referenceFrame=\"%1\" ")
Chris@1612 219 .arg(m_referenceFrame);
Chris@1612 220 stream << QString("label=\"%1\" ")
Chris@1612 221 .arg(XmlExportable::encodeEntities(m_label));
Chris@1612 222 stream << extraAttributes << ">\n";
Chris@1612 223 }
Chris@1612 224
Chris@1612 225 QString toXmlString(QString indent = "",
Chris@1612 226 QString extraAttributes = "") const {
Chris@1612 227 QString s;
Chris@1612 228 QTextStream out(&s);
Chris@1612 229 toXml(out, indent, extraAttributes);
Chris@1612 230 out.flush();
Chris@1612 231 return s;
Chris@1612 232 }
Chris@1615 233
Chris@1615 234 NoteData toNoteData(sv_samplerate_t sampleRate, bool valueIsMidiPitch) {
Chris@1615 235
Chris@1615 236 sv_frame_t duration;
Chris@1615 237 if (m_duration > 0) {
Chris@1615 238 duration = m_duration;
Chris@1615 239 } else {
Chris@1615 240 duration = sv_frame_t(sampleRate / 6); // arbitrary short duration
Chris@1615 241 }
Chris@1615 242
Chris@1615 243 int midiPitch;
Chris@1615 244 float frequency = 0.f;
Chris@1615 245 if (m_haveValue) {
Chris@1615 246 if (valueIsMidiPitch) {
Chris@1615 247 midiPitch = int(roundf(m_value));
Chris@1615 248 } else {
Chris@1615 249 frequency = m_value;
Chris@1615 250 midiPitch = Pitch::getPitchForFrequency(frequency);
Chris@1615 251 }
Chris@1615 252 } else {
Chris@1615 253 midiPitch = 64;
Chris@1615 254 valueIsMidiPitch = true;
Chris@1615 255 }
Chris@1615 256
Chris@1615 257 int velocity = 100;
Chris@1615 258 if (m_haveLevel) {
Chris@1615 259 if (m_level > 0.f && m_level <= 1.f) {
Chris@1615 260 velocity = int(roundf(m_level * 127.f));
Chris@1615 261 }
Chris@1615 262 }
Chris@1615 263
Chris@1615 264 NoteData n(m_frame, duration, midiPitch, velocity);
Chris@1615 265 n.isMidiPitchQuantized = valueIsMidiPitch;
Chris@1615 266 if (!valueIsMidiPitch) {
Chris@1615 267 n.frequency = frequency;
Chris@1615 268 }
Chris@1615 269
Chris@1615 270 return n;
Chris@1615 271 }
Chris@1623 272
Chris@1623 273 uint hash(uint seed = 0) const {
Chris@1623 274 uint h = qHash(m_label, seed);
Chris@1623 275 if (m_haveValue) h ^= qHash(m_value);
Chris@1623 276 if (m_haveLevel) h ^= qHash(m_level);
Chris@1623 277 h ^= qHash(m_frame);
Chris@1623 278 h ^= qHash(m_duration);
Chris@1623 279 if (m_haveReferenceFrame) h ^= qHash(m_referenceFrame);
Chris@1623 280 return h;
Chris@1623 281 }
Chris@1611 282
Chris@1611 283 private:
Chris@1611 284 // The order of fields here is chosen to minimise overall size of struct.
Chris@1612 285 // We potentially store very many of these objects.
Chris@1611 286 // If you change something, check what difference it makes to packing.
Chris@1611 287 bool m_haveValue : 1;
Chris@1611 288 bool m_haveLevel : 1;
Chris@1611 289 bool m_haveReferenceFrame : 1;
Chris@1611 290 float m_value;
Chris@1611 291 float m_level;
Chris@1611 292 sv_frame_t m_frame;
Chris@1611 293 sv_frame_t m_duration;
Chris@1611 294 sv_frame_t m_referenceFrame;
Chris@1611 295 QString m_label;
Chris@1611 296 };
Chris@1611 297
Chris@1623 298 inline uint qHash(const Event &e, uint seed = 0) {
Chris@1623 299 return e.hash(seed);
Chris@1623 300 }
Chris@1623 301
Chris@1615 302 typedef std::vector<Event> EventVector;
Chris@1612 303
Chris@1611 304 #endif