annotate base/Event.h @ 1671:82d03c9661f9 single-point

Rework isReady()/getCompletion() on models. Previously the new overhauled models were implementing getCompletion() but inheriting a version of isReady() (from the Model base) that didn't call it, referring only to isOK(). So they were reporting completion as soon as they had begun. Instead hoist getCompletion() to abstract base and call it from Model::isReady().
author Chris Cannam
date Wed, 27 Mar 2019 13:15:16 +0000
parents a77a7e8c085c
children 69ab62d378bf
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@1647 30 * An immutable(-ish) type used for point and event representation in
Chris@1647 31 * sparse models, as well as for interchange within the clipboard. An
Chris@1647 32 * event always has a frame and (possibly empty) label, and optionally
Chris@1647 33 * has numerical value, level, duration in frames, and a mapped
Chris@1647 34 * reference frame. Event has an operator< defining a total ordering,
Chris@1647 35 * by frame 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@1647 86
Chris@1647 87 // We would ideally like Event to be immutable - but we have to
Chris@1647 88 // have these because otherwise we can't put Events in vectors
Chris@1647 89 // etc. Let's call it conceptually immutable
Chris@1615 90 Event &operator=(const Event &event) =default;
Chris@1615 91 Event &operator=(Event &&event) =default;
Chris@1611 92
Chris@1611 93 sv_frame_t getFrame() const { return m_frame; }
Chris@1611 94
Chris@1615 95 Event withFrame(sv_frame_t frame) const {
Chris@1615 96 Event p(*this);
Chris@1611 97 p.m_frame = frame;
Chris@1611 98 return p;
Chris@1611 99 }
Chris@1611 100
Chris@1615 101 bool hasValue() const { return m_haveValue; }
Chris@1634 102 float getValue() const { return m_haveValue ? m_value : 0.f; }
Chris@1611 103
Chris@1615 104 Event withValue(float value) const {
Chris@1615 105 Event p(*this);
Chris@1611 106 p.m_haveValue = true;
Chris@1611 107 p.m_value = value;
Chris@1611 108 return p;
Chris@1611 109 }
Chris@1615 110 Event withoutValue() const {
Chris@1615 111 Event p(*this);
Chris@1615 112 p.m_haveValue = false;
Chris@1615 113 p.m_value = 0.f;
Chris@1615 114 return p;
Chris@1615 115 }
Chris@1611 116
Chris@1629 117 bool hasDuration() const { return m_haveDuration; }
Chris@1634 118 sv_frame_t getDuration() const { return m_haveDuration ? m_duration : 0; }
Chris@1611 119
Chris@1615 120 Event withDuration(sv_frame_t duration) const {
Chris@1615 121 Event p(*this);
Chris@1611 122 p.m_duration = duration;
Chris@1629 123 p.m_haveDuration = true;
Chris@1615 124 if (duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 125 return p;
Chris@1615 126 }
Chris@1615 127 Event withoutDuration() const {
Chris@1615 128 Event p(*this);
Chris@1629 129 p.m_haveDuration = false;
Chris@1615 130 p.m_duration = 0;
Chris@1611 131 return p;
Chris@1611 132 }
Chris@1620 133
Chris@1620 134 bool hasLabel() const { return m_label != QString(); }
Chris@1611 135 QString getLabel() const { return m_label; }
Chris@1611 136
Chris@1615 137 Event withLabel(QString label) const {
Chris@1615 138 Event p(*this);
Chris@1611 139 p.m_label = label;
Chris@1611 140 return p;
Chris@1611 141 }
Chris@1663 142
Chris@1663 143 bool hasUri() const { return m_uri != QString(); }
Chris@1663 144 QString getURI() const { return m_uri; }
Chris@1663 145
Chris@1663 146 Event withURI(QString uri) const {
Chris@1663 147 Event p(*this);
Chris@1663 148 p.m_uri = uri;
Chris@1663 149 return p;
Chris@1663 150 }
Chris@1611 151
Chris@1615 152 bool hasLevel() const { return m_haveLevel; }
Chris@1634 153 float getLevel() const { return m_haveLevel ? m_level : 0.f; }
Chris@1615 154
Chris@1615 155 Event withLevel(float level) const {
Chris@1615 156 Event p(*this);
Chris@1611 157 p.m_haveLevel = true;
Chris@1611 158 p.m_level = level;
Chris@1611 159 return p;
Chris@1611 160 }
Chris@1615 161 Event withoutLevel() const {
Chris@1615 162 Event p(*this);
Chris@1615 163 p.m_haveLevel = false;
Chris@1615 164 p.m_level = 0.f;
Chris@1615 165 return p;
Chris@1615 166 }
Chris@1611 167
Chris@1615 168 bool hasReferenceFrame() const { return m_haveReferenceFrame; }
Chris@1634 169 sv_frame_t getReferenceFrame() const {
Chris@1634 170 return m_haveReferenceFrame ? m_referenceFrame : m_frame;
Chris@1634 171 }
Chris@1611 172
Chris@1615 173 bool referenceFrameDiffers() const { // from event frame
Chris@1611 174 return m_haveReferenceFrame && (m_referenceFrame != m_frame);
Chris@1611 175 }
Chris@1611 176
Chris@1615 177 Event withReferenceFrame(sv_frame_t frame) const {
Chris@1615 178 Event p(*this);
Chris@1611 179 p.m_haveReferenceFrame = true;
Chris@1611 180 p.m_referenceFrame = frame;
Chris@1611 181 return p;
Chris@1611 182 }
Chris@1615 183 Event withoutReferenceFrame() const {
Chris@1615 184 Event p(*this);
Chris@1615 185 p.m_haveReferenceFrame = false;
Chris@1615 186 p.m_referenceFrame = 0;
Chris@1615 187 return p;
Chris@1615 188 }
Chris@1612 189
Chris@1615 190 bool operator==(const Event &p) const {
Chris@1612 191
Chris@1612 192 if (m_frame != p.m_frame) return false;
Chris@1629 193
Chris@1629 194 if (m_haveDuration != p.m_haveDuration) return false;
Chris@1629 195 if (m_haveDuration && (m_duration != p.m_duration)) return false;
Chris@1612 196
Chris@1612 197 if (m_haveValue != p.m_haveValue) return false;
Chris@1612 198 if (m_haveValue && (m_value != p.m_value)) return false;
Chris@1612 199
Chris@1612 200 if (m_haveLevel != p.m_haveLevel) return false;
Chris@1612 201 if (m_haveLevel && (m_level != p.m_level)) return false;
Chris@1612 202
Chris@1612 203 if (m_haveReferenceFrame != p.m_haveReferenceFrame) return false;
Chris@1612 204 if (m_haveReferenceFrame &&
Chris@1612 205 (m_referenceFrame != p.m_referenceFrame)) return false;
Chris@1612 206
Chris@1612 207 if (m_label != p.m_label) return false;
Chris@1663 208 if (m_uri != p.m_uri) return false;
Chris@1612 209
Chris@1612 210 return true;
Chris@1612 211 }
Chris@1612 212
Chris@1630 213 bool operator!=(const Event &p) const {
Chris@1630 214 return !operator==(p);
Chris@1630 215 }
Chris@1630 216
Chris@1615 217 bool operator<(const Event &p) const {
Chris@1612 218
Chris@1629 219 if (m_frame != p.m_frame) {
Chris@1629 220 return m_frame < p.m_frame;
Chris@1629 221 }
Chris@1612 222
Chris@1615 223 // events without a property sort before events with that property
Chris@1612 224
Chris@1629 225 if (m_haveDuration != p.m_haveDuration) {
Chris@1629 226 return !m_haveDuration;
Chris@1629 227 }
Chris@1629 228 if (m_haveDuration && (m_duration != p.m_duration)) {
Chris@1629 229 return m_duration < p.m_duration;
Chris@1629 230 }
Chris@1629 231
Chris@1629 232 if (m_haveValue != p.m_haveValue) {
Chris@1629 233 return !m_haveValue;
Chris@1629 234 }
Chris@1629 235 if (m_haveValue && (m_value != p.m_value)) {
Chris@1629 236 return m_value < p.m_value;
Chris@1629 237 }
Chris@1612 238
Chris@1629 239 if (m_haveLevel != p.m_haveLevel) {
Chris@1629 240 return !m_haveLevel;
Chris@1629 241 }
Chris@1629 242 if (m_haveLevel && (m_level != p.m_level)) {
Chris@1629 243 return m_level < p.m_level;
Chris@1629 244 }
Chris@1612 245
Chris@1612 246 if (m_haveReferenceFrame != p.m_haveReferenceFrame) {
Chris@1612 247 return !m_haveReferenceFrame;
Chris@1612 248 }
Chris@1612 249 if (m_haveReferenceFrame && (m_referenceFrame != p.m_referenceFrame)) {
Chris@1612 250 return m_referenceFrame < p.m_referenceFrame;
Chris@1612 251 }
Chris@1612 252
Chris@1663 253 if (m_label != p.m_label) {
Chris@1663 254 return m_label < p.m_label;
Chris@1663 255 }
Chris@1663 256 return m_uri < p.m_uri;
Chris@1612 257 }
Chris@1612 258
Chris@1612 259 void toXml(QTextStream &stream,
Chris@1612 260 QString indent = "",
Chris@1612 261 QString extraAttributes = "") const {
Chris@1612 262
Chris@1615 263 // For I/O purposes these are points, not events
Chris@1612 264 stream << indent << QString("<point frame=\"%1\" ").arg(m_frame);
Chris@1612 265 if (m_haveValue) stream << QString("value=\"%1\" ").arg(m_value);
Chris@1629 266 if (m_haveDuration) stream << QString("duration=\"%1\" ").arg(m_duration);
Chris@1612 267 if (m_haveLevel) stream << QString("level=\"%1\" ").arg(m_level);
Chris@1612 268 if (m_haveReferenceFrame) stream << QString("referenceFrame=\"%1\" ")
Chris@1612 269 .arg(m_referenceFrame);
Chris@1612 270 stream << QString("label=\"%1\" ")
Chris@1612 271 .arg(XmlExportable::encodeEntities(m_label));
Chris@1663 272 if (m_uri != QString()) {
Chris@1663 273 stream << QString("uri=\"%1\" ")
Chris@1663 274 .arg(XmlExportable::encodeEntities(m_uri));
Chris@1663 275 }
Chris@1641 276 stream << extraAttributes << "/>\n";
Chris@1612 277 }
Chris@1612 278
Chris@1612 279 QString toXmlString(QString indent = "",
Chris@1612 280 QString extraAttributes = "") const {
Chris@1612 281 QString s;
Chris@1612 282 QTextStream out(&s);
Chris@1612 283 toXml(out, indent, extraAttributes);
Chris@1612 284 out.flush();
Chris@1612 285 return s;
Chris@1612 286 }
Chris@1615 287
Chris@1629 288 NoteData toNoteData(sv_samplerate_t sampleRate,
Chris@1637 289 bool valueIsMidiPitch) const {
Chris@1615 290
Chris@1615 291 sv_frame_t duration;
Chris@1629 292 if (m_haveDuration && m_duration > 0) {
Chris@1615 293 duration = m_duration;
Chris@1615 294 } else {
Chris@1615 295 duration = sv_frame_t(sampleRate / 6); // arbitrary short duration
Chris@1615 296 }
Chris@1615 297
Chris@1615 298 int midiPitch;
Chris@1615 299 float frequency = 0.f;
Chris@1615 300 if (m_haveValue) {
Chris@1615 301 if (valueIsMidiPitch) {
Chris@1615 302 midiPitch = int(roundf(m_value));
Chris@1615 303 } else {
Chris@1615 304 frequency = m_value;
Chris@1615 305 midiPitch = Pitch::getPitchForFrequency(frequency);
Chris@1615 306 }
Chris@1615 307 } else {
Chris@1615 308 midiPitch = 64;
Chris@1615 309 valueIsMidiPitch = true;
Chris@1615 310 }
Chris@1615 311
Chris@1615 312 int velocity = 100;
Chris@1615 313 if (m_haveLevel) {
Chris@1615 314 if (m_level > 0.f && m_level <= 1.f) {
Chris@1615 315 velocity = int(roundf(m_level * 127.f));
Chris@1615 316 }
Chris@1615 317 }
Chris@1615 318
Chris@1615 319 NoteData n(m_frame, duration, midiPitch, velocity);
Chris@1615 320 n.isMidiPitchQuantized = valueIsMidiPitch;
Chris@1615 321 if (!valueIsMidiPitch) {
Chris@1615 322 n.frequency = frequency;
Chris@1615 323 }
Chris@1615 324
Chris@1615 325 return n;
Chris@1615 326 }
Chris@1623 327
Chris@1629 328 QString toDelimitedDataString(QString delimiter,
Chris@1629 329 DataExportOptions opts,
Chris@1629 330 sv_samplerate_t sampleRate) const {
Chris@1629 331 QStringList list;
Chris@1629 332
Chris@1629 333 list << RealTime::frame2RealTime(m_frame, sampleRate)
Chris@1629 334 .toString().c_str();
Chris@1629 335
Chris@1629 336 if (m_haveValue) {
Chris@1629 337 list << QString("%1").arg(m_value);
Chris@1629 338 }
Chris@1629 339
Chris@1629 340 if (m_haveDuration) {
Chris@1629 341 list << RealTime::frame2RealTime(m_duration, sampleRate)
Chris@1629 342 .toString().c_str();
Chris@1629 343 }
Chris@1629 344
Chris@1629 345 if (m_haveLevel) {
Chris@1629 346 if (!(opts & DataExportOmitLevels)) {
Chris@1629 347 list << QString("%1").arg(m_level);
Chris@1629 348 }
Chris@1629 349 }
Chris@1629 350
Chris@1629 351 if (m_label != "") list << m_label;
Chris@1663 352 if (m_uri != "") list << m_uri;
Chris@1629 353
Chris@1629 354 return list.join(delimiter);
Chris@1629 355 }
Chris@1629 356
Chris@1623 357 uint hash(uint seed = 0) const {
Chris@1623 358 uint h = qHash(m_label, seed);
Chris@1623 359 if (m_haveValue) h ^= qHash(m_value);
Chris@1623 360 if (m_haveLevel) h ^= qHash(m_level);
Chris@1623 361 h ^= qHash(m_frame);
Chris@1629 362 if (m_haveDuration) h ^= qHash(m_duration);
Chris@1623 363 if (m_haveReferenceFrame) h ^= qHash(m_referenceFrame);
Chris@1663 364 h ^= qHash(m_uri);
Chris@1623 365 return h;
Chris@1623 366 }
Chris@1611 367
Chris@1611 368 private:
Chris@1611 369 // The order of fields here is chosen to minimise overall size of struct.
Chris@1612 370 // We potentially store very many of these objects.
Chris@1611 371 // If you change something, check what difference it makes to packing.
Chris@1611 372 bool m_haveValue : 1;
Chris@1611 373 bool m_haveLevel : 1;
Chris@1629 374 bool m_haveDuration : 1;
Chris@1611 375 bool m_haveReferenceFrame : 1;
Chris@1611 376 float m_value;
Chris@1611 377 float m_level;
Chris@1611 378 sv_frame_t m_frame;
Chris@1611 379 sv_frame_t m_duration;
Chris@1611 380 sv_frame_t m_referenceFrame;
Chris@1611 381 QString m_label;
Chris@1663 382 QString m_uri;
Chris@1611 383 };
Chris@1611 384
Chris@1623 385 inline uint qHash(const Event &e, uint seed = 0) {
Chris@1623 386 return e.hash(seed);
Chris@1623 387 }
Chris@1623 388
Chris@1615 389 typedef std::vector<Event> EventVector;
Chris@1612 390
Chris@1611 391 #endif