Mercurial > hg > svcore
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 |