annotate base/EventSeries.cpp @ 1664:f66ad018b729 osc-script

Toward running an OSC script in batch
author Chris Cannam
date Mon, 25 Mar 2019 15:48: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