annotate base/Event.h @ 1789:baafe1bb7e51 time-frequency-boxes

Fixes to export of time-frequency box model
author Chris Cannam
date Fri, 20 Sep 2019 14:18:38 +0100
parents 9ab3be243397
children 8c34ecba70df
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@1674 259 struct ExportNameOptions {
Chris@1674 260
Chris@1674 261 ExportNameOptions() :
Chris@1789 262 valueAttributeName("value"),
Chris@1789 263 levelAttributeName("level"),
Chris@1674 264 uriAttributeName("uri") { }
Chris@1674 265
Chris@1789 266 QString valueAttributeName;
Chris@1789 267 QString levelAttributeName;
Chris@1674 268 QString uriAttributeName;
Chris@1674 269 };
Chris@1674 270
Chris@1612 271 void toXml(QTextStream &stream,
Chris@1612 272 QString indent = "",
Chris@1674 273 QString extraAttributes = "",
Chris@1674 274 ExportNameOptions opts = ExportNameOptions()) const {
Chris@1612 275
Chris@1615 276 // For I/O purposes these are points, not events
Chris@1612 277 stream << indent << QString("<point frame=\"%1\" ").arg(m_frame);
Chris@1674 278 if (m_haveValue) {
Chris@1674 279 stream << QString("%1=\"%2\" ")
Chris@1789 280 .arg(opts.valueAttributeName).arg(m_value);
Chris@1674 281 }
Chris@1674 282 if (m_haveDuration) {
Chris@1674 283 stream << QString("duration=\"%1\" ").arg(m_duration);
Chris@1674 284 }
Chris@1674 285 if (m_haveLevel) {
Chris@1789 286 stream << QString("%1=\"%2\" ")
Chris@1789 287 .arg(opts.levelAttributeName)
Chris@1789 288 .arg(m_level);
Chris@1674 289 }
Chris@1674 290 if (m_haveReferenceFrame) {
Chris@1674 291 stream << QString("referenceFrame=\"%1\" ")
Chris@1674 292 .arg(m_referenceFrame);
Chris@1674 293 }
Chris@1682 294
Chris@1682 295 stream << QString("label=\"%1\" ")
Chris@1682 296 .arg(XmlExportable::encodeEntities(m_label));
Chris@1682 297
Chris@1663 298 if (m_uri != QString()) {
Chris@1674 299 stream << QString("%1=\"%2\" ")
Chris@1674 300 .arg(opts.uriAttributeName)
Chris@1663 301 .arg(XmlExportable::encodeEntities(m_uri));
Chris@1663 302 }
Chris@1641 303 stream << extraAttributes << "/>\n";
Chris@1612 304 }
Chris@1612 305
Chris@1612 306 QString toXmlString(QString indent = "",
Chris@1612 307 QString extraAttributes = "") const {
Chris@1612 308 QString s;
Chris@1612 309 QTextStream out(&s);
Chris@1612 310 toXml(out, indent, extraAttributes);
Chris@1612 311 out.flush();
Chris@1612 312 return s;
Chris@1612 313 }
Chris@1615 314
Chris@1629 315 NoteData toNoteData(sv_samplerate_t sampleRate,
Chris@1637 316 bool valueIsMidiPitch) const {
Chris@1615 317
Chris@1615 318 sv_frame_t duration;
Chris@1629 319 if (m_haveDuration && m_duration > 0) {
Chris@1615 320 duration = m_duration;
Chris@1615 321 } else {
Chris@1615 322 duration = sv_frame_t(sampleRate / 6); // arbitrary short duration
Chris@1615 323 }
Chris@1615 324
Chris@1615 325 int midiPitch;
Chris@1615 326 float frequency = 0.f;
Chris@1615 327 if (m_haveValue) {
Chris@1615 328 if (valueIsMidiPitch) {
Chris@1615 329 midiPitch = int(roundf(m_value));
Chris@1615 330 } else {
Chris@1615 331 frequency = m_value;
Chris@1615 332 midiPitch = Pitch::getPitchForFrequency(frequency);
Chris@1615 333 }
Chris@1615 334 } else {
Chris@1615 335 midiPitch = 64;
Chris@1615 336 valueIsMidiPitch = true;
Chris@1615 337 }
Chris@1615 338
Chris@1615 339 int velocity = 100;
Chris@1615 340 if (m_haveLevel) {
Chris@1615 341 if (m_level > 0.f && m_level <= 1.f) {
Chris@1615 342 velocity = int(roundf(m_level * 127.f));
Chris@1615 343 }
Chris@1615 344 }
Chris@1615 345
Chris@1615 346 NoteData n(m_frame, duration, midiPitch, velocity);
Chris@1615 347 n.isMidiPitchQuantized = valueIsMidiPitch;
Chris@1615 348 if (!valueIsMidiPitch) {
Chris@1615 349 n.frequency = frequency;
Chris@1615 350 }
Chris@1615 351
Chris@1615 352 return n;
Chris@1615 353 }
Chris@1623 354
Chris@1629 355 QString toDelimitedDataString(QString delimiter,
Chris@1629 356 DataExportOptions opts,
Chris@1629 357 sv_samplerate_t sampleRate) const {
Chris@1629 358 QStringList list;
Chris@1629 359
Chris@1629 360 list << RealTime::frame2RealTime(m_frame, sampleRate)
Chris@1629 361 .toString().c_str();
Chris@1629 362
Chris@1629 363 if (m_haveValue) {
Chris@1629 364 list << QString("%1").arg(m_value);
Chris@1629 365 }
Chris@1629 366
Chris@1629 367 if (m_haveDuration) {
Chris@1629 368 list << RealTime::frame2RealTime(m_duration, sampleRate)
Chris@1629 369 .toString().c_str();
Chris@1629 370 }
Chris@1629 371
Chris@1629 372 if (m_haveLevel) {
Chris@1629 373 if (!(opts & DataExportOmitLevels)) {
Chris@1629 374 list << QString("%1").arg(m_level);
Chris@1629 375 }
Chris@1629 376 }
Chris@1787 377
Chris@1787 378 // Put URI before label, to preserve the ordering previously
Chris@1787 379 // used in the custom Image model exporter. We shouldn't
Chris@1787 380 // change the column ordering unless (until?) we provide a
Chris@1787 381 // facility for the user to customise it
Chris@1787 382 if (m_uri != "") list << m_uri;
Chris@1629 383 if (m_label != "") list << m_label;
Chris@1629 384
Chris@1629 385 return list.join(delimiter);
Chris@1629 386 }
Chris@1629 387
Chris@1623 388 uint hash(uint seed = 0) const {
Chris@1623 389 uint h = qHash(m_label, seed);
Chris@1623 390 if (m_haveValue) h ^= qHash(m_value);
Chris@1623 391 if (m_haveLevel) h ^= qHash(m_level);
Chris@1623 392 h ^= qHash(m_frame);
Chris@1629 393 if (m_haveDuration) h ^= qHash(m_duration);
Chris@1623 394 if (m_haveReferenceFrame) h ^= qHash(m_referenceFrame);
Chris@1663 395 h ^= qHash(m_uri);
Chris@1623 396 return h;
Chris@1623 397 }
Chris@1611 398
Chris@1611 399 private:
Chris@1611 400 // The order of fields here is chosen to minimise overall size of struct.
Chris@1612 401 // We potentially store very many of these objects.
Chris@1611 402 // If you change something, check what difference it makes to packing.
Chris@1611 403 bool m_haveValue : 1;
Chris@1611 404 bool m_haveLevel : 1;
Chris@1629 405 bool m_haveDuration : 1;
Chris@1611 406 bool m_haveReferenceFrame : 1;
Chris@1611 407 float m_value;
Chris@1611 408 float m_level;
Chris@1611 409 sv_frame_t m_frame;
Chris@1611 410 sv_frame_t m_duration;
Chris@1611 411 sv_frame_t m_referenceFrame;
Chris@1611 412 QString m_label;
Chris@1663 413 QString m_uri;
Chris@1611 414 };
Chris@1611 415
Chris@1623 416 inline uint qHash(const Event &e, uint seed = 0) {
Chris@1623 417 return e.hash(seed);
Chris@1623 418 }
Chris@1623 419
Chris@1615 420 typedef std::vector<Event> EventVector;
Chris@1612 421
Chris@1611 422 #endif