Chris@1631
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@1631
|
2
|
Chris@1631
|
3 /*
|
Chris@1631
|
4 Sonic Visualiser
|
Chris@1631
|
5 An audio file viewer and annotation editor.
|
Chris@1631
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@1631
|
7
|
Chris@1631
|
8 This program is free software; you can redistribute it and/or
|
Chris@1631
|
9 modify it under the terms of the GNU General Public License as
|
Chris@1631
|
10 published by the Free Software Foundation; either version 2 of the
|
Chris@1631
|
11 License, or (at your option) any later version. See the file
|
Chris@1631
|
12 COPYING included with this distribution for more information.
|
Chris@1631
|
13 */
|
Chris@1631
|
14
|
Chris@1631
|
15 #include "EventSeries.h"
|
Chris@1631
|
16
|
Chris@1631
|
17 bool
|
Chris@1631
|
18 EventSeries::isEmpty() const
|
Chris@1631
|
19 {
|
Chris@1631
|
20 return m_events.empty();
|
Chris@1631
|
21 }
|
Chris@1631
|
22
|
Chris@1631
|
23 int
|
Chris@1631
|
24 EventSeries::count() const
|
Chris@1631
|
25 {
|
Chris@1631
|
26 if (m_events.size() > INT_MAX) {
|
Chris@1632
|
27 throw std::logic_error("too many events");
|
Chris@1631
|
28 }
|
Chris@1631
|
29 return int(m_events.size());
|
Chris@1631
|
30 }
|
Chris@1631
|
31
|
Chris@1631
|
32 void
|
Chris@1631
|
33 EventSeries::add(const Event &p)
|
Chris@1631
|
34 {
|
Chris@1631
|
35 bool isUnique = true;
|
Chris@1631
|
36
|
Chris@1631
|
37 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
38 if (pitr != m_events.end() && *pitr == p) {
|
Chris@1631
|
39 isUnique = false;
|
Chris@1631
|
40 }
|
Chris@1631
|
41 m_events.insert(pitr, p);
|
Chris@1631
|
42
|
Chris@1631
|
43 if (p.hasDuration() && isUnique) {
|
Chris@1631
|
44
|
Chris@1631
|
45 const sv_frame_t frame = p.getFrame();
|
Chris@1631
|
46 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
|
Chris@1631
|
47
|
Chris@1631
|
48 createSeam(frame);
|
Chris@1631
|
49 createSeam(endFrame);
|
Chris@1631
|
50
|
Chris@1631
|
51 // These calls must both succeed after calling createSeam above
|
Chris@1631
|
52 const auto i0 = m_seams.find(frame);
|
Chris@1631
|
53 const auto i1 = m_seams.find(endFrame);
|
Chris@1631
|
54
|
Chris@1631
|
55 for (auto i = i0; i != i1; ++i) {
|
Chris@1631
|
56 if (i == m_seams.end()) {
|
Chris@1631
|
57 SVCERR << "ERROR: EventSeries::add: "
|
Chris@1631
|
58 << "reached end of seam map"
|
Chris@1631
|
59 << endl;
|
Chris@1631
|
60 break;
|
Chris@1631
|
61 }
|
Chris@1631
|
62 i->second.push_back(p);
|
Chris@1631
|
63 }
|
Chris@1631
|
64 }
|
Chris@1631
|
65
|
Chris@1631
|
66 #ifdef DEBUG_EVENT_SERIES
|
Chris@1631
|
67 std::cerr << "after add:" << std::endl;
|
Chris@1631
|
68 dumpEvents();
|
Chris@1631
|
69 dumpSeams();
|
Chris@1631
|
70 #endif
|
Chris@1631
|
71 }
|
Chris@1631
|
72
|
Chris@1631
|
73 void
|
Chris@1631
|
74 EventSeries::remove(const Event &p)
|
Chris@1631
|
75 {
|
Chris@1631
|
76 // If we are removing the last (unique) example of an event,
|
Chris@1631
|
77 // then we also need to remove it from the seam map. If this
|
Chris@1631
|
78 // is only one of multiple identical events, then we don't.
|
Chris@1631
|
79 bool isUnique = true;
|
Chris@1631
|
80
|
Chris@1631
|
81 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
82 if (pitr == m_events.end() || *pitr != p) {
|
Chris@1631
|
83 // we don't know this event
|
Chris@1631
|
84 return;
|
Chris@1631
|
85 } else {
|
Chris@1631
|
86 auto nitr = pitr;
|
Chris@1631
|
87 ++nitr;
|
Chris@1631
|
88 if (nitr != m_events.end() && *nitr == p) {
|
Chris@1631
|
89 isUnique = false;
|
Chris@1631
|
90 }
|
Chris@1631
|
91 }
|
Chris@1631
|
92
|
Chris@1631
|
93 m_events.erase(pitr);
|
Chris@1631
|
94
|
Chris@1631
|
95 if (p.hasDuration() && isUnique) {
|
Chris@1631
|
96
|
Chris@1631
|
97 const sv_frame_t frame = p.getFrame();
|
Chris@1631
|
98 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
|
Chris@1631
|
99
|
Chris@1631
|
100 const auto i0 = m_seams.find(frame);
|
Chris@1631
|
101 const auto i1 = m_seams.find(endFrame);
|
Chris@1631
|
102
|
Chris@1631
|
103 #ifdef DEBUG_EVENT_SERIES
|
Chris@1631
|
104 // This should be impossible if we found p in m_events above
|
Chris@1631
|
105 if (i0 == m_seams.end() || i1 == m_seams.end()) {
|
Chris@1631
|
106 SVCERR << "ERROR: EventSeries::remove: either frame " << frame
|
Chris@1631
|
107 << " or endFrame " << endFrame
|
Chris@1631
|
108 << " for event not found in seam map: event is "
|
Chris@1631
|
109 << p.toXmlString() << endl;
|
Chris@1631
|
110 }
|
Chris@1631
|
111 #endif
|
Chris@1631
|
112
|
Chris@1631
|
113 // Remove any and all instances of p from the seam map; we
|
Chris@1631
|
114 // are only supposed to get here if we are removing the
|
Chris@1631
|
115 // last instance of p from the series anyway
|
Chris@1631
|
116
|
Chris@1631
|
117 for (auto i = i0; i != i1; ++i) {
|
Chris@1631
|
118 if (i == m_seams.end()) {
|
Chris@1631
|
119 // This can happen only if we have a negative
|
Chris@1631
|
120 // duration, which Event forbids
|
Chris@1631
|
121 throw std::logic_error("unexpectedly reached end of map");
|
Chris@1631
|
122 }
|
Chris@1631
|
123 for (size_t j = 0; j < i->second.size(); ) {
|
Chris@1631
|
124 if (i->second[j] == p) {
|
Chris@1631
|
125 i->second.erase(i->second.begin() + j);
|
Chris@1631
|
126 } else {
|
Chris@1631
|
127 ++j;
|
Chris@1631
|
128 }
|
Chris@1631
|
129 }
|
Chris@1631
|
130 }
|
Chris@1631
|
131
|
Chris@1631
|
132 // Tidy up by removing any entries that are now identical
|
Chris@1631
|
133 // to their predecessors
|
Chris@1631
|
134
|
Chris@1631
|
135 std::vector<sv_frame_t> redundant;
|
Chris@1631
|
136
|
Chris@1631
|
137 auto pitr = m_seams.end();
|
Chris@1631
|
138 if (i0 != m_seams.begin()) {
|
Chris@1631
|
139 pitr = i0;
|
Chris@1631
|
140 --pitr;
|
Chris@1631
|
141 }
|
Chris@1631
|
142
|
Chris@1631
|
143 for (auto i = i0; i != m_seams.end(); ++i) {
|
Chris@1631
|
144 if (pitr != m_seams.end() &&
|
Chris@1631
|
145 seamsEqual(i->second, pitr->second)) {
|
Chris@1631
|
146 redundant.push_back(i->first);
|
Chris@1631
|
147 }
|
Chris@1631
|
148 pitr = i;
|
Chris@1631
|
149 if (i == i1) {
|
Chris@1631
|
150 break;
|
Chris@1631
|
151 }
|
Chris@1631
|
152 }
|
Chris@1631
|
153
|
Chris@1631
|
154 for (sv_frame_t f: redundant) {
|
Chris@1631
|
155 m_seams.erase(f);
|
Chris@1631
|
156 }
|
Chris@1631
|
157
|
Chris@1631
|
158 // And remove any empty seams from the start of the map
|
Chris@1631
|
159
|
Chris@1631
|
160 while (m_seams.begin() != m_seams.end() &&
|
Chris@1631
|
161 m_seams.begin()->second.empty()) {
|
Chris@1631
|
162 m_seams.erase(m_seams.begin());
|
Chris@1631
|
163 }
|
Chris@1631
|
164 }
|
Chris@1631
|
165
|
Chris@1631
|
166 #ifdef DEBUG_EVENT_SERIES
|
Chris@1631
|
167 std::cerr << "after remove:" << std::endl;
|
Chris@1631
|
168 dumpEvents();
|
Chris@1631
|
169 dumpSeams();
|
Chris@1631
|
170 #endif
|
Chris@1631
|
171 }
|
Chris@1631
|
172
|
Chris@1631
|
173 bool
|
Chris@1631
|
174 EventSeries::contains(const Event &p) const
|
Chris@1631
|
175 {
|
Chris@1631
|
176 return binary_search(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
177 }
|
Chris@1631
|
178
|
Chris@1631
|
179 void
|
Chris@1631
|
180 EventSeries::clear()
|
Chris@1631
|
181 {
|
Chris@1631
|
182 m_events.clear();
|
Chris@1631
|
183 m_seams.clear();
|
Chris@1631
|
184 }
|
Chris@1631
|
185
|
Chris@1631
|
186 EventVector
|
Chris@1631
|
187 EventSeries::getEventsSpanning(sv_frame_t frame,
|
Chris@1631
|
188 sv_frame_t duration) const
|
Chris@1631
|
189 {
|
Chris@1631
|
190 EventVector span;
|
Chris@1631
|
191
|
Chris@1631
|
192 const sv_frame_t start = frame;
|
Chris@1631
|
193 const sv_frame_t end = frame + duration;
|
Chris@1631
|
194
|
Chris@1631
|
195 // first find any zero-duration events
|
Chris@1631
|
196
|
Chris@1631
|
197 auto pitr = lower_bound(m_events.begin(), m_events.end(),
|
Chris@1631
|
198 Event(start));
|
Chris@1631
|
199 while (pitr != m_events.end() && pitr->getFrame() < end) {
|
Chris@1631
|
200 if (!pitr->hasDuration()) {
|
Chris@1631
|
201 span.push_back(*pitr);
|
Chris@1631
|
202 }
|
Chris@1631
|
203 ++pitr;
|
Chris@1631
|
204 }
|
Chris@1631
|
205
|
Chris@1631
|
206 // now any non-zero-duration ones from the seam map
|
Chris@1631
|
207
|
Chris@1631
|
208 std::set<Event> found;
|
Chris@1631
|
209 auto sitr = m_seams.lower_bound(start);
|
Chris@1631
|
210 if (sitr == m_seams.end() || sitr->first > start) {
|
Chris@1631
|
211 if (sitr != m_seams.begin()) {
|
Chris@1631
|
212 --sitr;
|
Chris@1631
|
213 }
|
Chris@1631
|
214 }
|
Chris@1631
|
215 while (sitr != m_seams.end() && sitr->first < end) {
|
Chris@1631
|
216 for (const auto &p: sitr->second) {
|
Chris@1631
|
217 found.insert(p);
|
Chris@1631
|
218 }
|
Chris@1631
|
219 ++sitr;
|
Chris@1631
|
220 }
|
Chris@1631
|
221 for (const auto &p: found) {
|
Chris@1631
|
222 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
223 while (pitr != m_events.end() && *pitr == p) {
|
Chris@1631
|
224 span.push_back(p);
|
Chris@1631
|
225 ++pitr;
|
Chris@1631
|
226 }
|
Chris@1631
|
227 }
|
Chris@1631
|
228
|
Chris@1631
|
229 return span;
|
Chris@1631
|
230 }
|
Chris@1631
|
231
|
Chris@1631
|
232 EventVector
|
Chris@1636
|
233 EventSeries::getEventsWithin(sv_frame_t frame,
|
Chris@1636
|
234 sv_frame_t duration) const
|
Chris@1636
|
235 {
|
Chris@1636
|
236 EventVector span;
|
Chris@1636
|
237
|
Chris@1636
|
238 const sv_frame_t start = frame;
|
Chris@1636
|
239 const sv_frame_t end = frame + duration;
|
Chris@1636
|
240
|
Chris@1636
|
241 // because we don't need to "look back" at events that started
|
Chris@1636
|
242 // earlier than the start of the given range, we can do this
|
Chris@1636
|
243 // entirely from m_events
|
Chris@1636
|
244
|
Chris@1636
|
245 auto pitr = lower_bound(m_events.begin(), m_events.end(),
|
Chris@1636
|
246 Event(start));
|
Chris@1636
|
247 while (pitr != m_events.end() && pitr->getFrame() < end) {
|
Chris@1636
|
248 if (!pitr->hasDuration()) {
|
Chris@1636
|
249 span.push_back(*pitr);
|
Chris@1636
|
250 } else if (pitr->getFrame() + pitr->getDuration() <= end) {
|
Chris@1636
|
251 span.push_back(*pitr);
|
Chris@1636
|
252 }
|
Chris@1636
|
253 ++pitr;
|
Chris@1636
|
254 }
|
Chris@1636
|
255
|
Chris@1636
|
256 return span;
|
Chris@1636
|
257 }
|
Chris@1636
|
258
|
Chris@1636
|
259 EventVector
|
Chris@1638
|
260 EventSeries::getEventsStartingWithin(sv_frame_t frame,
|
Chris@1638
|
261 sv_frame_t duration) const
|
Chris@1638
|
262 {
|
Chris@1638
|
263 EventVector span;
|
Chris@1638
|
264
|
Chris@1638
|
265 const sv_frame_t start = frame;
|
Chris@1638
|
266 const sv_frame_t end = frame + duration;
|
Chris@1638
|
267
|
Chris@1638
|
268 // because we don't need to "look back" at events that started
|
Chris@1638
|
269 // earlier than the start of the given range, we can do this
|
Chris@1638
|
270 // entirely from m_events
|
Chris@1638
|
271
|
Chris@1638
|
272 auto pitr = lower_bound(m_events.begin(), m_events.end(),
|
Chris@1638
|
273 Event(start));
|
Chris@1638
|
274 while (pitr != m_events.end() && pitr->getFrame() < end) {
|
Chris@1638
|
275 span.push_back(*pitr);
|
Chris@1638
|
276 ++pitr;
|
Chris@1638
|
277 }
|
Chris@1638
|
278
|
Chris@1638
|
279 return span;
|
Chris@1638
|
280 }
|
Chris@1638
|
281
|
Chris@1638
|
282 EventVector
|
Chris@1631
|
283 EventSeries::getEventsCovering(sv_frame_t frame) const
|
Chris@1631
|
284 {
|
Chris@1631
|
285 EventVector cover;
|
Chris@1631
|
286
|
Chris@1631
|
287 // first find any zero-duration events
|
Chris@1631
|
288
|
Chris@1631
|
289 auto pitr = lower_bound(m_events.begin(), m_events.end(),
|
Chris@1631
|
290 Event(frame));
|
Chris@1631
|
291 while (pitr != m_events.end() && pitr->getFrame() == frame) {
|
Chris@1631
|
292 if (!pitr->hasDuration()) {
|
Chris@1631
|
293 cover.push_back(*pitr);
|
Chris@1631
|
294 }
|
Chris@1631
|
295 ++pitr;
|
Chris@1631
|
296 }
|
Chris@1631
|
297
|
Chris@1631
|
298 // now any non-zero-duration ones from the seam map
|
Chris@1631
|
299
|
Chris@1631
|
300 std::set<Event> found;
|
Chris@1631
|
301 auto sitr = m_seams.lower_bound(frame);
|
Chris@1631
|
302 if (sitr == m_seams.end() || sitr->first > frame) {
|
Chris@1631
|
303 if (sitr != m_seams.begin()) {
|
Chris@1631
|
304 --sitr;
|
Chris@1631
|
305 }
|
Chris@1631
|
306 }
|
Chris@1631
|
307 if (sitr != m_seams.end() && sitr->first <= frame) {
|
Chris@1631
|
308 for (const auto &p: sitr->second) {
|
Chris@1631
|
309 found.insert(p);
|
Chris@1631
|
310 }
|
Chris@1631
|
311 ++sitr;
|
Chris@1631
|
312 }
|
Chris@1631
|
313 for (const auto &p: found) {
|
Chris@1631
|
314 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
315 while (pitr != m_events.end() && *pitr == p) {
|
Chris@1631
|
316 cover.push_back(p);
|
Chris@1631
|
317 ++pitr;
|
Chris@1631
|
318 }
|
Chris@1631
|
319 }
|
Chris@1631
|
320
|
Chris@1631
|
321 return cover;
|
Chris@1631
|
322 }
|
Chris@1631
|
323
|
Chris@1632
|
324 bool
|
Chris@1632
|
325 EventSeries::getEventPreceding(const Event &e, Event &preceding) const
|
Chris@1632
|
326 {
|
Chris@1632
|
327 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
|
Chris@1632
|
328 if (pitr == m_events.end() || *pitr != e) {
|
Chris@1632
|
329 return false;
|
Chris@1632
|
330 }
|
Chris@1632
|
331 if (pitr == m_events.begin()) {
|
Chris@1632
|
332 return false;
|
Chris@1632
|
333 }
|
Chris@1632
|
334 --pitr;
|
Chris@1632
|
335 preceding = *pitr;
|
Chris@1632
|
336 return true;
|
Chris@1632
|
337 }
|
Chris@1632
|
338
|
Chris@1632
|
339 bool
|
Chris@1632
|
340 EventSeries::getEventFollowing(const Event &e, Event &following) const
|
Chris@1632
|
341 {
|
Chris@1632
|
342 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
|
Chris@1632
|
343 if (pitr == m_events.end() || *pitr != e) {
|
Chris@1632
|
344 return false;
|
Chris@1632
|
345 }
|
Chris@1633
|
346 while (*pitr == e) {
|
Chris@1633
|
347 ++pitr;
|
Chris@1633
|
348 if (pitr == m_events.end()) {
|
Chris@1633
|
349 return false;
|
Chris@1633
|
350 }
|
Chris@1632
|
351 }
|
Chris@1632
|
352 following = *pitr;
|
Chris@1632
|
353 return true;
|
Chris@1632
|
354 }
|
Chris@1632
|
355
|
Chris@1632
|
356 Event
|
Chris@1632
|
357 EventSeries::getEventByIndex(int index) const
|
Chris@1632
|
358 {
|
Chris@1632
|
359 if (index < 0 || index >= count()) {
|
Chris@1632
|
360 throw std::logic_error("index out of range");
|
Chris@1632
|
361 }
|
Chris@1632
|
362 return m_events[index];
|
Chris@1632
|
363 }
|
Chris@1632
|
364
|
Chris@1631
|
365 void
|
Chris@1631
|
366 EventSeries::toXml(QTextStream &out,
|
Chris@1631
|
367 QString indent,
|
Chris@1631
|
368 QString extraAttributes) const
|
Chris@1631
|
369 {
|
Chris@1631
|
370 out << indent << QString("<dataset id=\"%1\" %2>\n")
|
Chris@1631
|
371 .arg(getObjectExportId(this))
|
Chris@1631
|
372 .arg(extraAttributes);
|
Chris@1631
|
373
|
Chris@1631
|
374 for (const auto &p: m_events) {
|
Chris@1631
|
375 p.toXml(out, indent + " ");
|
Chris@1631
|
376 }
|
Chris@1631
|
377
|
Chris@1631
|
378 out << indent << "</dataset>\n";
|
Chris@1631
|
379 }
|
Chris@1631
|
380
|
Chris@1631
|
381
|