annotate base/EventSeries.h @ 1624:dcd510bd89db single-point

Try out Qt containers
author Chris Cannam
date Mon, 11 Mar 2019 11:17:30 +0000
parents f594fd249473
children 7dbb2a7b592e
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@1612 19
Chris@1612 20 #include <set>
Chris@1612 21
Chris@1624 22 #include <QHash>
Chris@1624 23 #include <QMap>
Chris@1624 24
Chris@1615 25 //#define DEBUG_EVENT_SERIES 1
Chris@1614 26
Chris@1615 27 /**
Chris@1615 28 * Container storing a series of events, with or without durations,
Chris@1616 29 * and supporting the ability to query which events are active at a
Chris@1616 30 * given frame or within a span of frames.
Chris@1616 31 *
Chris@1616 32 * To that end, in addition to the series of events, it stores a
Chris@1616 33 * series of "seams", which are frame positions at which the set of
Chris@1616 34 * simultaneous events changes (i.e. an event of non-zero duration
Chris@1616 35 * starts or ends) associated with a set of the events that are active
Chris@1616 36 * at or from that position. These are updated when an event is added
Chris@1616 37 * or removed.
Chris@1618 38 *
Chris@1618 39 * Performance is highly dependent on the extent of overlapping events
Chris@1618 40 * and the order in which events are added. Each event (with duration)
Chris@1618 41 * that is added requires updating all the seams within the extent of
Chris@1618 42 * that event, taking a number of ordered-set updates proportional to
Chris@1618 43 * the number of events already existing within its extent. Add events
Chris@1618 44 * in order of start frame if possible.
Chris@1615 45 */
Chris@1615 46 class EventSeries
Chris@1612 47 {
Chris@1612 48 public:
Chris@1615 49 EventSeries() : m_count(0) { }
Chris@1616 50 ~EventSeries() =default;
Chris@1616 51
Chris@1616 52 EventSeries(const EventSeries &) =default;
Chris@1616 53 EventSeries &operator=(const EventSeries &) =default;
Chris@1616 54 EventSeries &operator=(EventSeries &&) =default;
Chris@1616 55
Chris@1616 56 bool operator==(const EventSeries &other) const {
Chris@1616 57 return m_events == other.m_events;
Chris@1616 58 }
Chris@1612 59
Chris@1615 60 void add(const Event &p) {
Chris@1612 61
Chris@1616 62 ++m_events[p];
Chris@1612 63 ++m_count;
Chris@1612 64
Chris@1615 65 if (p.hasDuration()) {
Chris@1617 66
Chris@1617 67 const sv_frame_t frame = p.getFrame();
Chris@1617 68 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
Chris@1612 69
Chris@1614 70 createSeam(frame);
Chris@1614 71 createSeam(endFrame);
Chris@1616 72
Chris@1616 73 // These calls must both succeed after calling createSeam above
Chris@1616 74 const auto i0 = m_seams.find(frame);
Chris@1616 75 const auto i1 = m_seams.find(endFrame);
Chris@1614 76
Chris@1614 77 for (auto i = i0; i != i1; ++i) {
Chris@1614 78 if (i == m_seams.end()) {
Chris@1615 79 SVCERR << "ERROR: EventSeries::add: "
Chris@1614 80 << "reached end of seam map"
Chris@1614 81 << endl;
Chris@1614 82 break;
Chris@1612 83 }
Chris@1624 84 i.value().insert(p);
Chris@1612 85 }
Chris@1614 86 }
Chris@1612 87
Chris@1615 88 #ifdef DEBUG_EVENT_SERIES
Chris@1614 89 std::cerr << "after add:" << std::endl;
Chris@1615 90 dumpEvents();
Chris@1614 91 dumpSeams();
Chris@1614 92 #endif
Chris@1612 93 }
Chris@1612 94
Chris@1615 95 void remove(const Event &p) {
Chris@1612 96
Chris@1616 97 // If we are removing the last (unique) example of an event,
Chris@1616 98 // then we also need to remove it from the seam map. If this
Chris@1616 99 // is only one of multiple identical events, then we don't.
Chris@1616 100 bool isUnique = false;
Chris@1616 101
Chris@1615 102 auto pitr = m_events.find(p);
Chris@1615 103 if (pitr == m_events.end()) {
Chris@1615 104 return; // we don't know this event
Chris@1612 105 } else {
Chris@1624 106 if (--(pitr.value()) == 0) {
Chris@1616 107 isUnique = true;
Chris@1616 108 m_events.erase(pitr);
Chris@1616 109 }
Chris@1612 110 --m_count;
Chris@1612 111 }
Chris@1612 112
Chris@1616 113 if (p.hasDuration() && isUnique) {
Chris@1616 114
Chris@1617 115 const sv_frame_t frame = p.getFrame();
Chris@1617 116 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
Chris@1612 117
Chris@1616 118 const auto i0 = m_seams.find(frame);
Chris@1616 119 const auto i1 = m_seams.find(endFrame);
Chris@1614 120
Chris@1615 121 #ifdef DEBUG_EVENT_SERIES
Chris@1615 122 // This should be impossible if we found p in m_events above
Chris@1614 123 if (i0 == m_seams.end() || i1 == m_seams.end()) {
Chris@1615 124 SVCERR << "ERROR: EventSeries::remove: either frame " << frame
Chris@1614 125 << " or endFrame " << endFrame
Chris@1615 126 << " for event not found in seam map: event is "
Chris@1612 127 << p.toXmlString() << endl;
Chris@1612 128 }
Chris@1614 129 #endif
Chris@1612 130
Chris@1614 131 for (auto i = i0; i != i1; ++i) {
Chris@1614 132 if (i == m_seams.end()) {
Chris@1615 133 // This can happen only if we have a negative
Chris@1616 134 // duration, which Event forbids
Chris@1616 135 throw std::logic_error("unexpectedly reached end of map");
Chris@1616 136 }
Chris@1616 137
Chris@1624 138 i.value().remove(p);
Chris@1616 139 }
Chris@1616 140
Chris@1616 141 // Tidy up by removing any entries that are now identical
Chris@1616 142 // to their predecessors
Chris@1616 143
Chris@1616 144 std::vector<sv_frame_t> redundant;
Chris@1616 145
Chris@1616 146 auto pitr = m_seams.end();
Chris@1616 147 if (i0 != m_seams.begin()) {
Chris@1616 148 pitr = i0;
Chris@1616 149 --pitr;
Chris@1616 150 }
Chris@1616 151
Chris@1616 152 for (auto i = i0; i != m_seams.end(); ++i) {
Chris@1624 153 if (pitr != m_seams.end() && i.value() == pitr.value()) {
Chris@1624 154 redundant.push_back(i.key());
Chris@1616 155 }
Chris@1616 156 pitr = i;
Chris@1616 157 if (i == i1) {
Chris@1614 158 break;
Chris@1614 159 }
Chris@1612 160 }
Chris@1612 161
Chris@1616 162 for (sv_frame_t f: redundant) {
Chris@1624 163 m_seams.remove(f);
Chris@1616 164 }
Chris@1612 165 }
Chris@1614 166
Chris@1615 167 #ifdef DEBUG_EVENT_SERIES
Chris@1614 168 std::cerr << "after remove:" << std::endl;
Chris@1615 169 dumpEvents();
Chris@1614 170 dumpSeams();
Chris@1614 171 #endif
Chris@1612 172 }
Chris@1612 173
Chris@1615 174 bool contains(const Event &p) {
Chris@1615 175 return m_events.find(p) != m_events.end();
Chris@1612 176 }
Chris@1612 177
Chris@1612 178 int count() const {
Chris@1612 179 return m_count;
Chris@1612 180 }
Chris@1612 181
Chris@1612 182 bool isEmpty() const {
Chris@1612 183 return m_count == 0;
Chris@1612 184 }
Chris@1612 185
Chris@1612 186 void clear() {
Chris@1615 187 m_events.clear();
Chris@1612 188 m_seams.clear();
Chris@1612 189 m_count = 0;
Chris@1612 190 }
Chris@1612 191
Chris@1612 192 /**
Chris@1616 193 * Retrieve all events any part of which falls within the span in
Chris@1616 194 * frames defined by the given frame f and duration d.
Chris@1616 195 *
Chris@1616 196 * - An event without duration is within the span if its own frame
Chris@1616 197 * is greater than or equal to f and less than f + d.
Chris@1616 198 *
Chris@1616 199 * - An event with duration is within the span if its start frame
Chris@1616 200 * is less than f + d and its start frame plus its duration is
Chris@1616 201 * greater than f.
Chris@1616 202 *
Chris@1619 203 * Note: Passing a duration of zero is seldom useful here; you
Chris@1619 204 * probably want getEventsCovering instead. getEventsSpanning(f,
Chris@1619 205 * 0) is not equivalent to getEventsCovering(f). The latter
Chris@1619 206 * includes durationless events at f and events starting at f,
Chris@1619 207 * both of which are excluded from the former.
Chris@1616 208 */
Chris@1617 209 EventVector getEventsSpanning(sv_frame_t frame,
Chris@1617 210 sv_frame_t duration) const {
Chris@1616 211 EventVector span;
Chris@1616 212
Chris@1617 213 const sv_frame_t start = frame;
Chris@1617 214 const sv_frame_t end = frame + duration;
Chris@1616 215
Chris@1616 216 // first find any zero-duration events
Chris@1616 217
Chris@1624 218 auto pitr = m_events.lowerBound(Event(start, QString()));
Chris@1624 219 while (pitr != m_events.end() && pitr.key().getFrame() < end) {
Chris@1624 220 if (!pitr.key().hasDuration()) {
Chris@1624 221 for (int i = 0; i < pitr.value(); ++i) {
Chris@1624 222 span.push_back(pitr.key());
Chris@1616 223 }
Chris@1616 224 }
Chris@1616 225 ++pitr;
Chris@1616 226 }
Chris@1616 227
Chris@1616 228 // now any non-zero-duration ones from the seam map
Chris@1616 229
Chris@1616 230 std::set<Event> found;
Chris@1624 231 auto sitr = m_seams.lowerBound(start);
Chris@1624 232 if (sitr == m_seams.end() || sitr.key() > start) {
Chris@1616 233 if (sitr != m_seams.begin()) {
Chris@1616 234 --sitr;
Chris@1616 235 }
Chris@1616 236 }
Chris@1624 237 while (sitr != m_seams.end() && sitr.key() < end) {
Chris@1624 238 for (const auto &p: sitr.value()) {
Chris@1616 239 found.insert(p);
Chris@1616 240 }
Chris@1616 241 ++sitr;
Chris@1616 242 }
Chris@1616 243 for (const auto &p: found) {
Chris@1624 244 int n = m_events.value(p);
Chris@1616 245 if (n < 1) {
Chris@1616 246 throw std::logic_error("event is in seams but not events");
Chris@1616 247 }
Chris@1616 248 for (int i = 0; i < n; ++i) {
Chris@1616 249 span.push_back(p);
Chris@1616 250 }
Chris@1616 251 }
Chris@1616 252
Chris@1616 253 return span;
Chris@1616 254 }
Chris@1616 255
Chris@1616 256 /**
Chris@1616 257 * Retrieve all events that cover the given frame. An event without
Chris@1616 258 * duration covers a frame if its own frame is equal to it. An event
Chris@1616 259 * with duration covers a frame if its start frame is less than or
Chris@1612 260 * equal to it and its end frame (start + duration) is greater
Chris@1612 261 * than it.
Chris@1612 262 */
Chris@1616 263 EventVector getEventsCovering(sv_frame_t frame) const {
Chris@1616 264 EventVector cover;
Chris@1614 265
Chris@1615 266 // first find any zero-duration events
Chris@1616 267
Chris@1624 268 auto pitr = m_events.lowerBound(Event(frame, QString()));
Chris@1624 269 while (pitr != m_events.end() && pitr.key().getFrame() == frame) {
Chris@1624 270 if (!pitr.key().hasDuration()) {
Chris@1624 271 for (int i = 0; i < pitr.value(); ++i) {
Chris@1624 272 cover.push_back(pitr.key());
Chris@1614 273 }
Chris@1614 274 }
Chris@1616 275 ++pitr;
Chris@1614 276 }
Chris@1614 277
Chris@1624 278 // now any non-zero-duration ones from the seam map. We insert
Chris@1624 279 // these into a std::set to recover the ordering, lost by QSet
Chris@1616 280
Chris@1624 281 std::set<Event> found;
Chris@1624 282 auto sitr = m_seams.lowerBound(frame);
Chris@1624 283 if (sitr == m_seams.end() || sitr.key() > frame) {
Chris@1614 284 if (sitr != m_seams.begin()) {
Chris@1614 285 --sitr;
Chris@1614 286 }
Chris@1614 287 }
Chris@1624 288 if (sitr != m_seams.end() && sitr.key() <= frame) {
Chris@1624 289 for (const auto &p: sitr.value()) {
Chris@1624 290 found.insert(p);
Chris@1624 291 }
Chris@1624 292 ++sitr;
Chris@1624 293 }
Chris@1624 294 for (const auto &p: found) {
Chris@1624 295 int n = m_events.value(p);
Chris@1624 296 if (n < 1) {
Chris@1624 297 throw std::logic_error("event is in seams but not events");
Chris@1624 298 }
Chris@1624 299 for (int i = 0; i < n; ++i) {
Chris@1624 300 cover.push_back(p);
Chris@1614 301 }
Chris@1614 302 }
Chris@1614 303
Chris@1616 304 return cover;
Chris@1612 305 }
Chris@1612 306
Chris@1612 307 private:
Chris@1616 308 /**
Chris@1616 309 * Total number of events in the series. Will exceed
Chris@1616 310 * m_events.size() if the series contains duplicate events.
Chris@1616 311 */
Chris@1612 312 int m_count;
Chris@1624 313
Chris@1616 314 /**
Chris@1616 315 * The (ordered) Events map acts as a list of all the events in
Chris@1616 316 * the series. For reasons of backward compatibility, we have to
Chris@1616 317 * handle series containing multiple instances of identical
Chris@1616 318 * events; the int indexed against each event records the number
Chris@1616 319 * of instances of that event. We do this in preference to using a
Chris@1616 320 * multiset, in order to support the case in which we have
Chris@1616 321 * obtained a set of distinct events (from the seam container
Chris@1616 322 * below) and then need to return the correct number of each.
Chris@1616 323 *
Chris@1616 324 * Because events are immutable, we never have to move one to a
Chris@1616 325 * different "key" in this map - we only add or delete them or
Chris@1616 326 * change their counts.
Chris@1616 327 */
Chris@1624 328 typedef QMap<Event, int> Events;
Chris@1616 329 Events m_events;
Chris@1614 330
Chris@1616 331 /**
Chris@1616 332 * The FrameEventMap maps from frame number to a set of events. In
Chris@1616 333 * the seam map this is used to represent the events that are
Chris@1616 334 * active at that frame, either because they begin at that frame
Chris@1616 335 * or because they are continuing from an earlier frame. There is
Chris@1616 336 * an entry here for each frame at which an event starts or ends,
Chris@1616 337 * with the event appearing in all entries from its start time
Chris@1616 338 * onward and disappearing again at its end frame.
Chris@1616 339 *
Chris@1616 340 * Only events with duration appear in this map; point events
Chris@1616 341 * appear only in m_events.
Chris@1616 342 */
Chris@1624 343 typedef QMap<sv_frame_t, QSet<Event>> FrameEventMap;
Chris@1616 344 FrameEventMap m_seams;
Chris@1614 345
Chris@1614 346 /** Create a seam at the given frame, copying from the prior seam
Chris@1614 347 * if there is one. If a seam already exists at the given frame,
Chris@1614 348 * leave it untouched.
Chris@1614 349 */
Chris@1614 350 void createSeam(sv_frame_t frame) {
Chris@1624 351 auto itr = m_seams.lowerBound(frame);
Chris@1624 352 if (itr == m_seams.end() || itr.key() > frame) {
Chris@1614 353 if (itr != m_seams.begin()) {
Chris@1614 354 --itr;
Chris@1614 355 }
Chris@1614 356 }
Chris@1614 357 if (itr == m_seams.end()) {
Chris@1614 358 m_seams[frame] = {};
Chris@1624 359 } else if (itr.key() < frame) {
Chris@1624 360 m_seams[frame] = itr.value();
Chris@1624 361 } else if (itr.key() > frame) { // itr must be begin()
Chris@1614 362 m_seams[frame] = {};
Chris@1614 363 }
Chris@1614 364 }
Chris@1614 365
Chris@1615 366 #ifdef DEBUG_EVENT_SERIES
Chris@1615 367 void dumpEvents() const {
Chris@1618 368 std::cerr << "EVENTS (" << m_events.size() << ") [" << std::endl;
Chris@1616 369 for (const auto &i: m_events) {
Chris@1618 370 std::cerr << " " << i.second << "x " << i.first.toXmlString();
Chris@1614 371 }
Chris@1614 372 std::cerr << "]" << std::endl;
Chris@1614 373 }
Chris@1614 374
Chris@1614 375 void dumpSeams() const {
Chris@1618 376 std::cerr << "SEAMS (" << m_seams.size() << ") [" << std::endl;
Chris@1614 377 for (const auto &s: m_seams) {
Chris@1614 378 std::cerr << " " << s.first << " -> {" << std::endl;
Chris@1614 379 for (const auto &p: s.second) {
Chris@1614 380 std::cerr << p.toXmlString(" ");
Chris@1614 381 }
Chris@1614 382 std::cerr << " }" << std::endl;
Chris@1614 383 }
Chris@1614 384 std::cerr << "]" << std::endl;
Chris@1614 385 }
Chris@1614 386 #endif
Chris@1612 387 };
Chris@1612 388
Chris@1612 389 #endif