changeset 1640:e7f557789f99 single-point

Add and test getEndFrame (and getStartFrame)
author Chris Cannam
date Wed, 13 Mar 2019 11:54:13 +0000
parents 7482da1cd920
children 751a52865270
files base/EventSeries.cpp base/EventSeries.h base/test/TestEventSeries.h
diffstat 3 files changed, 125 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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,
--- 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<sv_frame_t, std::vector<Event>> 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.
--- 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() {