Mercurial > hg > svcore
diff base/EventSeries.cpp @ 1631:b2048f350906 single-point
Switch EventSeries to using a vector for m_events, so as to allow indexed access
author | Chris Cannam |
---|---|
date | Tue, 12 Mar 2019 14:14:00 +0000 |
parents | |
children | 0890c10e5129 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/EventSeries.cpp Tue Mar 12 14:14:00 2019 +0000 @@ -0,0 +1,290 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "EventSeries.h" + +bool +EventSeries::isEmpty() const +{ + return m_events.empty(); +} + +int +EventSeries::count() const +{ + if (m_events.size() > INT_MAX) { + throw std::runtime_error("too many events"); + } + return int(m_events.size()); +} + +void +EventSeries::add(const Event &p) +{ + bool isUnique = true; + + auto pitr = lower_bound(m_events.begin(), m_events.end(), p); + if (pitr != m_events.end() && *pitr == p) { + isUnique = false; + } + m_events.insert(pitr, p); + + if (p.hasDuration() && isUnique) { + + const sv_frame_t frame = p.getFrame(); + const sv_frame_t endFrame = p.getFrame() + p.getDuration(); + + createSeam(frame); + createSeam(endFrame); + + // These calls must both succeed after calling createSeam above + const auto i0 = m_seams.find(frame); + const auto i1 = m_seams.find(endFrame); + + for (auto i = i0; i != i1; ++i) { + if (i == m_seams.end()) { + SVCERR << "ERROR: EventSeries::add: " + << "reached end of seam map" + << endl; + break; + } + i->second.push_back(p); + } + } + +#ifdef DEBUG_EVENT_SERIES + std::cerr << "after add:" << std::endl; + dumpEvents(); + dumpSeams(); +#endif +} + +void +EventSeries::remove(const Event &p) +{ + // If we are removing the last (unique) example of an event, + // then we also need to remove it from the seam map. If this + // is only one of multiple identical events, then we don't. + bool isUnique = true; + + auto pitr = lower_bound(m_events.begin(), m_events.end(), p); + if (pitr == m_events.end() || *pitr != p) { + // we don't know this event + return; + } else { + auto nitr = pitr; + ++nitr; + if (nitr != m_events.end() && *nitr == p) { + isUnique = false; + } + } + + m_events.erase(pitr); + + if (p.hasDuration() && isUnique) { + + const sv_frame_t frame = p.getFrame(); + const sv_frame_t endFrame = p.getFrame() + p.getDuration(); + + const auto i0 = m_seams.find(frame); + const auto i1 = m_seams.find(endFrame); + +#ifdef DEBUG_EVENT_SERIES + // This should be impossible if we found p in m_events above + if (i0 == m_seams.end() || i1 == m_seams.end()) { + SVCERR << "ERROR: EventSeries::remove: either frame " << frame + << " or endFrame " << endFrame + << " for event not found in seam map: event is " + << p.toXmlString() << endl; + } +#endif + + // Remove any and all instances of p from the seam map; we + // are only supposed to get here if we are removing the + // last instance of p from the series anyway + + for (auto i = i0; i != i1; ++i) { + if (i == m_seams.end()) { + // This can happen only if we have a negative + // duration, which Event forbids + throw std::logic_error("unexpectedly reached end of map"); + } + for (size_t j = 0; j < i->second.size(); ) { + if (i->second[j] == p) { + i->second.erase(i->second.begin() + j); + } else { + ++j; + } + } + } + + // Tidy up by removing any entries that are now identical + // to their predecessors + + std::vector<sv_frame_t> redundant; + + auto pitr = m_seams.end(); + if (i0 != m_seams.begin()) { + pitr = i0; + --pitr; + } + + for (auto i = i0; i != m_seams.end(); ++i) { + if (pitr != m_seams.end() && + seamsEqual(i->second, pitr->second)) { + redundant.push_back(i->first); + } + pitr = i; + if (i == i1) { + break; + } + } + + for (sv_frame_t f: redundant) { + m_seams.erase(f); + } + + // And remove any empty seams from the start of the map + + while (m_seams.begin() != m_seams.end() && + m_seams.begin()->second.empty()) { + m_seams.erase(m_seams.begin()); + } + } + +#ifdef DEBUG_EVENT_SERIES + std::cerr << "after remove:" << std::endl; + dumpEvents(); + dumpSeams(); +#endif +} + +bool +EventSeries::contains(const Event &p) const +{ + return binary_search(m_events.begin(), m_events.end(), p); +} + +void +EventSeries::clear() +{ + m_events.clear(); + m_seams.clear(); +} + +EventVector +EventSeries::getEventsSpanning(sv_frame_t frame, + sv_frame_t duration) const +{ + EventVector span; + + const sv_frame_t start = frame; + const sv_frame_t end = frame + duration; + + // first find any zero-duration events + + auto pitr = lower_bound(m_events.begin(), m_events.end(), + Event(start)); + while (pitr != m_events.end() && pitr->getFrame() < end) { + if (!pitr->hasDuration()) { + span.push_back(*pitr); + } + ++pitr; + } + + // now any non-zero-duration ones from the seam map + + std::set<Event> found; + auto sitr = m_seams.lower_bound(start); + if (sitr == m_seams.end() || sitr->first > start) { + if (sitr != m_seams.begin()) { + --sitr; + } + } + while (sitr != m_seams.end() && sitr->first < end) { + for (const auto &p: sitr->second) { + found.insert(p); + } + ++sitr; + } + for (const auto &p: found) { + auto pitr = lower_bound(m_events.begin(), m_events.end(), p); + while (pitr != m_events.end() && *pitr == p) { + span.push_back(p); + ++pitr; + } + } + + return span; +} + +EventVector +EventSeries::getEventsCovering(sv_frame_t frame) const +{ + EventVector cover; + + // first find any zero-duration events + + auto pitr = lower_bound(m_events.begin(), m_events.end(), + Event(frame)); + while (pitr != m_events.end() && pitr->getFrame() == frame) { + if (!pitr->hasDuration()) { + cover.push_back(*pitr); + } + ++pitr; + } + + // now any non-zero-duration ones from the seam map + + std::set<Event> found; + auto sitr = m_seams.lower_bound(frame); + if (sitr == m_seams.end() || sitr->first > frame) { + if (sitr != m_seams.begin()) { + --sitr; + } + } + if (sitr != m_seams.end() && sitr->first <= frame) { + for (const auto &p: sitr->second) { + found.insert(p); + } + ++sitr; + } + for (const auto &p: found) { + auto pitr = lower_bound(m_events.begin(), m_events.end(), p); + while (pitr != m_events.end() && *pitr == p) { + cover.push_back(p); + ++pitr; + } + } + + return cover; +} + +void +EventSeries::toXml(QTextStream &out, + QString indent, + QString extraAttributes) const +{ + out << indent << QString("<dataset id=\"%1\" %2>\n") + .arg(getObjectExportId(this)) + .arg(extraAttributes); + + for (const auto &p: m_events) { + p.toXml(out, indent + " "); + } + + out << indent << "</dataset>\n"; +} + +