comparison base/EventSeries.cpp @ 1631:b2048f350906 single-point

Switch EventSeries to using a vector for m_events, so as to allow indexed access
author Chris Cannam
date Tue, 12 Mar 2019 14:14:00 +0000
parents
children 0890c10e5129
comparison
equal deleted inserted replaced
1630:73bda079567a 1631:b2048f350906
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 #include "EventSeries.h"
16
17 bool
18 EventSeries::isEmpty() const
19 {
20 return m_events.empty();
21 }
22
23 int
24 EventSeries::count() const
25 {
26 if (m_events.size() > INT_MAX) {
27 throw std::runtime_error("too many events");
28 }
29 return int(m_events.size());
30 }
31
32 void
33 EventSeries::add(const Event &p)
34 {
35 bool isUnique = true;
36
37 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
38 if (pitr != m_events.end() && *pitr == p) {
39 isUnique = false;
40 }
41 m_events.insert(pitr, p);
42
43 if (p.hasDuration() && isUnique) {
44
45 const sv_frame_t frame = p.getFrame();
46 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
47
48 createSeam(frame);
49 createSeam(endFrame);
50
51 // These calls must both succeed after calling createSeam above
52 const auto i0 = m_seams.find(frame);
53 const auto i1 = m_seams.find(endFrame);
54
55 for (auto i = i0; i != i1; ++i) {
56 if (i == m_seams.end()) {
57 SVCERR << "ERROR: EventSeries::add: "
58 << "reached end of seam map"
59 << endl;
60 break;
61 }
62 i->second.push_back(p);
63 }
64 }
65
66 #ifdef DEBUG_EVENT_SERIES
67 std::cerr << "after add:" << std::endl;
68 dumpEvents();
69 dumpSeams();
70 #endif
71 }
72
73 void
74 EventSeries::remove(const Event &p)
75 {
76 // If we are removing the last (unique) example of an event,
77 // then we also need to remove it from the seam map. If this
78 // is only one of multiple identical events, then we don't.
79 bool isUnique = true;
80
81 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
82 if (pitr == m_events.end() || *pitr != p) {
83 // we don't know this event
84 return;
85 } else {
86 auto nitr = pitr;
87 ++nitr;
88 if (nitr != m_events.end() && *nitr == p) {
89 isUnique = false;
90 }
91 }
92
93 m_events.erase(pitr);
94
95 if (p.hasDuration() && isUnique) {
96
97 const sv_frame_t frame = p.getFrame();
98 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
99
100 const auto i0 = m_seams.find(frame);
101 const auto i1 = m_seams.find(endFrame);
102
103 #ifdef DEBUG_EVENT_SERIES
104 // This should be impossible if we found p in m_events above
105 if (i0 == m_seams.end() || i1 == m_seams.end()) {
106 SVCERR << "ERROR: EventSeries::remove: either frame " << frame
107 << " or endFrame " << endFrame
108 << " for event not found in seam map: event is "
109 << p.toXmlString() << endl;
110 }
111 #endif
112
113 // Remove any and all instances of p from the seam map; we
114 // are only supposed to get here if we are removing the
115 // last instance of p from the series anyway
116
117 for (auto i = i0; i != i1; ++i) {
118 if (i == m_seams.end()) {
119 // This can happen only if we have a negative
120 // duration, which Event forbids
121 throw std::logic_error("unexpectedly reached end of map");
122 }
123 for (size_t j = 0; j < i->second.size(); ) {
124 if (i->second[j] == p) {
125 i->second.erase(i->second.begin() + j);
126 } else {
127 ++j;
128 }
129 }
130 }
131
132 // Tidy up by removing any entries that are now identical
133 // to their predecessors
134
135 std::vector<sv_frame_t> redundant;
136
137 auto pitr = m_seams.end();
138 if (i0 != m_seams.begin()) {
139 pitr = i0;
140 --pitr;
141 }
142
143 for (auto i = i0; i != m_seams.end(); ++i) {
144 if (pitr != m_seams.end() &&
145 seamsEqual(i->second, pitr->second)) {
146 redundant.push_back(i->first);
147 }
148 pitr = i;
149 if (i == i1) {
150 break;
151 }
152 }
153
154 for (sv_frame_t f: redundant) {
155 m_seams.erase(f);
156 }
157
158 // And remove any empty seams from the start of the map
159
160 while (m_seams.begin() != m_seams.end() &&
161 m_seams.begin()->second.empty()) {
162 m_seams.erase(m_seams.begin());
163 }
164 }
165
166 #ifdef DEBUG_EVENT_SERIES
167 std::cerr << "after remove:" << std::endl;
168 dumpEvents();
169 dumpSeams();
170 #endif
171 }
172
173 bool
174 EventSeries::contains(const Event &p) const
175 {
176 return binary_search(m_events.begin(), m_events.end(), p);
177 }
178
179 void
180 EventSeries::clear()
181 {
182 m_events.clear();
183 m_seams.clear();
184 }
185
186 EventVector
187 EventSeries::getEventsSpanning(sv_frame_t frame,
188 sv_frame_t duration) const
189 {
190 EventVector span;
191
192 const sv_frame_t start = frame;
193 const sv_frame_t end = frame + duration;
194
195 // first find any zero-duration events
196
197 auto pitr = lower_bound(m_events.begin(), m_events.end(),
198 Event(start));
199 while (pitr != m_events.end() && pitr->getFrame() < end) {
200 if (!pitr->hasDuration()) {
201 span.push_back(*pitr);
202 }
203 ++pitr;
204 }
205
206 // now any non-zero-duration ones from the seam map
207
208 std::set<Event> found;
209 auto sitr = m_seams.lower_bound(start);
210 if (sitr == m_seams.end() || sitr->first > start) {
211 if (sitr != m_seams.begin()) {
212 --sitr;
213 }
214 }
215 while (sitr != m_seams.end() && sitr->first < end) {
216 for (const auto &p: sitr->second) {
217 found.insert(p);
218 }
219 ++sitr;
220 }
221 for (const auto &p: found) {
222 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
223 while (pitr != m_events.end() && *pitr == p) {
224 span.push_back(p);
225 ++pitr;
226 }
227 }
228
229 return span;
230 }
231
232 EventVector
233 EventSeries::getEventsCovering(sv_frame_t frame) const
234 {
235 EventVector cover;
236
237 // first find any zero-duration events
238
239 auto pitr = lower_bound(m_events.begin(), m_events.end(),
240 Event(frame));
241 while (pitr != m_events.end() && pitr->getFrame() == frame) {
242 if (!pitr->hasDuration()) {
243 cover.push_back(*pitr);
244 }
245 ++pitr;
246 }
247
248 // now any non-zero-duration ones from the seam map
249
250 std::set<Event> found;
251 auto sitr = m_seams.lower_bound(frame);
252 if (sitr == m_seams.end() || sitr->first > frame) {
253 if (sitr != m_seams.begin()) {
254 --sitr;
255 }
256 }
257 if (sitr != m_seams.end() && sitr->first <= frame) {
258 for (const auto &p: sitr->second) {
259 found.insert(p);
260 }
261 ++sitr;
262 }
263 for (const auto &p: found) {
264 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
265 while (pitr != m_events.end() && *pitr == p) {
266 cover.push_back(p);
267 ++pitr;
268 }
269 }
270
271 return cover;
272 }
273
274 void
275 EventSeries::toXml(QTextStream &out,
276 QString indent,
277 QString extraAttributes) const
278 {
279 out << indent << QString("<dataset id=\"%1\" %2>\n")
280 .arg(getObjectExportId(this))
281 .arg(extraAttributes);
282
283 for (const auto &p: m_events) {
284 p.toXml(out, indent + " ");
285 }
286
287 out << indent << "</dataset>\n";
288 }
289
290