annotate base/PointSeries.h @ 1614:2e14a7876945 single-point

Fixes and tests for PointSeries
author Chris Cannam
date Thu, 07 Mar 2019 14:35:57 +0000
parents 23a29e5dc0e9
children
rev   line source
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