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@1612
|
15 #ifndef SV_POINT_SERIES_H
|
Chris@1612
|
16 #define SV_POINT_SERIES_H
|
Chris@1612
|
17
|
Chris@1612
|
18 #include "Point.h"
|
Chris@1612
|
19
|
Chris@1612
|
20 #include <set>
|
Chris@1612
|
21
|
Chris@1614
|
22 //#define DEBUG_POINT_SERIES 1
|
Chris@1614
|
23
|
Chris@1612
|
24 class PointSeries
|
Chris@1612
|
25 {
|
Chris@1612
|
26 public:
|
Chris@1612
|
27 PointSeries() : m_count(0) { }
|
Chris@1612
|
28
|
Chris@1612
|
29 void add(const Point &p) {
|
Chris@1612
|
30
|
Chris@1612
|
31 m_points.insert(p);
|
Chris@1612
|
32 ++m_count;
|
Chris@1612
|
33
|
Chris@1612
|
34 if (p.haveDuration()) {
|
Chris@1612
|
35 sv_frame_t frame = p.getFrame();
|
Chris@1612
|
36 sv_frame_t endFrame = p.getFrame() + p.getDuration();
|
Chris@1612
|
37
|
Chris@1614
|
38 createSeam(frame);
|
Chris@1614
|
39 createSeam(endFrame);
|
Chris@1614
|
40
|
Chris@1614
|
41 auto i0 = m_seams.find(frame); // must succeed after createSeam
|
Chris@1614
|
42 auto i1 = m_seams.find(endFrame); // likewise
|
Chris@1614
|
43
|
Chris@1614
|
44 for (auto i = i0; i != i1; ++i) {
|
Chris@1614
|
45 if (i == m_seams.end()) {
|
Chris@1614
|
46 SVCERR << "ERROR: PointSeries::add: "
|
Chris@1614
|
47 << "reached end of seam map"
|
Chris@1614
|
48 << endl;
|
Chris@1614
|
49 break;
|
Chris@1612
|
50 }
|
Chris@1614
|
51 i->second.insert(p);
|
Chris@1612
|
52 }
|
Chris@1614
|
53 }
|
Chris@1612
|
54
|
Chris@1614
|
55 #ifdef DEBUG_POINT_SERIES
|
Chris@1614
|
56 std::cerr << "after add:" << std::endl;
|
Chris@1614
|
57 dumpPoints();
|
Chris@1614
|
58 dumpSeams();
|
Chris@1614
|
59 #endif
|
Chris@1612
|
60 }
|
Chris@1612
|
61
|
Chris@1612
|
62 void remove(const Point &p) {
|
Chris@1612
|
63
|
Chris@1612
|
64 // erase first itr that matches p; if there is more than one
|
Chris@1612
|
65 // p, erase(p) would remove all of them, but we only want to
|
Chris@1612
|
66 // remove (any) one
|
Chris@1612
|
67 auto pitr = m_points.find(p);
|
Chris@1612
|
68 if (pitr == m_points.end()) {
|
Chris@1612
|
69 return; // we don't know this point
|
Chris@1612
|
70 } else {
|
Chris@1612
|
71 m_points.erase(pitr);
|
Chris@1612
|
72 --m_count;
|
Chris@1612
|
73 }
|
Chris@1612
|
74
|
Chris@1612
|
75 if (p.haveDuration()) {
|
Chris@1612
|
76 sv_frame_t frame = p.getFrame();
|
Chris@1612
|
77 sv_frame_t endFrame = p.getFrame() + p.getDuration();
|
Chris@1612
|
78
|
Chris@1614
|
79 auto i0 = m_seams.find(frame);
|
Chris@1614
|
80 auto i1 = m_seams.find(endFrame);
|
Chris@1614
|
81
|
Chris@1614
|
82 #ifdef DEBUG_POINT_SERIES
|
Chris@1614
|
83 // This should be impossible if we found p in m_points above
|
Chris@1614
|
84 if (i0 == m_seams.end() || i1 == m_seams.end()) {
|
Chris@1614
|
85 SVCERR << "ERROR: PointSeries::remove: either frame " << frame
|
Chris@1614
|
86 << " or endFrame " << endFrame
|
Chris@1612
|
87 << " for point not found in seam map: point is "
|
Chris@1612
|
88 << p.toXmlString() << endl;
|
Chris@1612
|
89 }
|
Chris@1614
|
90 #endif
|
Chris@1612
|
91
|
Chris@1614
|
92 for (auto i = i0; i != i1; ++i) {
|
Chris@1614
|
93 if (i == m_seams.end()) {
|
Chris@1614
|
94 SVCERR << "ERROR: PointSeries::remove: "
|
Chris@1614
|
95 << "reached end of seam map"
|
Chris@1614
|
96 << endl;
|
Chris@1614
|
97 break;
|
Chris@1614
|
98 }
|
Chris@1614
|
99 // Can't just erase(p) as that would erase all of
|
Chris@1614
|
100 // them, if there are several identical ones
|
Chris@1614
|
101 auto si = i->second.find(p);
|
Chris@1614
|
102 if (si != i->second.end()) {
|
Chris@1614
|
103 i->second.erase(si);
|
Chris@1614
|
104 }
|
Chris@1612
|
105 }
|
Chris@1612
|
106
|
Chris@1612
|
107 // Shall we "garbage-collect" here? We could be leaving
|
Chris@1612
|
108 // lots of empty point-sets, or consecutive identical
|
Chris@1612
|
109 // ones, which are a pure irrelevance that take space and
|
Chris@1612
|
110 // slow us down. But a lot depends on whether callers ever
|
Chris@1612
|
111 // really delete anything much.
|
Chris@1612
|
112 }
|
Chris@1614
|
113
|
Chris@1614
|
114 #ifdef DEBUG_POINT_SERIES
|
Chris@1614
|
115 std::cerr << "after remove:" << std::endl;
|
Chris@1614
|
116 dumpPoints();
|
Chris@1614
|
117 dumpSeams();
|
Chris@1614
|
118 #endif
|
Chris@1612
|
119 }
|
Chris@1612
|
120
|
Chris@1612
|
121 bool contains(const Point &p) {
|
Chris@1612
|
122 return m_points.find(p) != m_points.end();
|
Chris@1612
|
123 }
|
Chris@1612
|
124
|
Chris@1612
|
125 int count() const {
|
Chris@1612
|
126 return m_count;
|
Chris@1612
|
127 }
|
Chris@1612
|
128
|
Chris@1612
|
129 bool isEmpty() const {
|
Chris@1612
|
130 return m_count == 0;
|
Chris@1612
|
131 }
|
Chris@1612
|
132
|
Chris@1612
|
133 void clear() {
|
Chris@1612
|
134 m_points.clear();
|
Chris@1612
|
135 m_seams.clear();
|
Chris@1612
|
136 m_count = 0;
|
Chris@1612
|
137 }
|
Chris@1612
|
138
|
Chris@1612
|
139 /**
|
Chris@1612
|
140 * Retrieve all points that span the given frame. A point without
|
Chris@1612
|
141 * duration spans a frame if its own frame is equal to it. A point
|
Chris@1612
|
142 * with duration spans a frame if its start frame is less than or
|
Chris@1612
|
143 * equal to it and its end frame (start + duration) is greater
|
Chris@1612
|
144 * than it.
|
Chris@1612
|
145 */
|
Chris@1614
|
146 PointVector getPointsSpanning(sv_frame_t frame) const {
|
Chris@1614
|
147 PointVector span;
|
Chris@1614
|
148
|
Chris@1614
|
149 // first find any zero-duration points
|
Chris@1614
|
150 auto pitr = m_points.lower_bound(Point(frame, QString()));
|
Chris@1614
|
151 if (pitr != m_points.end()) {
|
Chris@1614
|
152 while (pitr->getFrame() == frame) {
|
Chris@1614
|
153 if (!pitr->haveDuration()) {
|
Chris@1614
|
154 span.push_back(*pitr);
|
Chris@1614
|
155 }
|
Chris@1614
|
156 ++pitr;
|
Chris@1614
|
157 }
|
Chris@1614
|
158 }
|
Chris@1614
|
159
|
Chris@1614
|
160 // now any non-zero-duration ones from the seam map
|
Chris@1614
|
161 auto sitr = m_seams.lower_bound(frame);
|
Chris@1614
|
162 if (sitr == m_seams.end() || sitr->first > frame) {
|
Chris@1614
|
163 if (sitr != m_seams.begin()) {
|
Chris@1614
|
164 --sitr;
|
Chris@1614
|
165 }
|
Chris@1614
|
166 }
|
Chris@1614
|
167 if (sitr != m_seams.end() && sitr->first <= frame) {
|
Chris@1614
|
168 for (auto p: sitr->second) {
|
Chris@1614
|
169 span.push_back(p);
|
Chris@1614
|
170 }
|
Chris@1614
|
171 }
|
Chris@1614
|
172
|
Chris@1614
|
173 return span;
|
Chris@1612
|
174 }
|
Chris@1612
|
175
|
Chris@1612
|
176 private:
|
Chris@1612
|
177 int m_count;
|
Chris@1614
|
178
|
Chris@1614
|
179 typedef std::multiset<Point> PointMultiset;
|
Chris@1614
|
180 PointMultiset m_points;
|
Chris@1614
|
181
|
Chris@1614
|
182 typedef std::map<sv_frame_t, std::multiset<Point>> FramePointsMap;
|
Chris@1614
|
183 FramePointsMap m_seams;
|
Chris@1614
|
184
|
Chris@1614
|
185 /** Create a seam at the given frame, copying from the prior seam
|
Chris@1614
|
186 * if there is one. If a seam already exists at the given frame,
|
Chris@1614
|
187 * leave it untouched.
|
Chris@1614
|
188 */
|
Chris@1614
|
189 void createSeam(sv_frame_t frame) {
|
Chris@1614
|
190 auto itr = m_seams.lower_bound(frame);
|
Chris@1614
|
191 if (itr == m_seams.end() || itr->first > frame) {
|
Chris@1614
|
192 if (itr != m_seams.begin()) {
|
Chris@1614
|
193 --itr;
|
Chris@1614
|
194 }
|
Chris@1614
|
195 }
|
Chris@1614
|
196 if (itr == m_seams.end()) {
|
Chris@1614
|
197 m_seams[frame] = {};
|
Chris@1614
|
198 } else if (itr->first < frame) {
|
Chris@1614
|
199 m_seams[frame] = itr->second;
|
Chris@1614
|
200 } else if (itr->first > frame) { // itr must be begin()
|
Chris@1614
|
201 m_seams[frame] = {};
|
Chris@1614
|
202 }
|
Chris@1614
|
203 }
|
Chris@1614
|
204
|
Chris@1614
|
205 #ifdef DEBUG_POINT_SERIES
|
Chris@1614
|
206 void dumpPoints() const {
|
Chris@1614
|
207 std::cerr << "POINTS [" << std::endl;
|
Chris@1614
|
208 for (const auto &p: m_points) {
|
Chris@1614
|
209 std::cerr << p.toXmlString(" ");
|
Chris@1614
|
210 }
|
Chris@1614
|
211 std::cerr << "]" << std::endl;
|
Chris@1614
|
212 }
|
Chris@1614
|
213
|
Chris@1614
|
214 void dumpSeams() const {
|
Chris@1614
|
215 std::cerr << "SEAMS [" << std::endl;
|
Chris@1614
|
216 for (const auto &s: m_seams) {
|
Chris@1614
|
217 std::cerr << " " << s.first << " -> {" << std::endl;
|
Chris@1614
|
218 for (const auto &p: s.second) {
|
Chris@1614
|
219 std::cerr << p.toXmlString(" ");
|
Chris@1614
|
220 }
|
Chris@1614
|
221 std::cerr << " }" << std::endl;
|
Chris@1614
|
222 }
|
Chris@1614
|
223 std::cerr << "]" << std::endl;
|
Chris@1614
|
224 }
|
Chris@1614
|
225 #endif
|
Chris@1612
|
226 };
|
Chris@1612
|
227
|
Chris@1612
|
228 #endif
|