# HG changeset patch # User Chris Cannam # Date 1552478053 0 # Node ID e7f557789f99ea29253ce009987a56ad75353ade # Parent 7482da1cd92038815338e401456a9ded97fd197a Add and test getEndFrame (and getStartFrame) diff -r 7482da1cd920 -r e7f557789f99 base/EventSeries.cpp --- a/base/EventSeries.cpp Wed Mar 13 10:55:24 2019 +0000 +++ b/base/EventSeries.cpp Wed Mar 13 11:54:13 2019 +0000 @@ -40,6 +40,10 @@ } m_events.insert(pitr, p); + if (!p.hasDuration() && p.getFrame() > m_finalDurationlessEventFrame) { + m_finalDurationlessEventFrame = p.getFrame(); + } + if (p.hasDuration() && isUnique) { const sv_frame_t frame = p.getFrame(); @@ -92,6 +96,17 @@ m_events.erase(pitr); + if (!p.hasDuration() && isUnique && + p.getFrame() == m_finalDurationlessEventFrame) { + m_finalDurationlessEventFrame = 0; + for (auto ritr = m_events.rbegin(); ritr != m_events.rend(); ++ritr) { + if (!ritr->hasDuration()) { + m_finalDurationlessEventFrame = ritr->getFrame(); + break; + } + } + } + if (p.hasDuration() && isUnique) { const sv_frame_t frame = p.getFrame(); @@ -181,6 +196,33 @@ { m_events.clear(); m_seams.clear(); + m_finalDurationlessEventFrame = 0; +} + +sv_frame_t +EventSeries::getStartFrame() const +{ + if (m_events.empty()) return 0; + return m_events.begin()->getFrame(); +} + +sv_frame_t +EventSeries::getEndFrame() const +{ + sv_frame_t latest = 0; + + if (m_events.empty()) return latest; + + latest = m_finalDurationlessEventFrame; + + if (m_seams.empty()) return latest; + + sv_frame_t lastSeam = m_seams.rbegin()->first; + if (lastSeam > latest) { + latest = lastSeam; + } + + return latest; } EventVector @@ -362,6 +404,13 @@ return m_events[index]; } +int +EventSeries::getIndexForEvent(const Event &e) const +{ + auto pitr = lower_bound(m_events.begin(), m_events.end(), e); + return distance(m_events.begin(), pitr); +} + void EventSeries::toXml(QTextStream &out, QString indent, diff -r 7482da1cd920 -r e7f557789f99 base/EventSeries.h --- a/base/EventSeries.h Wed Mar 13 10:55:24 2019 +0000 +++ b/base/EventSeries.h Wed Mar 13 11:54:13 2019 +0000 @@ -42,7 +42,7 @@ class EventSeries : public XmlExportable { public: - EventSeries() { } + EventSeries() : m_finalDurationlessEventFrame(0) { } ~EventSeries() =default; EventSeries(const EventSeries &) =default; @@ -61,6 +61,18 @@ int count() const; /** + * Return the frame of the first event in the series. If there are + * no events, return 0. + */ + sv_frame_t getStartFrame() const; + + /** + * Return the frame plus duration of the event in the series that + * ends last. If there are no events, return 0. + */ + sv_frame_t getEndFrame() const; + + /** * Retrieve all events any part of which falls within the range in * frames defined by the given frame f and duration d. * @@ -149,6 +161,13 @@ * where 0 = the first event and count()-1 = the last. */ Event getEventByIndex(int index) const; + + /** + * Return the index of the first event in the series that does not + * compare inferior to the given event. If there is no such event, + * return count(). + */ + int getIndexForEvent(const Event &e) const; /** * Emit to XML as a dataset element. @@ -192,6 +211,17 @@ typedef std::map> FrameEventMap; FrameEventMap m_seams; + /** + * The frame of the last durationless event we have in the series. + * This is to support a fast-ish getEndFrame(): we can easily keep + * this up-to-date when events are added or removed, and we can + * easily find the end frame of the last with-duration event from + * the seam map, but it's not so easy to continuously update an + * overall end frame or to find the last frame of all events + * without this. + */ + sv_frame_t m_finalDurationlessEventFrame; + /** Create a seam at the given frame, copying from the prior seam * if there is one. If a seam already exists at the given frame, * leave it untouched. diff -r 7482da1cd920 -r e7f557789f99 base/test/TestEventSeries.h --- a/base/test/TestEventSeries.h Wed Mar 13 10:55:24 2019 +0000 +++ b/base/test/TestEventSeries.h Wed Mar 13 11:54:13 2019 +0000 @@ -284,6 +284,27 @@ QCOMPARE(s.getEventsSpanning(10, 0), EventVector()); } + void multipleEventsEndFrame() { + + EventSeries s; + Event a(10, QString("a")); + Event b(11, QString("b")); + Event c(40, QString("c")); + s.add(c); + s.add(a); + s.add(b); + s.add(b); + QCOMPARE(s.getEndFrame(), 40); + s.remove(c); + QCOMPARE(s.getEndFrame(), 11); + s.remove(b); + QCOMPARE(s.getEndFrame(), 11); + s.remove(a); + QCOMPARE(s.getEndFrame(), 11); + s.remove(b); + QCOMPARE(s.getEndFrame(), 0); + } + void disjointEventsWithDurationCover() { EventSeries s; @@ -468,6 +489,26 @@ EventVector({ b, c, cc, d, dd })); } + void eventPatternEndFrame() { + + EventSeries s; + Event a(0, 1.0f, 18, QString("a")); + Event b(3, 2.0f, 6, QString("b")); + Event c(5, 3.0f, 2, QString("c")); + Event cc(5, 3.1f, 2, QString("cc")); + Event d(6, 4.0f, 10, QString("d")); + Event dd(6, 4.5f, 10, QString("dd")); + Event e(14, 5.0f, 3, QString("e")); + s.add(b); + s.add(c); + s.add(d); + s.add(a); + s.add(cc); + s.add(dd); + s.add(e); + QCOMPARE(s.getEndFrame(), 18); + } + void eventPatternAddRemove() { // This is mostly here to exercise the innards of EventSeries @@ -492,17 +533,21 @@ QCOMPARE(s.count(), 7); s.remove(d); QCOMPARE(s.getEventsCovering(8), EventVector({ a, b, dd })); + QCOMPARE(s.getEndFrame(), 18); s.remove(e); s.remove(a); QCOMPARE(s.getEventsCovering(8), EventVector({ b, dd })); + QCOMPARE(s.getEndFrame(), 16); s.remove(cc); s.remove(c); s.remove(dd); QCOMPARE(s.getEventsCovering(8), EventVector({ b })); + QCOMPARE(s.getEndFrame(), 9); s.remove(b); QCOMPARE(s.getEventsCovering(8), EventVector()); QCOMPARE(s.count(), 0); QCOMPARE(s.isEmpty(), true); + QCOMPARE(s.getEndFrame(), 0); } void preceding() {