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