annotate base/EventSeries.h @ 1615:24dc8cb42755 single-point

Rename a number of classes and methods (including Point -> Event); comments
author Chris Cannam
date Thu, 07 Mar 2019 15:44:09 +0000
parents base/PointSeries.h@2e14a7876945
children de446dd905e6
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@1615 22 //#define DEBUG_EVENT_SERIES 1
Chris@1614 23
Chris@1615 24 /**
Chris@1615 25 * Container storing a series of events, with or without durations,
Chris@1615 26 * and supporting the ability to query which events span a given frame
Chris@1615 27 * time. To that end, in addition to the series of events, it stores a
Chris@1615 28 * series of "seam points", which are the frame positions at which the
Chris@1615 29 * set of simultaneous events changes (i.e. an event of non-zero
Chris@1615 30 * duration starts or ends). These are updated when event is added or
Chris@1615 31 * removed.
Chris@1615 32 */
Chris@1615 33 class EventSeries
Chris@1612 34 {
Chris@1612 35 public:
Chris@1615 36 EventSeries() : m_count(0) { }
Chris@1612 37
Chris@1615 38 void add(const Event &p) {
Chris@1612 39
Chris@1615 40 m_events.insert(p);
Chris@1612 41 ++m_count;
Chris@1612 42
Chris@1615 43 if (p.hasDuration()) {
Chris@1612 44 sv_frame_t frame = p.getFrame();
Chris@1612 45 sv_frame_t endFrame = p.getFrame() + p.getDuration();
Chris@1612 46
Chris@1614 47 createSeam(frame);
Chris@1614 48 createSeam(endFrame);
Chris@1614 49
Chris@1614 50 auto i0 = m_seams.find(frame); // must succeed after createSeam
Chris@1614 51 auto i1 = m_seams.find(endFrame); // likewise
Chris@1614 52
Chris@1614 53 for (auto i = i0; i != i1; ++i) {
Chris@1614 54 if (i == m_seams.end()) {
Chris@1615 55 SVCERR << "ERROR: EventSeries::add: "
Chris@1614 56 << "reached end of seam map"
Chris@1614 57 << endl;
Chris@1614 58 break;
Chris@1612 59 }
Chris@1614 60 i->second.insert(p);
Chris@1612 61 }
Chris@1614 62 }
Chris@1612 63
Chris@1615 64 #ifdef DEBUG_EVENT_SERIES
Chris@1614 65 std::cerr << "after add:" << std::endl;
Chris@1615 66 dumpEvents();
Chris@1614 67 dumpSeams();
Chris@1614 68 #endif
Chris@1612 69 }
Chris@1612 70
Chris@1615 71 void remove(const Event &p) {
Chris@1612 72
Chris@1612 73 // erase first itr that matches p; if there is more than one
Chris@1612 74 // p, erase(p) would remove all of them, but we only want to
Chris@1612 75 // remove (any) one
Chris@1615 76 auto pitr = m_events.find(p);
Chris@1615 77 if (pitr == m_events.end()) {
Chris@1615 78 return; // we don't know this event
Chris@1612 79 } else {
Chris@1615 80 m_events.erase(pitr);
Chris@1612 81 --m_count;
Chris@1612 82 }
Chris@1612 83
Chris@1615 84 if (p.hasDuration()) {
Chris@1612 85 sv_frame_t frame = p.getFrame();
Chris@1612 86 sv_frame_t endFrame = p.getFrame() + p.getDuration();
Chris@1612 87
Chris@1614 88 auto i0 = m_seams.find(frame);
Chris@1614 89 auto i1 = m_seams.find(endFrame);
Chris@1614 90
Chris@1615 91 #ifdef DEBUG_EVENT_SERIES
Chris@1615 92 // This should be impossible if we found p in m_events above
Chris@1614 93 if (i0 == m_seams.end() || i1 == m_seams.end()) {
Chris@1615 94 SVCERR << "ERROR: EventSeries::remove: either frame " << frame
Chris@1614 95 << " or endFrame " << endFrame
Chris@1615 96 << " for event not found in seam map: event is "
Chris@1612 97 << p.toXmlString() << endl;
Chris@1612 98 }
Chris@1614 99 #endif
Chris@1612 100
Chris@1614 101 for (auto i = i0; i != i1; ++i) {
Chris@1614 102 if (i == m_seams.end()) {
Chris@1615 103 // This can happen only if we have a negative
Chris@1615 104 // duration, which Event forbids, but we don't
Chris@1615 105 // protect against it in this class, so we'll
Chris@1615 106 // leave this check in
Chris@1615 107 SVCERR << "ERROR: EventSeries::remove: "
Chris@1614 108 << "reached end of seam map"
Chris@1614 109 << endl;
Chris@1614 110 break;
Chris@1614 111 }
Chris@1614 112 // Can't just erase(p) as that would erase all of
Chris@1614 113 // them, if there are several identical ones
Chris@1614 114 auto si = i->second.find(p);
Chris@1614 115 if (si != i->second.end()) {
Chris@1614 116 i->second.erase(si);
Chris@1614 117 }
Chris@1612 118 }
Chris@1612 119
Chris@1612 120 // Shall we "garbage-collect" here? We could be leaving
Chris@1615 121 // lots of empty event-sets, or consecutive identical
Chris@1612 122 // ones, which are a pure irrelevance that take space and
Chris@1612 123 // slow us down. But a lot depends on whether callers ever
Chris@1612 124 // really delete anything much.
Chris@1612 125 }
Chris@1614 126
Chris@1615 127 #ifdef DEBUG_EVENT_SERIES
Chris@1614 128 std::cerr << "after remove:" << std::endl;
Chris@1615 129 dumpEvents();
Chris@1614 130 dumpSeams();
Chris@1614 131 #endif
Chris@1612 132 }
Chris@1612 133
Chris@1615 134 bool contains(const Event &p) {
Chris@1615 135 return m_events.find(p) != m_events.end();
Chris@1612 136 }
Chris@1612 137
Chris@1612 138 int count() const {
Chris@1612 139 return m_count;
Chris@1612 140 }
Chris@1612 141
Chris@1612 142 bool isEmpty() const {
Chris@1612 143 return m_count == 0;
Chris@1612 144 }
Chris@1612 145
Chris@1612 146 void clear() {
Chris@1615 147 m_events.clear();
Chris@1612 148 m_seams.clear();
Chris@1612 149 m_count = 0;
Chris@1612 150 }
Chris@1612 151
Chris@1612 152 /**
Chris@1615 153 * Retrieve all events that span the given frame. A event without
Chris@1615 154 * duration spans a frame if its own frame is equal to it. A event
Chris@1612 155 * with duration spans a frame if its start frame is less than or
Chris@1612 156 * equal to it and its end frame (start + duration) is greater
Chris@1612 157 * than it.
Chris@1612 158 */
Chris@1615 159 EventVector getEventsSpanning(sv_frame_t frame) const {
Chris@1615 160 EventVector span;
Chris@1614 161
Chris@1615 162 // first find any zero-duration events
Chris@1615 163 auto pitr = m_events.lower_bound(Event(frame, QString()));
Chris@1615 164 if (pitr != m_events.end()) {
Chris@1614 165 while (pitr->getFrame() == frame) {
Chris@1615 166 if (!pitr->hasDuration()) {
Chris@1614 167 span.push_back(*pitr);
Chris@1614 168 }
Chris@1614 169 ++pitr;
Chris@1614 170 }
Chris@1614 171 }
Chris@1614 172
Chris@1614 173 // now any non-zero-duration ones from the seam map
Chris@1614 174 auto sitr = m_seams.lower_bound(frame);
Chris@1614 175 if (sitr == m_seams.end() || sitr->first > frame) {
Chris@1614 176 if (sitr != m_seams.begin()) {
Chris@1614 177 --sitr;
Chris@1614 178 }
Chris@1614 179 }
Chris@1614 180 if (sitr != m_seams.end() && sitr->first <= frame) {
Chris@1614 181 for (auto p: sitr->second) {
Chris@1614 182 span.push_back(p);
Chris@1614 183 }
Chris@1614 184 }
Chris@1614 185
Chris@1614 186 return span;
Chris@1612 187 }
Chris@1612 188
Chris@1612 189 private:
Chris@1612 190 int m_count;
Chris@1614 191
Chris@1615 192 typedef std::multiset<Event> EventMultiset;
Chris@1615 193 EventMultiset m_events;
Chris@1614 194
Chris@1615 195 typedef std::map<sv_frame_t, std::multiset<Event>> FrameEventsMap;
Chris@1615 196 FrameEventsMap m_seams;
Chris@1614 197
Chris@1614 198 /** Create a seam at the given frame, copying from the prior seam
Chris@1614 199 * if there is one. If a seam already exists at the given frame,
Chris@1614 200 * leave it untouched.
Chris@1614 201 */
Chris@1614 202 void createSeam(sv_frame_t frame) {
Chris@1614 203 auto itr = m_seams.lower_bound(frame);
Chris@1614 204 if (itr == m_seams.end() || itr->first > frame) {
Chris@1614 205 if (itr != m_seams.begin()) {
Chris@1614 206 --itr;
Chris@1614 207 }
Chris@1614 208 }
Chris@1614 209 if (itr == m_seams.end()) {
Chris@1614 210 m_seams[frame] = {};
Chris@1614 211 } else if (itr->first < frame) {
Chris@1614 212 m_seams[frame] = itr->second;
Chris@1614 213 } else if (itr->first > frame) { // itr must be begin()
Chris@1614 214 m_seams[frame] = {};
Chris@1614 215 }
Chris@1614 216 }
Chris@1614 217
Chris@1615 218 #ifdef DEBUG_EVENT_SERIES
Chris@1615 219 void dumpEvents() const {
Chris@1615 220 std::cerr << "EVENTS [" << std::endl;
Chris@1615 221 for (const auto &p: m_events) {
Chris@1614 222 std::cerr << p.toXmlString(" ");
Chris@1614 223 }
Chris@1614 224 std::cerr << "]" << std::endl;
Chris@1614 225 }
Chris@1614 226
Chris@1614 227 void dumpSeams() const {
Chris@1614 228 std::cerr << "SEAMS [" << std::endl;
Chris@1614 229 for (const auto &s: m_seams) {
Chris@1614 230 std::cerr << " " << s.first << " -> {" << std::endl;
Chris@1614 231 for (const auto &p: s.second) {
Chris@1614 232 std::cerr << p.toXmlString(" ");
Chris@1614 233 }
Chris@1614 234 std::cerr << " }" << std::endl;
Chris@1614 235 }
Chris@1614 236 std::cerr << "]" << std::endl;
Chris@1614 237 }
Chris@1614 238 #endif
Chris@1612 239 };
Chris@1612 240
Chris@1612 241 #endif