annotate base/EventSeries.h @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +0100
parents c546429d4c2f
children
rev   line source
Chris@1612 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1612 2
Chris@1612 3 /*
Chris@1612 4 Sonic Visualiser
Chris@1612 5 An audio file viewer and annotation editor.
Chris@1612 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1612 7
Chris@1612 8 This program is free software; you can redistribute it and/or
Chris@1612 9 modify it under the terms of the GNU General Public License as
Chris@1612 10 published by the Free Software Foundation; either version 2 of the
Chris@1612 11 License, or (at your option) any later version. See the file
Chris@1612 12 COPYING included with this distribution for more information.
Chris@1612 13 */
Chris@1612 14
Chris@1615 15 #ifndef SV_EVENT_SERIES_H
Chris@1615 16 #define SV_EVENT_SERIES_H
Chris@1612 17
Chris@1615 18 #include "Event.h"
Chris@1631 19 #include "XmlExportable.h"
Chris@1612 20
Chris@1612 21 #include <set>
Chris@1833 22 #include <string>
Chris@1833 23 #include <vector>
Chris@1687 24 #include <functional>
Chris@1612 25
Chris@1796 26 #include <QMutex>
Chris@1796 27
Chris@1615 28 //#define DEBUG_EVENT_SERIES 1
Chris@1614 29
Chris@1615 30 /**
Chris@1615 31 * Container storing a series of events, with or without durations,
Chris@1616 32 * and supporting the ability to query which events are active at a
Chris@1616 33 * given frame or within a span of frames.
Chris@1616 34 *
Chris@1616 35 * To that end, in addition to the series of events, it stores a
Chris@1616 36 * series of "seams", which are frame positions at which the set of
Chris@1616 37 * simultaneous events changes (i.e. an event of non-zero duration
Chris@1616 38 * starts or ends) associated with a set of the events that are active
Chris@1616 39 * at or from that position. These are updated when an event is added
Chris@1616 40 * or removed.
Chris@1618 41 *
Chris@1632 42 * This class is highly optimised for inserting events in increasing
Chris@1632 43 * order of start frame. Inserting (or deleting) events in the middle
Chris@1632 44 * does work, and should be acceptable in interactive use, but it is
Chris@1632 45 * very slow in bulk.
Chris@1795 46 *
Chris@1796 47 * EventSeries is thread-safe.
Chris@1615 48 */
Chris@1631 49 class EventSeries : public XmlExportable
Chris@1612 50 {
Chris@1612 51 public:
Chris@1640 52 EventSeries() : m_finalDurationlessEventFrame(0) { }
Chris@1616 53 ~EventSeries() =default;
Chris@1616 54
Chris@1796 55 EventSeries(const EventSeries &);
Chris@1795 56
Chris@1796 57 EventSeries &operator=(const EventSeries &);
Chris@1796 58 EventSeries &operator=(EventSeries &&);
Chris@1616 59
Chris@1796 60 bool operator==(const EventSeries &other) const;
Chris@1679 61
Chris@1679 62 static EventSeries fromEvents(const EventVector &ee);
Chris@1612 63
Chris@1631 64 void clear();
Chris@1632 65 void add(const Event &e);
Chris@1632 66 void remove(const Event &e);
Chris@1632 67 bool contains(const Event &e) const;
Chris@1631 68 bool isEmpty() const;
Chris@1631 69 int count() const;
Chris@1612 70
Chris@1612 71 /**
Chris@1640 72 * Return the frame of the first event in the series. If there are
Chris@1640 73 * no events, return 0.
Chris@1640 74 */
Chris@1640 75 sv_frame_t getStartFrame() const;
Chris@1640 76
Chris@1640 77 /**
Chris@1640 78 * Return the frame plus duration of the event in the series that
Chris@1640 79 * ends last. If there are no events, return 0.
Chris@1640 80 */
Chris@1640 81 sv_frame_t getEndFrame() const;
Chris@1640 82
Chris@1640 83 /**
Chris@1635 84 * Retrieve all events any part of which falls within the range in
Chris@1616 85 * frames defined by the given frame f and duration d.
Chris@1616 86 *
Chris@1635 87 * - An event without duration is spanned by the range if its own
Chris@1635 88 * frame is greater than or equal to f and less than f + d.
Chris@1616 89 *
Chris@1635 90 * - An event with duration is spanned by the range if its start
Chris@1635 91 * frame is less than f + d and its start frame plus its duration
Chris@1635 92 * is greater than f.
Chris@1616 93 *
Chris@1619 94 * Note: Passing a duration of zero is seldom useful here; you
Chris@1619 95 * probably want getEventsCovering instead. getEventsSpanning(f,
Chris@1619 96 * 0) is not equivalent to getEventsCovering(f). The latter
Chris@1619 97 * includes durationless events at f and events starting at f,
Chris@1619 98 * both of which are excluded from the former.
Chris@1616 99 */
Chris@1617 100 EventVector getEventsSpanning(sv_frame_t frame,
Chris@1631 101 sv_frame_t duration) const;
Chris@1616 102
Chris@1616 103 /**
Chris@1656 104 * Retrieve all events that cover the given frame. An event without
Chris@1656 105 * duration covers a frame if its own frame is equal to it. An event
Chris@1656 106 * with duration covers a frame if its start frame is less than or
Chris@1656 107 * equal to it and its end frame (start + duration) is greater
Chris@1656 108 * than it.
Chris@1656 109 */
Chris@1656 110 EventVector getEventsCovering(sv_frame_t frame) const;
Chris@1656 111
Chris@1656 112 /**
Chris@1635 113 * Retrieve all events falling wholly within the range in frames
Chris@1635 114 * defined by the given frame f and duration d.
Chris@1635 115 *
Chris@1635 116 * - An event without duration is within the range if its own
Chris@1635 117 * frame is greater than or equal to f and less than f + d.
Chris@1635 118 *
Chris@1635 119 * - An event with duration is within the range if its start frame
Chris@1635 120 * is greater than or equal to f and its start frame plus its
Chris@1635 121 * duration is less than or equal to f + d.
Chris@1654 122 *
Chris@1654 123 * If overspill is greater than zero, also include that number of
Chris@1654 124 * additional events (where they exist) both before and after the
Chris@1654 125 * edges of the range.
Chris@1635 126 */
Chris@1635 127 EventVector getEventsWithin(sv_frame_t frame,
Chris@1654 128 sv_frame_t duration,
Chris@1654 129 int overspill = 0) const;
Chris@1635 130
Chris@1635 131 /**
Chris@1638 132 * Retrieve all events starting within the range in frames defined
Chris@1656 133 * by the given frame f and duration d. An event (regardless of
Chris@1656 134 * whether it has duration or not) starts within the range if its
Chris@1656 135 * start frame is greater than or equal to f and less than f + d.
Chris@1638 136 */
Chris@1638 137 EventVector getEventsStartingWithin(sv_frame_t frame,
Chris@1638 138 sv_frame_t duration) const;
Chris@1638 139
Chris@1638 140 /**
Chris@1656 141 * Retrieve all events starting at exactly the given frame.
Chris@1612 142 */
Chris@1656 143 EventVector getEventsStartingAt(sv_frame_t frame) const {
Chris@1656 144 return getEventsStartingWithin(frame, 1);
Chris@1656 145 }
Chris@1644 146
Chris@1644 147 /**
Chris@1644 148 * Retrieve all events, in their natural order.
Chris@1644 149 */
Chris@1644 150 EventVector getAllEvents() const;
Chris@1635 151
Chris@1631 152 /**
Chris@1632 153 * If e is in the series and is not the first event in it, set
Chris@1632 154 * preceding to the event immediate preceding it according to the
Chris@1632 155 * standard event ordering and return true. Otherwise leave
Chris@1632 156 * preceding unchanged and return false.
Chris@1632 157 *
Chris@1632 158 * If there are multiple events identical to e in the series,
Chris@1632 159 * assume that the event passed in is the first one (i.e. never
Chris@1632 160 * set preceding equal to e).
Chris@1633 161 *
Chris@1633 162 * It is acceptable for preceding to alias e when this is called.
Chris@1632 163 */
Chris@1632 164 bool getEventPreceding(const Event &e, Event &preceding) const;
Chris@1632 165
Chris@1632 166 /**
Chris@1632 167 * If e is in the series and is not the final event in it, set
Chris@1632 168 * following to the event immediate following it according to the
Chris@1632 169 * standard event ordering and return true. Otherwise leave
Chris@1632 170 * following unchanged and return false.
Chris@1632 171 *
Chris@1632 172 * If there are multiple events identical to e in the series,
Chris@1632 173 * assume that the event passed in is the last one (i.e. never set
Chris@1632 174 * following equal to e).
Chris@1633 175 *
Chris@1633 176 * It is acceptable for following to alias e when this is called.
Chris@1632 177 */
Chris@1632 178 bool getEventFollowing(const Event &e, Event &following) const;
Chris@1632 179
Chris@1653 180 enum Direction {
Chris@1653 181 Forward,
Chris@1653 182 Backward
Chris@1653 183 };
Chris@1653 184
Chris@1653 185 /**
Chris@1653 186 * Return the first event for which the given predicate returns
Chris@1653 187 * true, searching events with start frames increasingly far from
Chris@1653 188 * the given frame in the given direction. If the direction is
Chris@1653 189 * Forward then the search includes events starting at the given
Chris@1653 190 * frame, otherwise it does not.
Chris@1653 191 */
Chris@1653 192 bool getNearestEventMatching(sv_frame_t startSearchAt,
Chris@1653 193 std::function<bool(const Event &)> predicate,
Chris@1653 194 Direction direction,
Chris@1653 195 Event &found) const;
Chris@1653 196
Chris@1632 197 /**
Chris@1632 198 * Return the event at the given numerical index in the series,
Chris@1632 199 * where 0 = the first event and count()-1 = the last.
Chris@1632 200 */
Chris@1632 201 Event getEventByIndex(int index) const;
Chris@1640 202
Chris@1640 203 /**
Chris@1640 204 * Return the index of the first event in the series that does not
Chris@1640 205 * compare inferior to the given event. If there is no such event,
Chris@1640 206 * return count().
Chris@1640 207 */
Chris@1640 208 int getIndexForEvent(const Event &e) const;
Chris@1653 209
Chris@1632 210 /**
Chris@1631 211 * Emit to XML as a dataset element.
Chris@1631 212 */
Chris@1631 213 void toXml(QTextStream &out,
Chris@1631 214 QString indent,
Chris@1631 215 QString extraAttributes) const override;
Chris@1674 216
Chris@1674 217 /**
Chris@1674 218 * Emit to XML as a dataset element.
Chris@1674 219 */
Chris@1674 220 void toXml(QTextStream &out,
Chris@1674 221 QString indent,
Chris@1674 222 QString extraAttributes,
Chris@1674 223 Event::ExportNameOptions) const;
Chris@1679 224
Chris@1679 225 /**
Chris@1833 226 * Return a label for each column that would be written by
Chris@1833 227 * toStringExportRows.
Chris@1815 228 */
Chris@1833 229 QVector<QString>
Chris@1833 230 getStringExportHeaders(DataExportOptions options,
Chris@1833 231 Event::ExportNameOptions) const;
Chris@1815 232
Chris@1815 233 /**
Chris@1833 234 * Emit events starting within the given range as string rows
Chris@1833 235 * ready for conversion to an e.g. comma-separated data format.
Chris@1679 236 */
Chris@1833 237 QVector<QVector<QString>>
Chris@1833 238 toStringExportRows(DataExportOptions options,
Chris@1833 239 sv_frame_t startFrame,
Chris@1833 240 sv_frame_t duration,
Chris@1833 241 sv_samplerate_t sampleRate,
Chris@1833 242 sv_frame_t resolution,
Chris@1833 243 Event fillEvent) const;
Chris@1631 244
Chris@1612 245 private:
Chris@1796 246 mutable QMutex m_mutex;
Chris@1796 247
Chris@1797 248 EventSeries(const EventSeries &other, const QMutexLocker &);
Chris@1796 249
Chris@1616 250 /**
Chris@1631 251 * This vector contains all events in the series, in the normal
Chris@1631 252 * sort order. For backward compatibility we must support series
Chris@1631 253 * containing multiple instances of identical events, so
Chris@1631 254 * consecutive events in this vector will not always be distinct.
Chris@1631 255 * The vector is used in preference to a multiset or map<Event,
Chris@1631 256 * int> in order to allow indexing by "row number" as well as by
Chris@1631 257 * properties such as frame.
Chris@1631 258 *
Chris@1631 259 * Because events are immutable, we do not have to worry about the
Chris@1631 260 * order changing once an event is inserted - we only add or
Chris@1631 261 * delete them.
Chris@1616 262 */
Chris@1631 263 typedef std::vector<Event> Events;
Chris@1616 264 Events m_events;
Chris@1631 265
Chris@1616 266 /**
Chris@1616 267 * The FrameEventMap maps from frame number to a set of events. In
Chris@1616 268 * the seam map this is used to represent the events that are
Chris@1616 269 * active at that frame, either because they begin at that frame
Chris@1616 270 * or because they are continuing from an earlier frame. There is
Chris@1616 271 * an entry here for each frame at which an event starts or ends,
Chris@1616 272 * with the event appearing in all entries from its start time
Chris@1616 273 * onward and disappearing again at its end frame.
Chris@1616 274 *
Chris@1616 275 * Only events with duration appear in this map; point events
Chris@1627 276 * appear only in m_events. Note that unlike m_events, we only
Chris@1627 277 * store one instance of each event here, even if we hold many -
Chris@1627 278 * we refer back to m_events when we need to know how many
Chris@1627 279 * identical copies of a given event we have.
Chris@1616 280 */
Chris@1627 281 typedef std::map<sv_frame_t, std::vector<Event>> FrameEventMap;
Chris@1616 282 FrameEventMap m_seams;
Chris@1614 283
Chris@1640 284 /**
Chris@1640 285 * The frame of the last durationless event we have in the series.
Chris@1640 286 * This is to support a fast-ish getEndFrame(): we can easily keep
Chris@1640 287 * this up-to-date when events are added or removed, and we can
Chris@1640 288 * easily find the end frame of the last with-duration event from
Chris@1640 289 * the seam map, but it's not so easy to continuously update an
Chris@1640 290 * overall end frame or to find the last frame of all events
Chris@1640 291 * without this.
Chris@1640 292 */
Chris@1640 293 sv_frame_t m_finalDurationlessEventFrame;
Chris@1640 294
Chris@1796 295 /**
Chris@1796 296 * Create a seam at the given frame, copying from the prior seam
Chris@1796 297 * if there is one. If a seam already exists at the given frame,
Chris@1796 298 * leave it untouched.
Chris@1796 299 *
Chris@1796 300 * Call with m_mutex locked.
Chris@1614 301 */
Chris@1614 302 void createSeam(sv_frame_t frame) {
Chris@1625 303 auto itr = m_seams.lower_bound(frame);
Chris@1625 304 if (itr == m_seams.end() || itr->first > frame) {
Chris@1614 305 if (itr != m_seams.begin()) {
Chris@1614 306 --itr;
Chris@1614 307 }
Chris@1614 308 }
Chris@1614 309 if (itr == m_seams.end()) {
Chris@1614 310 m_seams[frame] = {};
Chris@1625 311 } else if (itr->first < frame) {
Chris@1625 312 m_seams[frame] = itr->second;
Chris@1625 313 } else if (itr->first > frame) { // itr must be begin()
Chris@1614 314 m_seams[frame] = {};
Chris@1614 315 }
Chris@1614 316 }
Chris@1614 317
Chris@1796 318 /**
Chris@1796 319 * Return true if the two seam map entries contain the same set of
Chris@1796 320 * events.
Chris@1796 321 *
Chris@1796 322 * Precondition: no duplicates, i.e. no event appears more than
Chris@1796 323 * once in s1 or more than once in s2.
Chris@1796 324 *
Chris@1796 325 * Call with m_mutex locked.
Chris@1796 326 */
Chris@1627 327 bool seamsEqual(const std::vector<Event> &s1,
Chris@1627 328 const std::vector<Event> &s2) const {
Chris@1627 329
Chris@1627 330 if (s1.size() != s2.size()) {
Chris@1627 331 return false;
Chris@1627 332 }
Chris@1627 333
Chris@1627 334 #ifdef DEBUG_EVENT_SERIES
Chris@1627 335 for (int i = 0; in_range_for(s1, i); ++i) {
Chris@1627 336 for (int j = i + 1; in_range_for(s1, j); ++j) {
Chris@1627 337 if (s1[i] == s1[j] || s2[i] == s2[j]) {
Chris@1627 338 throw std::runtime_error
Chris@1627 339 ("debug error: duplicate event in s1 or s2");
Chris@1627 340 }
Chris@1627 341 }
Chris@1627 342 }
Chris@1627 343 #endif
Chris@1627 344
Chris@1627 345 std::set<Event> ee;
Chris@1627 346 for (const auto &e: s1) {
Chris@1627 347 ee.insert(e);
Chris@1627 348 }
Chris@1627 349 for (const auto &e: s2) {
Chris@1627 350 if (ee.find(e) == ee.end()) {
Chris@1627 351 return false;
Chris@1627 352 }
Chris@1627 353 }
Chris@1627 354 return true;
Chris@1627 355 }
Chris@1627 356
Chris@1615 357 #ifdef DEBUG_EVENT_SERIES
Chris@1615 358 void dumpEvents() const {
Chris@1618 359 std::cerr << "EVENTS (" << m_events.size() << ") [" << std::endl;
Chris@1616 360 for (const auto &i: m_events) {
Chris@1631 361 std::cerr << " " << i.toXmlString();
Chris@1614 362 }
Chris@1614 363 std::cerr << "]" << std::endl;
Chris@1614 364 }
Chris@1614 365
Chris@1614 366 void dumpSeams() const {
Chris@1618 367 std::cerr << "SEAMS (" << m_seams.size() << ") [" << std::endl;
Chris@1614 368 for (const auto &s: m_seams) {
Chris@1614 369 std::cerr << " " << s.first << " -> {" << std::endl;
Chris@1614 370 for (const auto &p: s.second) {
Chris@1614 371 std::cerr << p.toXmlString(" ");
Chris@1614 372 }
Chris@1614 373 std::cerr << " }" << std::endl;
Chris@1614 374 }
Chris@1614 375 std::cerr << "]" << std::endl;
Chris@1614 376 }
Chris@1614 377 #endif
Chris@1612 378 };
Chris@1612 379
Chris@1612 380 #endif