annotate base/EventSeries.cpp @ 1640:e7f557789f99 single-point

Add and test getEndFrame (and getStartFrame)
author Chris Cannam
date Wed, 13 Mar 2019 11:54:13 +0000
parents b57a75aa5ae3
children d591836e47ef
rev   line source
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@1640 43 if (!p.hasDuration() && p.getFrame() > m_finalDurationlessEventFrame) {
Chris@1640 44 m_finalDurationlessEventFrame = p.getFrame();
Chris@1640 45 }
Chris@1640 46
Chris@1631 47 if (p.hasDuration() && isUnique) {
Chris@1631 48
Chris@1631 49 const sv_frame_t frame = p.getFrame();
Chris@1631 50 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
Chris@1631 51
Chris@1631 52 createSeam(frame);
Chris@1631 53 createSeam(endFrame);
Chris@1631 54
Chris@1631 55 // These calls must both succeed after calling createSeam above
Chris@1631 56 const auto i0 = m_seams.find(frame);
Chris@1631 57 const auto i1 = m_seams.find(endFrame);
Chris@1631 58
Chris@1631 59 for (auto i = i0; i != i1; ++i) {
Chris@1631 60 if (i == m_seams.end()) {
Chris@1631 61 SVCERR << "ERROR: EventSeries::add: "
Chris@1631 62 << "reached end of seam map"
Chris@1631 63 << endl;
Chris@1631 64 break;
Chris@1631 65 }
Chris@1631 66 i->second.push_back(p);
Chris@1631 67 }
Chris@1631 68 }
Chris@1631 69
Chris@1631 70 #ifdef DEBUG_EVENT_SERIES
Chris@1631 71 std::cerr << "after add:" << std::endl;
Chris@1631 72 dumpEvents();
Chris@1631 73 dumpSeams();
Chris@1631 74 #endif
Chris@1631 75 }
Chris@1631 76
Chris@1631 77 void
Chris@1631 78 EventSeries::remove(const Event &p)
Chris@1631 79 {
Chris@1631 80 // If we are removing the last (unique) example of an event,
Chris@1631 81 // then we also need to remove it from the seam map. If this
Chris@1631 82 // is only one of multiple identical events, then we don't.
Chris@1631 83 bool isUnique = true;
Chris@1631 84
Chris@1631 85 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
Chris@1631 86 if (pitr == m_events.end() || *pitr != p) {
Chris@1631 87 // we don't know this event
Chris@1631 88 return;
Chris@1631 89 } else {
Chris@1631 90 auto nitr = pitr;
Chris@1631 91 ++nitr;
Chris@1631 92 if (nitr != m_events.end() && *nitr == p) {
Chris@1631 93 isUnique = false;
Chris@1631 94 }
Chris@1631 95 }
Chris@1631 96
Chris@1631 97 m_events.erase(pitr);
Chris@1631 98
Chris@1640 99 if (!p.hasDuration() && isUnique &&
Chris@1640 100 p.getFrame() == m_finalDurationlessEventFrame) {
Chris@1640 101 m_finalDurationlessEventFrame = 0;
Chris@1640 102 for (auto ritr = m_events.rbegin(); ritr != m_events.rend(); ++ritr) {
Chris@1640 103 if (!ritr->hasDuration()) {
Chris@1640 104 m_finalDurationlessEventFrame = ritr->getFrame();
Chris@1640 105 break;
Chris@1640 106 }
Chris@1640 107 }
Chris@1640 108 }
Chris@1640 109
Chris@1631 110 if (p.hasDuration() && isUnique) {
Chris@1631 111
Chris@1631 112 const sv_frame_t frame = p.getFrame();
Chris@1631 113 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
Chris@1631 114
Chris@1631 115 const auto i0 = m_seams.find(frame);
Chris@1631 116 const auto i1 = m_seams.find(endFrame);
Chris@1631 117
Chris@1631 118 #ifdef DEBUG_EVENT_SERIES
Chris@1631 119 // This should be impossible if we found p in m_events above
Chris@1631 120 if (i0 == m_seams.end() || i1 == m_seams.end()) {
Chris@1631 121 SVCERR << "ERROR: EventSeries::remove: either frame " << frame
Chris@1631 122 << " or endFrame " << endFrame
Chris@1631 123 << " for event not found in seam map: event is "
Chris@1631 124 << p.toXmlString() << endl;
Chris@1631 125 }
Chris@1631 126 #endif
Chris@1631 127
Chris@1631 128 // Remove any and all instances of p from the seam map; we
Chris@1631 129 // are only supposed to get here if we are removing the
Chris@1631 130 // last instance of p from the series anyway
Chris@1631 131
Chris@1631 132 for (auto i = i0; i != i1; ++i) {
Chris@1631 133 if (i == m_seams.end()) {
Chris@1631 134 // This can happen only if we have a negative
Chris@1631 135 // duration, which Event forbids
Chris@1631 136 throw std::logic_error("unexpectedly reached end of map");
Chris@1631 137 }
Chris@1631 138 for (size_t j = 0; j < i->second.size(); ) {
Chris@1631 139 if (i->second[j] == p) {
Chris@1631 140 i->second.erase(i->second.begin() + j);
Chris@1631 141 } else {
Chris@1631 142 ++j;
Chris@1631 143 }
Chris@1631 144 }
Chris@1631 145 }
Chris@1631 146
Chris@1631 147 // Tidy up by removing any entries that are now identical
Chris@1631 148 // to their predecessors
Chris@1631 149
Chris@1631 150 std::vector<sv_frame_t> redundant;
Chris@1631 151
Chris@1631 152 auto pitr = m_seams.end();
Chris@1631 153 if (i0 != m_seams.begin()) {
Chris@1631 154 pitr = i0;
Chris@1631 155 --pitr;
Chris@1631 156 }
Chris@1631 157
Chris@1631 158 for (auto i = i0; i != m_seams.end(); ++i) {
Chris@1631 159 if (pitr != m_seams.end() &&
Chris@1631 160 seamsEqual(i->second, pitr->second)) {
Chris@1631 161 redundant.push_back(i->first);
Chris@1631 162 }
Chris@1631 163 pitr = i;
Chris@1631 164 if (i == i1) {
Chris@1631 165 break;
Chris@1631 166 }
Chris@1631 167 }
Chris@1631 168
Chris@1631 169 for (sv_frame_t f: redundant) {
Chris@1631 170 m_seams.erase(f);
Chris@1631 171 }
Chris@1631 172
Chris@1631 173 // And remove any empty seams from the start of the map
Chris@1631 174
Chris@1631 175 while (m_seams.begin() != m_seams.end() &&
Chris@1631 176 m_seams.begin()->second.empty()) {
Chris@1631 177 m_seams.erase(m_seams.begin());
Chris@1631 178 }
Chris@1631 179 }
Chris@1631 180
Chris@1631 181 #ifdef DEBUG_EVENT_SERIES
Chris@1631 182 std::cerr << "after remove:" << std::endl;
Chris@1631 183 dumpEvents();
Chris@1631 184 dumpSeams();
Chris@1631 185 #endif
Chris@1631 186 }
Chris@1631 187
Chris@1631 188 bool
Chris@1631 189 EventSeries::contains(const Event &p) const
Chris@1631 190 {
Chris@1631 191 return binary_search(m_events.begin(), m_events.end(), p);
Chris@1631 192 }
Chris@1631 193
Chris@1631 194 void
Chris@1631 195 EventSeries::clear()
Chris@1631 196 {
Chris@1631 197 m_events.clear();
Chris@1631 198 m_seams.clear();
Chris@1640 199 m_finalDurationlessEventFrame = 0;
Chris@1640 200 }
Chris@1640 201
Chris@1640 202 sv_frame_t
Chris@1640 203 EventSeries::getStartFrame() const
Chris@1640 204 {
Chris@1640 205 if (m_events.empty()) return 0;
Chris@1640 206 return m_events.begin()->getFrame();
Chris@1640 207 }
Chris@1640 208
Chris@1640 209 sv_frame_t
Chris@1640 210 EventSeries::getEndFrame() const
Chris@1640 211 {
Chris@1640 212 sv_frame_t latest = 0;
Chris@1640 213
Chris@1640 214 if (m_events.empty()) return latest;
Chris@1640 215
Chris@1640 216 latest = m_finalDurationlessEventFrame;
Chris@1640 217
Chris@1640 218 if (m_seams.empty()) return latest;
Chris@1640 219
Chris@1640 220 sv_frame_t lastSeam = m_seams.rbegin()->first;
Chris@1640 221 if (lastSeam > latest) {
Chris@1640 222 latest = lastSeam;
Chris@1640 223 }
Chris@1640 224
Chris@1640 225 return latest;
Chris@1631 226 }
Chris@1631 227
Chris@1631 228 EventVector
Chris@1631 229 EventSeries::getEventsSpanning(sv_frame_t frame,
Chris@1631 230 sv_frame_t duration) const
Chris@1631 231 {
Chris@1631 232 EventVector span;
Chris@1631 233
Chris@1631 234 const sv_frame_t start = frame;
Chris@1631 235 const sv_frame_t end = frame + duration;
Chris@1631 236
Chris@1631 237 // first find any zero-duration events
Chris@1631 238
Chris@1631 239 auto pitr = lower_bound(m_events.begin(), m_events.end(),
Chris@1631 240 Event(start));
Chris@1631 241 while (pitr != m_events.end() && pitr->getFrame() < end) {
Chris@1631 242 if (!pitr->hasDuration()) {
Chris@1631 243 span.push_back(*pitr);
Chris@1631 244 }
Chris@1631 245 ++pitr;
Chris@1631 246 }
Chris@1631 247
Chris@1631 248 // now any non-zero-duration ones from the seam map
Chris@1631 249
Chris@1631 250 std::set<Event> found;
Chris@1631 251 auto sitr = m_seams.lower_bound(start);
Chris@1631 252 if (sitr == m_seams.end() || sitr->first > start) {
Chris@1631 253 if (sitr != m_seams.begin()) {
Chris@1631 254 --sitr;
Chris@1631 255 }
Chris@1631 256 }
Chris@1631 257 while (sitr != m_seams.end() && sitr->first < end) {
Chris@1631 258 for (const auto &p: sitr->second) {
Chris@1631 259 found.insert(p);
Chris@1631 260 }
Chris@1631 261 ++sitr;
Chris@1631 262 }
Chris@1631 263 for (const auto &p: found) {
Chris@1631 264 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
Chris@1631 265 while (pitr != m_events.end() && *pitr == p) {
Chris@1631 266 span.push_back(p);
Chris@1631 267 ++pitr;
Chris@1631 268 }
Chris@1631 269 }
Chris@1631 270
Chris@1631 271 return span;
Chris@1631 272 }
Chris@1631 273
Chris@1631 274 EventVector
Chris@1636 275 EventSeries::getEventsWithin(sv_frame_t frame,
Chris@1636 276 sv_frame_t duration) const
Chris@1636 277 {
Chris@1636 278 EventVector span;
Chris@1636 279
Chris@1636 280 const sv_frame_t start = frame;
Chris@1636 281 const sv_frame_t end = frame + duration;
Chris@1636 282
Chris@1636 283 // because we don't need to "look back" at events that started
Chris@1636 284 // earlier than the start of the given range, we can do this
Chris@1636 285 // entirely from m_events
Chris@1636 286
Chris@1636 287 auto pitr = lower_bound(m_events.begin(), m_events.end(),
Chris@1636 288 Event(start));
Chris@1636 289 while (pitr != m_events.end() && pitr->getFrame() < end) {
Chris@1636 290 if (!pitr->hasDuration()) {
Chris@1636 291 span.push_back(*pitr);
Chris@1636 292 } else if (pitr->getFrame() + pitr->getDuration() <= end) {
Chris@1636 293 span.push_back(*pitr);
Chris@1636 294 }
Chris@1636 295 ++pitr;
Chris@1636 296 }
Chris@1636 297
Chris@1636 298 return span;
Chris@1636 299 }
Chris@1636 300
Chris@1636 301 EventVector
Chris@1638 302 EventSeries::getEventsStartingWithin(sv_frame_t frame,
Chris@1638 303 sv_frame_t duration) const
Chris@1638 304 {
Chris@1638 305 EventVector span;
Chris@1638 306
Chris@1638 307 const sv_frame_t start = frame;
Chris@1638 308 const sv_frame_t end = frame + duration;
Chris@1638 309
Chris@1638 310 // because we don't need to "look back" at events that started
Chris@1638 311 // earlier than the start of the given range, we can do this
Chris@1638 312 // entirely from m_events
Chris@1638 313
Chris@1638 314 auto pitr = lower_bound(m_events.begin(), m_events.end(),
Chris@1638 315 Event(start));
Chris@1638 316 while (pitr != m_events.end() && pitr->getFrame() < end) {
Chris@1638 317 span.push_back(*pitr);
Chris@1638 318 ++pitr;
Chris@1638 319 }
Chris@1638 320
Chris@1638 321 return span;
Chris@1638 322 }
Chris@1638 323
Chris@1638 324 EventVector
Chris@1631 325 EventSeries::getEventsCovering(sv_frame_t frame) const
Chris@1631 326 {
Chris@1631 327 EventVector cover;
Chris@1631 328
Chris@1631 329 // first find any zero-duration events
Chris@1631 330
Chris@1631 331 auto pitr = lower_bound(m_events.begin(), m_events.end(),
Chris@1631 332 Event(frame));
Chris@1631 333 while (pitr != m_events.end() && pitr->getFrame() == frame) {
Chris@1631 334 if (!pitr->hasDuration()) {
Chris@1631 335 cover.push_back(*pitr);
Chris@1631 336 }
Chris@1631 337 ++pitr;
Chris@1631 338 }
Chris@1631 339
Chris@1631 340 // now any non-zero-duration ones from the seam map
Chris@1631 341
Chris@1631 342 std::set<Event> found;
Chris@1631 343 auto sitr = m_seams.lower_bound(frame);
Chris@1631 344 if (sitr == m_seams.end() || sitr->first > frame) {
Chris@1631 345 if (sitr != m_seams.begin()) {
Chris@1631 346 --sitr;
Chris@1631 347 }
Chris@1631 348 }
Chris@1631 349 if (sitr != m_seams.end() && sitr->first <= frame) {
Chris@1631 350 for (const auto &p: sitr->second) {
Chris@1631 351 found.insert(p);
Chris@1631 352 }
Chris@1631 353 ++sitr;
Chris@1631 354 }
Chris@1631 355 for (const auto &p: found) {
Chris@1631 356 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
Chris@1631 357 while (pitr != m_events.end() && *pitr == p) {
Chris@1631 358 cover.push_back(p);
Chris@1631 359 ++pitr;
Chris@1631 360 }
Chris@1631 361 }
Chris@1631 362
Chris@1631 363 return cover;
Chris@1631 364 }
Chris@1631 365
Chris@1632 366 bool
Chris@1632 367 EventSeries::getEventPreceding(const Event &e, Event &preceding) const
Chris@1632 368 {
Chris@1632 369 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
Chris@1632 370 if (pitr == m_events.end() || *pitr != e) {
Chris@1632 371 return false;
Chris@1632 372 }
Chris@1632 373 if (pitr == m_events.begin()) {
Chris@1632 374 return false;
Chris@1632 375 }
Chris@1632 376 --pitr;
Chris@1632 377 preceding = *pitr;
Chris@1632 378 return true;
Chris@1632 379 }
Chris@1632 380
Chris@1632 381 bool
Chris@1632 382 EventSeries::getEventFollowing(const Event &e, Event &following) const
Chris@1632 383 {
Chris@1632 384 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
Chris@1632 385 if (pitr == m_events.end() || *pitr != e) {
Chris@1632 386 return false;
Chris@1632 387 }
Chris@1633 388 while (*pitr == e) {
Chris@1633 389 ++pitr;
Chris@1633 390 if (pitr == m_events.end()) {
Chris@1633 391 return false;
Chris@1633 392 }
Chris@1632 393 }
Chris@1632 394 following = *pitr;
Chris@1632 395 return true;
Chris@1632 396 }
Chris@1632 397
Chris@1632 398 Event
Chris@1632 399 EventSeries::getEventByIndex(int index) const
Chris@1632 400 {
Chris@1632 401 if (index < 0 || index >= count()) {
Chris@1632 402 throw std::logic_error("index out of range");
Chris@1632 403 }
Chris@1632 404 return m_events[index];
Chris@1632 405 }
Chris@1632 406
Chris@1640 407 int
Chris@1640 408 EventSeries::getIndexForEvent(const Event &e) const
Chris@1640 409 {
Chris@1640 410 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
Chris@1640 411 return distance(m_events.begin(), pitr);
Chris@1640 412 }
Chris@1640 413
Chris@1631 414 void
Chris@1631 415 EventSeries::toXml(QTextStream &out,
Chris@1631 416 QString indent,
Chris@1631 417 QString extraAttributes) const
Chris@1631 418 {
Chris@1631 419 out << indent << QString("<dataset id=\"%1\" %2>\n")
Chris@1631 420 .arg(getObjectExportId(this))
Chris@1631 421 .arg(extraAttributes);
Chris@1631 422
Chris@1631 423 for (const auto &p: m_events) {
Chris@1631 424 p.toXml(out, indent + " ");
Chris@1631 425 }
Chris@1631 426
Chris@1631 427 out << indent << "</dataset>\n";
Chris@1631 428 }
Chris@1631 429
Chris@1631 430