Chris@1611: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@1611: Chris@1611: /* Chris@1611: Sonic Visualiser Chris@1611: An audio file viewer and annotation editor. Chris@1611: Centre for Digital Music, Queen Mary, University of London. Chris@1611: This file copyright 2006 Chris Cannam. Chris@1611: Chris@1611: This program is free software; you can redistribute it and/or Chris@1611: modify it under the terms of the GNU General Public License as Chris@1611: published by the Free Software Foundation; either version 2 of the Chris@1611: License, or (at your option) any later version. See the file Chris@1611: COPYING included with this distribution for more information. Chris@1611: */ Chris@1611: Chris@1615: #ifndef SV_EVENT_H Chris@1615: #define SV_EVENT_H Chris@1615: Chris@1615: #include "BaseTypes.h" Chris@1615: #include "NoteData.h" Chris@1615: #include "XmlExportable.h" Chris@1629: #include "DataExportOptions.h" Chris@1615: Chris@1615: #include Chris@1615: #include Chris@1611: Chris@1611: #include Chris@1611: Chris@1800: #if (QT_VERSION < QT_VERSION_CHECK(5, 3, 0)) Chris@1800: uint qHash(float key, uint seed) { Chris@1801: uint h = seed; Chris@1801: const uchar *p = reinterpret_cast(&key); Chris@1801: for (size_t i = 0; i < sizeof(key); ++i) { Chris@1801: h = 31 * h + p[i]; Chris@1801: } Chris@1801: return h; Chris@1800: } Chris@1800: #endif Chris@1800: Chris@1615: /** Chris@1647: * An immutable(-ish) type used for point and event representation in Chris@1647: * sparse models, as well as for interchange within the clipboard. An Chris@1647: * event always has a frame and (possibly empty) label, and optionally Chris@1647: * has numerical value, level, duration in frames, and a mapped Chris@1647: * reference frame. Event has an operator< defining a total ordering, Chris@1647: * by frame first and then by the other properties. Chris@1615: * Chris@1615: * Event is based on the Clipboard::Point type up to SV v3.2.1 and is Chris@1615: * intended also to replace the custom point types previously found in Chris@1615: * sparse models. Chris@1615: */ Chris@1615: class Event Chris@1611: { Chris@1611: public: Chris@1623: Event() : Chris@1629: m_haveValue(false), m_haveLevel(false), Chris@1629: m_haveDuration(false), m_haveReferenceFrame(false), Chris@1623: m_value(0.f), m_level(0.f), m_frame(0), Chris@1623: m_duration(0), m_referenceFrame(0), m_label() { } Chris@1623: Chris@1615: Event(sv_frame_t frame) : Chris@1629: m_haveValue(false), m_haveLevel(false), Chris@1629: m_haveDuration(false), m_haveReferenceFrame(false), Chris@1615: m_value(0.f), m_level(0.f), m_frame(frame), Chris@1615: m_duration(0), m_referenceFrame(0), m_label() { } Chris@1615: Chris@1615: Event(sv_frame_t frame, QString label) : Chris@1629: m_haveValue(false), m_haveLevel(false), Chris@1629: m_haveDuration(false), m_haveReferenceFrame(false), Chris@1611: m_value(0.f), m_level(0.f), m_frame(frame), Chris@1611: m_duration(0), m_referenceFrame(0), m_label(label) { } Chris@1611: Chris@1615: Event(sv_frame_t frame, float value, QString label) : Chris@1629: m_haveValue(true), m_haveLevel(false), Chris@1629: m_haveDuration(false), m_haveReferenceFrame(false), Chris@1611: m_value(value), m_level(0.f), m_frame(frame), Chris@1611: m_duration(0), m_referenceFrame(0), m_label(label) { } Chris@1611: Chris@1615: Event(sv_frame_t frame, float value, sv_frame_t duration, QString label) : Chris@1629: m_haveValue(true), m_haveLevel(false), Chris@1629: m_haveDuration(true), m_haveReferenceFrame(false), Chris@1611: m_value(value), m_level(0.f), m_frame(frame), Chris@1615: m_duration(duration), m_referenceFrame(0), m_label(label) { Chris@1615: if (m_duration < 0) throw std::logic_error("duration must be >= 0"); Chris@1615: } Chris@1611: Chris@1615: Event(sv_frame_t frame, float value, sv_frame_t duration, Chris@1612: float level, QString label) : Chris@1629: m_haveValue(true), m_haveLevel(true), Chris@1629: m_haveDuration(true), m_haveReferenceFrame(false), Chris@1611: m_value(value), m_level(level), m_frame(frame), Chris@1615: m_duration(duration), m_referenceFrame(0), m_label(label) { Chris@1615: if (m_duration < 0) throw std::logic_error("duration must be >= 0"); Chris@1615: } Chris@1611: Chris@1615: Event(const Event &event) =default; Chris@1647: Chris@1647: // We would ideally like Event to be immutable - but we have to Chris@1647: // have these because otherwise we can't put Events in vectors Chris@1647: // etc. Let's call it conceptually immutable Chris@1615: Event &operator=(const Event &event) =default; Chris@1615: Event &operator=(Event &&event) =default; Chris@1611: Chris@1611: sv_frame_t getFrame() const { return m_frame; } Chris@1611: Chris@1615: Event withFrame(sv_frame_t frame) const { Chris@1615: Event p(*this); Chris@1611: p.m_frame = frame; Chris@1611: return p; Chris@1611: } Chris@1611: Chris@1615: bool hasValue() const { return m_haveValue; } Chris@1634: float getValue() const { return m_haveValue ? m_value : 0.f; } Chris@1611: Chris@1615: Event withValue(float value) const { Chris@1615: Event p(*this); Chris@1611: p.m_haveValue = true; Chris@1611: p.m_value = value; Chris@1611: return p; Chris@1611: } Chris@1615: Event withoutValue() const { Chris@1615: Event p(*this); Chris@1615: p.m_haveValue = false; Chris@1615: p.m_value = 0.f; Chris@1615: return p; Chris@1615: } Chris@1611: Chris@1629: bool hasDuration() const { return m_haveDuration; } Chris@1634: sv_frame_t getDuration() const { return m_haveDuration ? m_duration : 0; } Chris@1611: Chris@1615: Event withDuration(sv_frame_t duration) const { Chris@1615: Event p(*this); Chris@1611: p.m_duration = duration; Chris@1629: p.m_haveDuration = true; Chris@1615: if (duration < 0) throw std::logic_error("duration must be >= 0"); Chris@1615: return p; Chris@1615: } Chris@1615: Event withoutDuration() const { Chris@1615: Event p(*this); Chris@1629: p.m_haveDuration = false; Chris@1615: p.m_duration = 0; Chris@1611: return p; Chris@1611: } Chris@1620: Chris@1620: bool hasLabel() const { return m_label != QString(); } Chris@1611: QString getLabel() const { return m_label; } Chris@1611: Chris@1615: Event withLabel(QString label) const { Chris@1615: Event p(*this); Chris@1611: p.m_label = label; Chris@1611: return p; Chris@1611: } Chris@1663: Chris@1663: bool hasUri() const { return m_uri != QString(); } Chris@1663: QString getURI() const { return m_uri; } Chris@1663: Chris@1663: Event withURI(QString uri) const { Chris@1663: Event p(*this); Chris@1663: p.m_uri = uri; Chris@1663: return p; Chris@1663: } Chris@1611: Chris@1615: bool hasLevel() const { return m_haveLevel; } Chris@1634: float getLevel() const { return m_haveLevel ? m_level : 0.f; } Chris@1615: Chris@1615: Event withLevel(float level) const { Chris@1615: Event p(*this); Chris@1611: p.m_haveLevel = true; Chris@1611: p.m_level = level; Chris@1611: return p; Chris@1611: } Chris@1615: Event withoutLevel() const { Chris@1615: Event p(*this); Chris@1615: p.m_haveLevel = false; Chris@1615: p.m_level = 0.f; Chris@1615: return p; Chris@1615: } Chris@1611: Chris@1615: bool hasReferenceFrame() const { return m_haveReferenceFrame; } Chris@1634: sv_frame_t getReferenceFrame() const { Chris@1634: return m_haveReferenceFrame ? m_referenceFrame : m_frame; Chris@1634: } Chris@1611: Chris@1615: bool referenceFrameDiffers() const { // from event frame Chris@1611: return m_haveReferenceFrame && (m_referenceFrame != m_frame); Chris@1611: } Chris@1611: Chris@1615: Event withReferenceFrame(sv_frame_t frame) const { Chris@1615: Event p(*this); Chris@1611: p.m_haveReferenceFrame = true; Chris@1611: p.m_referenceFrame = frame; Chris@1611: return p; Chris@1611: } Chris@1615: Event withoutReferenceFrame() const { Chris@1615: Event p(*this); Chris@1615: p.m_haveReferenceFrame = false; Chris@1615: p.m_referenceFrame = 0; Chris@1615: return p; Chris@1615: } Chris@1612: Chris@1615: bool operator==(const Event &p) const { Chris@1612: Chris@1612: if (m_frame != p.m_frame) return false; Chris@1629: Chris@1629: if (m_haveDuration != p.m_haveDuration) return false; Chris@1629: if (m_haveDuration && (m_duration != p.m_duration)) return false; Chris@1612: Chris@1612: if (m_haveValue != p.m_haveValue) return false; Chris@1612: if (m_haveValue && (m_value != p.m_value)) return false; Chris@1612: Chris@1612: if (m_haveLevel != p.m_haveLevel) return false; Chris@1612: if (m_haveLevel && (m_level != p.m_level)) return false; Chris@1612: Chris@1612: if (m_haveReferenceFrame != p.m_haveReferenceFrame) return false; Chris@1612: if (m_haveReferenceFrame && Chris@1612: (m_referenceFrame != p.m_referenceFrame)) return false; Chris@1612: Chris@1612: if (m_label != p.m_label) return false; Chris@1663: if (m_uri != p.m_uri) return false; Chris@1612: Chris@1612: return true; Chris@1612: } Chris@1612: Chris@1630: bool operator!=(const Event &p) const { Chris@1630: return !operator==(p); Chris@1630: } Chris@1630: Chris@1615: bool operator<(const Event &p) const { Chris@1612: Chris@1629: if (m_frame != p.m_frame) { Chris@1629: return m_frame < p.m_frame; Chris@1629: } Chris@1612: Chris@1615: // events without a property sort before events with that property Chris@1612: Chris@1629: if (m_haveDuration != p.m_haveDuration) { Chris@1629: return !m_haveDuration; Chris@1629: } Chris@1629: if (m_haveDuration && (m_duration != p.m_duration)) { Chris@1629: return m_duration < p.m_duration; Chris@1629: } Chris@1629: Chris@1629: if (m_haveValue != p.m_haveValue) { Chris@1629: return !m_haveValue; Chris@1629: } Chris@1629: if (m_haveValue && (m_value != p.m_value)) { Chris@1629: return m_value < p.m_value; Chris@1629: } Chris@1612: Chris@1629: if (m_haveLevel != p.m_haveLevel) { Chris@1629: return !m_haveLevel; Chris@1629: } Chris@1629: if (m_haveLevel && (m_level != p.m_level)) { Chris@1629: return m_level < p.m_level; Chris@1629: } Chris@1612: Chris@1612: if (m_haveReferenceFrame != p.m_haveReferenceFrame) { Chris@1612: return !m_haveReferenceFrame; Chris@1612: } Chris@1612: if (m_haveReferenceFrame && (m_referenceFrame != p.m_referenceFrame)) { Chris@1612: return m_referenceFrame < p.m_referenceFrame; Chris@1612: } Chris@1612: Chris@1663: if (m_label != p.m_label) { Chris@1663: return m_label < p.m_label; Chris@1663: } Chris@1663: return m_uri < p.m_uri; Chris@1612: } Chris@1612: Chris@1674: struct ExportNameOptions { Chris@1674: Chris@1674: ExportNameOptions() : Chris@1789: valueAttributeName("value"), Chris@1789: levelAttributeName("level"), Chris@1674: uriAttributeName("uri") { } Chris@1674: Chris@1789: QString valueAttributeName; Chris@1789: QString levelAttributeName; Chris@1674: QString uriAttributeName; Chris@1674: }; Chris@1674: Chris@1612: void toXml(QTextStream &stream, Chris@1612: QString indent = "", Chris@1674: QString extraAttributes = "", Chris@1674: ExportNameOptions opts = ExportNameOptions()) const { Chris@1612: Chris@1615: // For I/O purposes these are points, not events Chris@1612: stream << indent << QString("\n"; Chris@1612: } Chris@1612: Chris@1612: QString toXmlString(QString indent = "", Chris@1612: QString extraAttributes = "") const { Chris@1612: QString s; Chris@1612: QTextStream out(&s); Chris@1612: toXml(out, indent, extraAttributes); Chris@1612: out.flush(); Chris@1612: return s; Chris@1612: } Chris@1615: Chris@1629: NoteData toNoteData(sv_samplerate_t sampleRate, Chris@1637: bool valueIsMidiPitch) const { Chris@1615: Chris@1615: sv_frame_t duration; Chris@1629: if (m_haveDuration && m_duration > 0) { Chris@1615: duration = m_duration; Chris@1615: } else { Chris@1615: duration = sv_frame_t(sampleRate / 6); // arbitrary short duration Chris@1615: } Chris@1615: Chris@1615: int midiPitch; Chris@1615: float frequency = 0.f; Chris@1615: if (m_haveValue) { Chris@1615: if (valueIsMidiPitch) { Chris@1615: midiPitch = int(roundf(m_value)); Chris@1615: } else { Chris@1615: frequency = m_value; Chris@1615: midiPitch = Pitch::getPitchForFrequency(frequency); Chris@1615: } Chris@1615: } else { Chris@1615: midiPitch = 64; Chris@1615: valueIsMidiPitch = true; Chris@1615: } Chris@1615: Chris@1615: int velocity = 100; Chris@1615: if (m_haveLevel) { Chris@1615: if (m_level > 0.f && m_level <= 1.f) { Chris@1615: velocity = int(roundf(m_level * 127.f)); Chris@1615: } Chris@1615: } Chris@1615: Chris@1615: NoteData n(m_frame, duration, midiPitch, velocity); Chris@1615: n.isMidiPitchQuantized = valueIsMidiPitch; Chris@1615: if (!valueIsMidiPitch) { Chris@1615: n.frequency = frequency; Chris@1615: } Chris@1615: Chris@1615: return n; Chris@1615: } Chris@1623: Chris@1629: QString toDelimitedDataString(QString delimiter, Chris@1629: DataExportOptions opts, Chris@1629: sv_samplerate_t sampleRate) const { Chris@1629: QStringList list; Chris@1629: Chris@1629: list << RealTime::frame2RealTime(m_frame, sampleRate) Chris@1629: .toString().c_str(); Chris@1629: Chris@1629: if (m_haveValue) { Chris@1629: list << QString("%1").arg(m_value); Chris@1629: } Chris@1629: Chris@1629: if (m_haveDuration) { Chris@1629: list << RealTime::frame2RealTime(m_duration, sampleRate) Chris@1629: .toString().c_str(); Chris@1629: } Chris@1629: Chris@1629: if (m_haveLevel) { Chris@1629: if (!(opts & DataExportOmitLevels)) { Chris@1629: list << QString("%1").arg(m_level); Chris@1629: } Chris@1629: } Chris@1787: Chris@1787: // Put URI before label, to preserve the ordering previously Chris@1787: // used in the custom Image model exporter. We shouldn't Chris@1787: // change the column ordering unless (until?) we provide a Chris@1787: // facility for the user to customise it Chris@1787: if (m_uri != "") list << m_uri; Chris@1629: if (m_label != "") list << m_label; Chris@1629: Chris@1629: return list.join(delimiter); Chris@1629: } Chris@1799: Chris@1623: uint hash(uint seed = 0) const { Chris@1623: uint h = qHash(m_label, seed); Chris@1623: if (m_haveValue) h ^= qHash(m_value); Chris@1623: if (m_haveLevel) h ^= qHash(m_level); Chris@1623: h ^= qHash(m_frame); Chris@1629: if (m_haveDuration) h ^= qHash(m_duration); Chris@1623: if (m_haveReferenceFrame) h ^= qHash(m_referenceFrame); Chris@1663: h ^= qHash(m_uri); Chris@1623: return h; Chris@1623: } Chris@1611: Chris@1611: private: Chris@1611: // The order of fields here is chosen to minimise overall size of struct. Chris@1612: // We potentially store very many of these objects. Chris@1611: // If you change something, check what difference it makes to packing. Chris@1611: bool m_haveValue : 1; Chris@1611: bool m_haveLevel : 1; Chris@1629: bool m_haveDuration : 1; Chris@1611: bool m_haveReferenceFrame : 1; Chris@1611: float m_value; Chris@1611: float m_level; Chris@1611: sv_frame_t m_frame; Chris@1611: sv_frame_t m_duration; Chris@1611: sv_frame_t m_referenceFrame; Chris@1611: QString m_label; Chris@1663: QString m_uri; Chris@1611: }; Chris@1611: Chris@1623: inline uint qHash(const Event &e, uint seed = 0) { Chris@1623: return e.hash(seed); Chris@1623: } Chris@1623: Chris@1615: typedef std::vector EventVector; Chris@1612: Chris@1611: #endif