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
|