annotate base/Event.h @ 1801:af3f6c411a8e

*sigh*
author Chris Cannam
date Thu, 10 Oct 2019 09:34:12 +0100
parents 705ea8699d49
children c48548a85622
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@1800 29 #if (QT_VERSION < QT_VERSION_CHECK(5, 3, 0))
Chris@1800 30 uint qHash(float key, uint seed) {
Chris@1801 31 uint h = seed;
Chris@1801 32 const uchar *p = reinterpret_cast<const uchar *>(&key);
Chris@1801 33 for (size_t i = 0; i < sizeof(key); ++i) {
Chris@1801 34 h = 31 * h + p[i];
Chris@1801 35 }
Chris@1801 36 return h;
Chris@1800 37 }
Chris@1800 38 #endif
Chris@1800 39
Chris@1615 40 /**
Chris@1647 41 * An immutable(-ish) type used for point and event representation in
Chris@1647 42 * sparse models, as well as for interchange within the clipboard. An
Chris@1647 43 * event always has a frame and (possibly empty) label, and optionally
Chris@1647 44 * has numerical value, level, duration in frames, and a mapped
Chris@1647 45 * reference frame. Event has an operator< defining a total ordering,
Chris@1647 46 * by frame first and then by the other properties.
Chris@1615 47 *
Chris@1615 48 * Event is based on the Clipboard::Point type up to SV v3.2.1 and is
Chris@1615 49 * intended also to replace the custom point types previously found in
Chris@1615 50 * sparse models.
Chris@1615 51 */
Chris@1615 52 class Event
Chris@1611 53 {
Chris@1611 54 public:
Chris@1623 55 Event() :
Chris@1629 56 m_haveValue(false), m_haveLevel(false),
Chris@1629 57 m_haveDuration(false), m_haveReferenceFrame(false),
Chris@1623 58 m_value(0.f), m_level(0.f), m_frame(0),
Chris@1623 59 m_duration(0), m_referenceFrame(0), m_label() { }
Chris@1623 60
Chris@1615 61 Event(sv_frame_t frame) :
Chris@1629 62 m_haveValue(false), m_haveLevel(false),
Chris@1629 63 m_haveDuration(false), m_haveReferenceFrame(false),
Chris@1615 64 m_value(0.f), m_level(0.f), m_frame(frame),
Chris@1615 65 m_duration(0), m_referenceFrame(0), m_label() { }
Chris@1615 66
Chris@1615 67 Event(sv_frame_t frame, QString label) :
Chris@1629 68 m_haveValue(false), m_haveLevel(false),
Chris@1629 69 m_haveDuration(false), m_haveReferenceFrame(false),
Chris@1611 70 m_value(0.f), m_level(0.f), m_frame(frame),
Chris@1611 71 m_duration(0), m_referenceFrame(0), m_label(label) { }
Chris@1611 72
Chris@1615 73 Event(sv_frame_t frame, float value, QString label) :
Chris@1629 74 m_haveValue(true), m_haveLevel(false),
Chris@1629 75 m_haveDuration(false), m_haveReferenceFrame(false),
Chris@1611 76 m_value(value), m_level(0.f), m_frame(frame),
Chris@1611 77 m_duration(0), m_referenceFrame(0), m_label(label) { }
Chris@1611 78
Chris@1615 79 Event(sv_frame_t frame, float value, sv_frame_t duration, QString label) :
Chris@1629 80 m_haveValue(true), m_haveLevel(false),
Chris@1629 81 m_haveDuration(true), m_haveReferenceFrame(false),
Chris@1611 82 m_value(value), m_level(0.f), m_frame(frame),
Chris@1615 83 m_duration(duration), m_referenceFrame(0), m_label(label) {
Chris@1615 84 if (m_duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 85 }
Chris@1611 86
Chris@1615 87 Event(sv_frame_t frame, float value, sv_frame_t duration,
Chris@1612 88 float level, QString label) :
Chris@1629 89 m_haveValue(true), m_haveLevel(true),
Chris@1629 90 m_haveDuration(true), m_haveReferenceFrame(false),
Chris@1611 91 m_value(value), m_level(level), m_frame(frame),
Chris@1615 92 m_duration(duration), m_referenceFrame(0), m_label(label) {
Chris@1615 93 if (m_duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 94 }
Chris@1611 95
Chris@1615 96 Event(const Event &event) =default;
Chris@1647 97
Chris@1647 98 // We would ideally like Event to be immutable - but we have to
Chris@1647 99 // have these because otherwise we can't put Events in vectors
Chris@1647 100 // etc. Let's call it conceptually immutable
Chris@1615 101 Event &operator=(const Event &event) =default;
Chris@1615 102 Event &operator=(Event &&event) =default;
Chris@1611 103
Chris@1611 104 sv_frame_t getFrame() const { return m_frame; }
Chris@1611 105
Chris@1615 106 Event withFrame(sv_frame_t frame) const {
Chris@1615 107 Event p(*this);
Chris@1611 108 p.m_frame = frame;
Chris@1611 109 return p;
Chris@1611 110 }
Chris@1611 111
Chris@1615 112 bool hasValue() const { return m_haveValue; }
Chris@1634 113 float getValue() const { return m_haveValue ? m_value : 0.f; }
Chris@1611 114
Chris@1615 115 Event withValue(float value) const {
Chris@1615 116 Event p(*this);
Chris@1611 117 p.m_haveValue = true;
Chris@1611 118 p.m_value = value;
Chris@1611 119 return p;
Chris@1611 120 }
Chris@1615 121 Event withoutValue() const {
Chris@1615 122 Event p(*this);
Chris@1615 123 p.m_haveValue = false;
Chris@1615 124 p.m_value = 0.f;
Chris@1615 125 return p;
Chris@1615 126 }
Chris@1611 127
Chris@1629 128 bool hasDuration() const { return m_haveDuration; }
Chris@1634 129 sv_frame_t getDuration() const { return m_haveDuration ? m_duration : 0; }
Chris@1611 130
Chris@1615 131 Event withDuration(sv_frame_t duration) const {
Chris@1615 132 Event p(*this);
Chris@1611 133 p.m_duration = duration;
Chris@1629 134 p.m_haveDuration = true;
Chris@1615 135 if (duration < 0) throw std::logic_error("duration must be >= 0");
Chris@1615 136 return p;
Chris@1615 137 }
Chris@1615 138 Event withoutDuration() const {
Chris@1615 139 Event p(*this);
Chris@1629 140 p.m_haveDuration = false;
Chris@1615 141 p.m_duration = 0;
Chris@1611 142 return p;
Chris@1611 143 }
Chris@1620 144
Chris@1620 145 bool hasLabel() const { return m_label != QString(); }
Chris@1611 146 QString getLabel() const { return m_label; }
Chris@1611 147
Chris@1615 148 Event withLabel(QString label) const {
Chris@1615 149 Event p(*this);
Chris@1611 150 p.m_label = label;
Chris@1611 151 return p;
Chris@1611 152 }
Chris@1663 153
Chris@1663 154 bool hasUri() const { return m_uri != QString(); }
Chris@1663 155 QString getURI() const { return m_uri; }
Chris@1663 156
Chris@1663 157 Event withURI(QString uri) const {
Chris@1663 158 Event p(*this);
Chris@1663 159 p.m_uri = uri;
Chris@1663 160 return p;
Chris@1663 161 }
Chris@1611 162
Chris@1615 163 bool hasLevel() const { return m_haveLevel; }
Chris@1634 164 float getLevel() const { return m_haveLevel ? m_level : 0.f; }
Chris@1615 165
Chris@1615 166 Event withLevel(float level) const {
Chris@1615 167 Event p(*this);
Chris@1611 168 p.m_haveLevel = true;
Chris@1611 169 p.m_level = level;
Chris@1611 170 return p;
Chris@1611 171 }
Chris@1615 172 Event withoutLevel() const {
Chris@1615 173 Event p(*this);
Chris@1615 174 p.m_haveLevel = false;
Chris@1615 175 p.m_level = 0.f;
Chris@1615 176 return p;
Chris@1615 177 }
Chris@1611 178
Chris@1615 179 bool hasReferenceFrame() const { return m_haveReferenceFrame; }
Chris@1634 180 sv_frame_t getReferenceFrame() const {
Chris@1634 181 return m_haveReferenceFrame ? m_referenceFrame : m_frame;
Chris@1634 182 }
Chris@1611 183
Chris@1615 184 bool referenceFrameDiffers() const { // from event frame
Chris@1611 185 return m_haveReferenceFrame && (m_referenceFrame != m_frame);
Chris@1611 186 }
Chris@1611 187
Chris@1615 188 Event withReferenceFrame(sv_frame_t frame) const {
Chris@1615 189 Event p(*this);
Chris@1611 190 p.m_haveReferenceFrame = true;
Chris@1611 191 p.m_referenceFrame = frame;
Chris@1611 192 return p;
Chris@1611 193 }
Chris@1615 194 Event withoutReferenceFrame() const {
Chris@1615 195 Event p(*this);
Chris@1615 196 p.m_haveReferenceFrame = false;
Chris@1615 197 p.m_referenceFrame = 0;
Chris@1615 198 return p;
Chris@1615 199 }
Chris@1612 200
Chris@1615 201 bool operator==(const Event &p) const {
Chris@1612 202
Chris@1612 203 if (m_frame != p.m_frame) return false;
Chris@1629 204
Chris@1629 205 if (m_haveDuration != p.m_haveDuration) return false;
Chris@1629 206 if (m_haveDuration && (m_duration != p.m_duration)) return false;
Chris@1612 207
Chris@1612 208 if (m_haveValue != p.m_haveValue) return false;
Chris@1612 209 if (m_haveValue && (m_value != p.m_value)) return false;
Chris@1612 210
Chris@1612 211 if (m_haveLevel != p.m_haveLevel) return false;
Chris@1612 212 if (m_haveLevel && (m_level != p.m_level)) return false;
Chris@1612 213
Chris@1612 214 if (m_haveReferenceFrame != p.m_haveReferenceFrame) return false;
Chris@1612 215 if (m_haveReferenceFrame &&
Chris@1612 216 (m_referenceFrame != p.m_referenceFrame)) return false;
Chris@1612 217
Chris@1612 218 if (m_label != p.m_label) return false;
Chris@1663 219 if (m_uri != p.m_uri) return false;
Chris@1612 220
Chris@1612 221 return true;
Chris@1612 222 }
Chris@1612 223
Chris@1630 224 bool operator!=(const Event &p) const {
Chris@1630 225 return !operator==(p);
Chris@1630 226 }
Chris@1630 227
Chris@1615 228 bool operator<(const Event &p) const {
Chris@1612 229
Chris@1629 230 if (m_frame != p.m_frame) {
Chris@1629 231 return m_frame < p.m_frame;
Chris@1629 232 }
Chris@1612 233
Chris@1615 234 // events without a property sort before events with that property
Chris@1612 235
Chris@1629 236 if (m_haveDuration != p.m_haveDuration) {
Chris@1629 237 return !m_haveDuration;
Chris@1629 238 }
Chris@1629 239 if (m_haveDuration && (m_duration != p.m_duration)) {
Chris@1629 240 return m_duration < p.m_duration;
Chris@1629 241 }
Chris@1629 242
Chris@1629 243 if (m_haveValue != p.m_haveValue) {
Chris@1629 244 return !m_haveValue;
Chris@1629 245 }
Chris@1629 246 if (m_haveValue && (m_value != p.m_value)) {
Chris@1629 247 return m_value < p.m_value;
Chris@1629 248 }
Chris@1612 249
Chris@1629 250 if (m_haveLevel != p.m_haveLevel) {
Chris@1629 251 return !m_haveLevel;
Chris@1629 252 }
Chris@1629 253 if (m_haveLevel && (m_level != p.m_level)) {
Chris@1629 254 return m_level < p.m_level;
Chris@1629 255 }
Chris@1612 256
Chris@1612 257 if (m_haveReferenceFrame != p.m_haveReferenceFrame) {
Chris@1612 258 return !m_haveReferenceFrame;
Chris@1612 259 }
Chris@1612 260 if (m_haveReferenceFrame && (m_referenceFrame != p.m_referenceFrame)) {
Chris@1612 261 return m_referenceFrame < p.m_referenceFrame;
Chris@1612 262 }
Chris@1612 263
Chris@1663 264 if (m_label != p.m_label) {
Chris@1663 265 return m_label < p.m_label;
Chris@1663 266 }
Chris@1663 267 return m_uri < p.m_uri;
Chris@1612 268 }
Chris@1612 269
Chris@1674 270 struct ExportNameOptions {
Chris@1674 271
Chris@1674 272 ExportNameOptions() :
Chris@1789 273 valueAttributeName("value"),
Chris@1789 274 levelAttributeName("level"),
Chris@1674 275 uriAttributeName("uri") { }
Chris@1674 276
Chris@1789 277 QString valueAttributeName;
Chris@1789 278 QString levelAttributeName;
Chris@1674 279 QString uriAttributeName;
Chris@1674 280 };
Chris@1674 281
Chris@1612 282 void toXml(QTextStream &stream,
Chris@1612 283 QString indent = "",
Chris@1674 284 QString extraAttributes = "",
Chris@1674 285 ExportNameOptions opts = ExportNameOptions()) const {
Chris@1612 286
Chris@1615 287 // For I/O purposes these are points, not events
Chris@1612 288 stream << indent << QString("<point frame=\"%1\" ").arg(m_frame);
Chris@1674 289 if (m_haveValue) {
Chris@1674 290 stream << QString("%1=\"%2\" ")
Chris@1789 291 .arg(opts.valueAttributeName).arg(m_value);
Chris@1674 292 }
Chris@1674 293 if (m_haveDuration) {
Chris@1674 294 stream << QString("duration=\"%1\" ").arg(m_duration);
Chris@1674 295 }
Chris@1674 296 if (m_haveLevel) {
Chris@1789 297 stream << QString("%1=\"%2\" ")
Chris@1789 298 .arg(opts.levelAttributeName)
Chris@1789 299 .arg(m_level);
Chris@1674 300 }
Chris@1674 301 if (m_haveReferenceFrame) {
Chris@1674 302 stream << QString("referenceFrame=\"%1\" ")
Chris@1674 303 .arg(m_referenceFrame);
Chris@1674 304 }
Chris@1682 305
Chris@1682 306 stream << QString("label=\"%1\" ")
Chris@1682 307 .arg(XmlExportable::encodeEntities(m_label));
Chris@1682 308
Chris@1663 309 if (m_uri != QString()) {
Chris@1674 310 stream << QString("%1=\"%2\" ")
Chris@1674 311 .arg(opts.uriAttributeName)
Chris@1663 312 .arg(XmlExportable::encodeEntities(m_uri));
Chris@1663 313 }
Chris@1641 314 stream << extraAttributes << "/>\n";
Chris@1612 315 }
Chris@1612 316
Chris@1612 317 QString toXmlString(QString indent = "",
Chris@1612 318 QString extraAttributes = "") const {
Chris@1612 319 QString s;
Chris@1612 320 QTextStream out(&s);
Chris@1612 321 toXml(out, indent, extraAttributes);
Chris@1612 322 out.flush();
Chris@1612 323 return s;
Chris@1612 324 }
Chris@1615 325
Chris@1629 326 NoteData toNoteData(sv_samplerate_t sampleRate,
Chris@1637 327 bool valueIsMidiPitch) const {
Chris@1615 328
Chris@1615 329 sv_frame_t duration;
Chris@1629 330 if (m_haveDuration && m_duration > 0) {
Chris@1615 331 duration = m_duration;
Chris@1615 332 } else {
Chris@1615 333 duration = sv_frame_t(sampleRate / 6); // arbitrary short duration
Chris@1615 334 }
Chris@1615 335
Chris@1615 336 int midiPitch;
Chris@1615 337 float frequency = 0.f;
Chris@1615 338 if (m_haveValue) {
Chris@1615 339 if (valueIsMidiPitch) {
Chris@1615 340 midiPitch = int(roundf(m_value));
Chris@1615 341 } else {
Chris@1615 342 frequency = m_value;
Chris@1615 343 midiPitch = Pitch::getPitchForFrequency(frequency);
Chris@1615 344 }
Chris@1615 345 } else {
Chris@1615 346 midiPitch = 64;
Chris@1615 347 valueIsMidiPitch = true;
Chris@1615 348 }
Chris@1615 349
Chris@1615 350 int velocity = 100;
Chris@1615 351 if (m_haveLevel) {
Chris@1615 352 if (m_level > 0.f && m_level <= 1.f) {
Chris@1615 353 velocity = int(roundf(m_level * 127.f));
Chris@1615 354 }
Chris@1615 355 }
Chris@1615 356
Chris@1615 357 NoteData n(m_frame, duration, midiPitch, velocity);
Chris@1615 358 n.isMidiPitchQuantized = valueIsMidiPitch;
Chris@1615 359 if (!valueIsMidiPitch) {
Chris@1615 360 n.frequency = frequency;
Chris@1615 361 }
Chris@1615 362
Chris@1615 363 return n;
Chris@1615 364 }
Chris@1623 365
Chris@1629 366 QString toDelimitedDataString(QString delimiter,
Chris@1629 367 DataExportOptions opts,
Chris@1629 368 sv_samplerate_t sampleRate) const {
Chris@1629 369 QStringList list;
Chris@1629 370
Chris@1629 371 list << RealTime::frame2RealTime(m_frame, sampleRate)
Chris@1629 372 .toString().c_str();
Chris@1629 373
Chris@1629 374 if (m_haveValue) {
Chris@1629 375 list << QString("%1").arg(m_value);
Chris@1629 376 }
Chris@1629 377
Chris@1629 378 if (m_haveDuration) {
Chris@1629 379 list << RealTime::frame2RealTime(m_duration, sampleRate)
Chris@1629 380 .toString().c_str();
Chris@1629 381 }
Chris@1629 382
Chris@1629 383 if (m_haveLevel) {
Chris@1629 384 if (!(opts & DataExportOmitLevels)) {
Chris@1629 385 list << QString("%1").arg(m_level);
Chris@1629 386 }
Chris@1629 387 }
Chris@1787 388
Chris@1787 389 // Put URI before label, to preserve the ordering previously
Chris@1787 390 // used in the custom Image model exporter. We shouldn't
Chris@1787 391 // change the column ordering unless (until?) we provide a
Chris@1787 392 // facility for the user to customise it
Chris@1787 393 if (m_uri != "") list << m_uri;
Chris@1629 394 if (m_label != "") list << m_label;
Chris@1629 395
Chris@1629 396 return list.join(delimiter);
Chris@1629 397 }
Chris@1799 398
Chris@1623 399 uint hash(uint seed = 0) const {
Chris@1623 400 uint h = qHash(m_label, seed);
Chris@1623 401 if (m_haveValue) h ^= qHash(m_value);
Chris@1623 402 if (m_haveLevel) h ^= qHash(m_level);
Chris@1623 403 h ^= qHash(m_frame);
Chris@1629 404 if (m_haveDuration) h ^= qHash(m_duration);
Chris@1623 405 if (m_haveReferenceFrame) h ^= qHash(m_referenceFrame);
Chris@1663 406 h ^= qHash(m_uri);
Chris@1623 407 return h;
Chris@1623 408 }
Chris@1611 409
Chris@1611 410 private:
Chris@1611 411 // The order of fields here is chosen to minimise overall size of struct.
Chris@1612 412 // We potentially store very many of these objects.
Chris@1611 413 // If you change something, check what difference it makes to packing.
Chris@1611 414 bool m_haveValue : 1;
Chris@1611 415 bool m_haveLevel : 1;
Chris@1629 416 bool m_haveDuration : 1;
Chris@1611 417 bool m_haveReferenceFrame : 1;
Chris@1611 418 float m_value;
Chris@1611 419 float m_level;
Chris@1611 420 sv_frame_t m_frame;
Chris@1611 421 sv_frame_t m_duration;
Chris@1611 422 sv_frame_t m_referenceFrame;
Chris@1611 423 QString m_label;
Chris@1663 424 QString m_uri;
Chris@1611 425 };
Chris@1611 426
Chris@1623 427 inline uint qHash(const Event &e, uint seed = 0) {
Chris@1623 428 return e.hash(seed);
Chris@1623 429 }
Chris@1623 430
Chris@1615 431 typedef std::vector<Event> EventVector;
Chris@1612 432
Chris@1611 433 #endif