annotate base/EventSeries.cpp @ 1671:82d03c9661f9 single-point

Rework isReady()/getCompletion() on models. Previously the new overhauled models were implementing getCompletion() but inheriting a version of isReady() (from the Model base) that didn't call it, referring only to isOK(). So they were reporting completion as soon as they had begun. Instead hoist getCompletion() to abstract base and call it from Model::isReady().
author Chris Cannam
date Wed, 27 Mar 2019 13:15:16 +0000
parents 26aa42fd60e9
children 69ab62d378bf
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@1654 276 sv_frame_t duration,
Chris@1654 277 int overspill) const
Chris@1636 278 {
Chris@1636 279 EventVector span;
Chris@1636 280
Chris@1636 281 const sv_frame_t start = frame;
Chris@1636 282 const sv_frame_t end = frame + duration;
Chris@1636 283
Chris@1654 284 // because we don't need to "look back" at events that end within
Chris@1654 285 // but started without, we can do this entirely from m_events.
Chris@1654 286 // The core operation is very simple, it's just overspill that
Chris@1654 287 // complicates it.
Chris@1636 288
Chris@1654 289 Events::const_iterator reference =
Chris@1654 290 lower_bound(m_events.begin(), m_events.end(), Event(start));
Chris@1654 291
Chris@1654 292 Events::const_iterator first = reference;
Chris@1654 293 for (int i = 0; i < overspill; ++i) {
Chris@1654 294 if (first == m_events.begin()) break;
Chris@1654 295 --first;
Chris@1654 296 }
Chris@1654 297 for (int i = 0; i < overspill; ++i) {
Chris@1654 298 if (first == reference) break;
Chris@1654 299 span.push_back(*first);
Chris@1654 300 ++first;
Chris@1654 301 }
Chris@1654 302
Chris@1654 303 Events::const_iterator pitr = reference;
Chris@1654 304 Events::const_iterator last = reference;
Chris@1654 305
Chris@1636 306 while (pitr != m_events.end() && pitr->getFrame() < end) {
Chris@1654 307 if (!pitr->hasDuration() ||
Chris@1654 308 (pitr->getFrame() + pitr->getDuration() <= end)) {
Chris@1636 309 span.push_back(*pitr);
Chris@1654 310 last = pitr;
Chris@1654 311 ++last;
Chris@1636 312 }
Chris@1636 313 ++pitr;
Chris@1636 314 }
Chris@1654 315
Chris@1654 316 for (int i = 0; i < overspill; ++i) {
Chris@1654 317 if (last == m_events.end()) break;
Chris@1654 318 span.push_back(*last);
Chris@1654 319 ++last;
Chris@1654 320 }
Chris@1654 321
Chris@1636 322 return span;
Chris@1636 323 }
Chris@1636 324
Chris@1636 325 EventVector
Chris@1638 326 EventSeries::getEventsStartingWithin(sv_frame_t frame,
Chris@1638 327 sv_frame_t duration) const
Chris@1638 328 {
Chris@1638 329 EventVector span;
Chris@1638 330
Chris@1638 331 const sv_frame_t start = frame;
Chris@1638 332 const sv_frame_t end = frame + duration;
Chris@1638 333
Chris@1638 334 // because we don't need to "look back" at events that started
Chris@1638 335 // earlier than the start of the given range, we can do this
Chris@1638 336 // entirely from m_events
Chris@1638 337
Chris@1638 338 auto pitr = lower_bound(m_events.begin(), m_events.end(),
Chris@1638 339 Event(start));
Chris@1638 340 while (pitr != m_events.end() && pitr->getFrame() < end) {
Chris@1638 341 span.push_back(*pitr);
Chris@1638 342 ++pitr;
Chris@1638 343 }
Chris@1638 344
Chris@1638 345 return span;
Chris@1638 346 }
Chris@1638 347
Chris@1638 348 EventVector
Chris@1631 349 EventSeries::getEventsCovering(sv_frame_t frame) const
Chris@1631 350 {
Chris@1631 351 EventVector cover;
Chris@1631 352
Chris@1631 353 // first find any zero-duration events
Chris@1631 354
Chris@1631 355 auto pitr = lower_bound(m_events.begin(), m_events.end(),
Chris@1631 356 Event(frame));
Chris@1631 357 while (pitr != m_events.end() && pitr->getFrame() == frame) {
Chris@1631 358 if (!pitr->hasDuration()) {
Chris@1631 359 cover.push_back(*pitr);
Chris@1631 360 }
Chris@1631 361 ++pitr;
Chris@1631 362 }
Chris@1631 363
Chris@1631 364 // now any non-zero-duration ones from the seam map
Chris@1631 365
Chris@1631 366 std::set<Event> found;
Chris@1631 367 auto sitr = m_seams.lower_bound(frame);
Chris@1631 368 if (sitr == m_seams.end() || sitr->first > frame) {
Chris@1631 369 if (sitr != m_seams.begin()) {
Chris@1631 370 --sitr;
Chris@1631 371 }
Chris@1631 372 }
Chris@1631 373 if (sitr != m_seams.end() && sitr->first <= frame) {
Chris@1631 374 for (const auto &p: sitr->second) {
Chris@1631 375 found.insert(p);
Chris@1631 376 }
Chris@1631 377 ++sitr;
Chris@1631 378 }
Chris@1631 379 for (const auto &p: found) {
Chris@1631 380 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
Chris@1631 381 while (pitr != m_events.end() && *pitr == p) {
Chris@1631 382 cover.push_back(p);
Chris@1631 383 ++pitr;
Chris@1631 384 }
Chris@1631 385 }
Chris@1631 386
Chris@1631 387 return cover;
Chris@1631 388 }
Chris@1631 389
Chris@1644 390 EventVector
Chris@1644 391 EventSeries::getAllEvents() const
Chris@1644 392 {
Chris@1644 393 return m_events;
Chris@1644 394 }
Chris@1644 395
Chris@1632 396 bool
Chris@1632 397 EventSeries::getEventPreceding(const Event &e, Event &preceding) const
Chris@1632 398 {
Chris@1632 399 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
Chris@1632 400 if (pitr == m_events.end() || *pitr != e) {
Chris@1632 401 return false;
Chris@1632 402 }
Chris@1632 403 if (pitr == m_events.begin()) {
Chris@1632 404 return false;
Chris@1632 405 }
Chris@1632 406 --pitr;
Chris@1632 407 preceding = *pitr;
Chris@1632 408 return true;
Chris@1632 409 }
Chris@1632 410
Chris@1632 411 bool
Chris@1632 412 EventSeries::getEventFollowing(const Event &e, Event &following) const
Chris@1632 413 {
Chris@1632 414 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
Chris@1632 415 if (pitr == m_events.end() || *pitr != e) {
Chris@1632 416 return false;
Chris@1632 417 }
Chris@1633 418 while (*pitr == e) {
Chris@1633 419 ++pitr;
Chris@1633 420 if (pitr == m_events.end()) {
Chris@1633 421 return false;
Chris@1633 422 }
Chris@1632 423 }
Chris@1632 424 following = *pitr;
Chris@1632 425 return true;
Chris@1632 426 }
Chris@1632 427
Chris@1653 428 bool
Chris@1653 429 EventSeries::getNearestEventMatching(sv_frame_t startSearchAt,
Chris@1653 430 std::function<bool(const Event &)> predicate,
Chris@1653 431 Direction direction,
Chris@1653 432 Event &found) const
Chris@1653 433 {
Chris@1653 434 auto pitr = lower_bound(m_events.begin(), m_events.end(),
Chris@1653 435 Event(startSearchAt));
Chris@1653 436
Chris@1653 437 while (true) {
Chris@1653 438
Chris@1653 439 if (direction == Backward) {
Chris@1653 440 if (pitr == m_events.begin()) {
Chris@1653 441 break;
Chris@1653 442 } else {
Chris@1653 443 --pitr;
Chris@1653 444 }
Chris@1653 445 } else {
Chris@1653 446 if (pitr == m_events.end()) {
Chris@1653 447 break;
Chris@1653 448 }
Chris@1653 449 }
Chris@1653 450
Chris@1653 451 const Event &e = *pitr;
Chris@1653 452 if (predicate(e)) {
Chris@1653 453 found = e;
Chris@1653 454 return true;
Chris@1653 455 }
Chris@1653 456
Chris@1653 457 if (direction == Forward) {
Chris@1653 458 ++pitr;
Chris@1653 459 }
Chris@1653 460 }
Chris@1653 461
Chris@1653 462 return false;
Chris@1653 463 }
Chris@1653 464
Chris@1632 465 Event
Chris@1632 466 EventSeries::getEventByIndex(int index) const
Chris@1632 467 {
Chris@1632 468 if (index < 0 || index >= count()) {
Chris@1632 469 throw std::logic_error("index out of range");
Chris@1632 470 }
Chris@1632 471 return m_events[index];
Chris@1632 472 }
Chris@1632 473
Chris@1640 474 int
Chris@1640 475 EventSeries::getIndexForEvent(const Event &e) const
Chris@1640 476 {
Chris@1640 477 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
Chris@1642 478 auto d = distance(m_events.begin(), pitr);
Chris@1642 479 if (d < 0 || d > INT_MAX) return 0;
Chris@1642 480 return int(d);
Chris@1640 481 }
Chris@1640 482
Chris@1631 483 void
Chris@1631 484 EventSeries::toXml(QTextStream &out,
Chris@1631 485 QString indent,
Chris@1631 486 QString extraAttributes) const
Chris@1631 487 {
Chris@1631 488 out << indent << QString("<dataset id=\"%1\" %2>\n")
Chris@1631 489 .arg(getObjectExportId(this))
Chris@1631 490 .arg(extraAttributes);
Chris@1631 491
Chris@1631 492 for (const auto &p: m_events) {
Chris@1631 493 p.toXml(out, indent + " ");
Chris@1631 494 }
Chris@1631 495
Chris@1631 496 out << indent << "</dataset>\n";
Chris@1631 497 }
Chris@1631 498
Chris@1631 499