changeset 157:946115b8badd labfile

Proper implementation of fill-ends flag in LabFeatureWriter
author Chris Cannam
date Wed, 15 Oct 2014 08:00:01 +0100
parents 0fd5c3c28814
children a2310369b2cc
files runner/LabFeatureWriter.cpp runner/LabFeatureWriter.h tests/test-lab-writer/expected/curve-vsr-fill-ends.lab
diffstat 3 files changed, 88 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/runner/LabFeatureWriter.cpp	Tue Oct 14 18:50:01 2014 +0100
+++ b/runner/LabFeatureWriter.cpp	Wed Oct 15 08:00:01 2014 +0100
@@ -78,58 +78,99 @@
                         const Transform &transform,
                         const Plugin::OutputDescriptor& ,
                         const Plugin::FeatureList& features,
-                        std::string summaryType)
+                        std::string)
 {
     // Select appropriate output file for our track/transform
     // combination
 
-    QTextStream *sptr = getOutputStream(trackId, transform.getIdentifier());
+    TransformId transformId = transform.getIdentifier();
+
+    QTextStream *sptr = getOutputStream(trackId, transformId);
     if (!sptr) {
-        throw FailedToOpenOutputStream(trackId, transform.getIdentifier());
+        throw FailedToOpenOutputStream(trackId, transformId);
     }
 
     QTextStream &stream = *sptr;
 
-    QString sep = "\t";
+    int n = features.size();
 
-    for (unsigned int i = 0; i < features.size(); ++i) {
+    if (n == 0) return;
 
-        QString timestamp = features[i].timestamp.toString().c_str();
-        timestamp.replace(QRegExp("^ +"), "");
-        stream << timestamp;
+    TrackTransformPair tt(trackId, transformId);
 
-        Vamp::RealTime endTime;
-        bool haveEndTime = true;
+    if (m_pending.find(tt) != m_pending.end()) {
+        writeFeature(stream, m_pending[tt], &features[0]);
+        m_pending.erase(tt);
+    }
 
-        if (features[i].hasDuration) {
-            endTime = features[i].timestamp + features[i].duration;
-        } else if (m_forceEnd) {
-            if (i+1 < features.size()) {
-                endTime = features[i+1].timestamp;
-            } else {
-                //!!! what to do??? can we get the end time of the input file?
-                endTime = features[i].timestamp;
-            }
-        } else {
-            haveEndTime = false;
-        }
+    if (m_forceEnd) {
+        // can't write final feature until we know its end time
+        --n;
+        m_pending[tt] = features[n];
+    }
 
-        if (haveEndTime) {
-            QString e = endTime.toString().c_str();
-            e.replace(QRegExp("^ +"), "");
-            stream << sep << e;
-        }
-
-        for (unsigned int j = 0; j < features[i].values.size(); ++j) {
-            stream << sep << features[i].values[j];
-        }
-
-        if (features[i].label != "") {
-            stream << sep << "\"" << features[i].label.c_str() << "\"";
-        }
-
-        stream << "\n";
+    for (int i = 0; i < n; ++i) {
+        writeFeature(stream, features[i], m_forceEnd ? &features[i+1] : 0);
     }
 }
 
+void
+LabFeatureWriter::finish()
+{
+    for (PendingFeatures::const_iterator i = m_pending.begin();
+         i != m_pending.end(); ++i) {
+        TrackTransformPair tt = i->first;
+        Plugin::Feature f = i->second;
+        QTextStream *sptr = getOutputStream(tt.first, tt.second);
+        if (!sptr) {
+            throw FailedToOpenOutputStream(tt.first, tt.second);
+        }
+        QTextStream &stream = *sptr;
+        // final feature has its own time as end time (we can't
+        // reliably determine the end of audio file, and because of
+        // the nature of block processing, the feature could even
+        // start beyond that anyway)
+        writeFeature(stream, f, &f);
+    }
+}
 
+void
+LabFeatureWriter::writeFeature(QTextStream &stream,
+                               const Plugin::Feature &f,
+                               const Plugin::Feature *optionalNextFeature)
+{
+    QString sep = "\t";
+
+    QString timestamp = f.timestamp.toString().c_str();
+    timestamp.replace(QRegExp("^ +"), "");
+    stream << timestamp;
+
+    Vamp::RealTime endTime;
+    bool haveEndTime = true;
+
+    if (f.hasDuration) {
+        endTime = f.timestamp + f.duration;
+    } else if (optionalNextFeature) {
+        endTime = optionalNextFeature->timestamp;
+    } else {
+        haveEndTime = false;
+    }
+
+    if (haveEndTime) {
+        QString e = endTime.toString().c_str();
+        e.replace(QRegExp("^ +"), "");
+        stream << sep << e;
+    }
+    
+    for (unsigned int j = 0; j < f.values.size(); ++j) {
+        stream << sep << f.values[j];
+    }
+    
+    if (f.label != "") {
+        stream << sep << "\"" << f.label.c_str() << "\"";
+    }
+    
+    stream << "\n";
+}
+
+
--- a/runner/LabFeatureWriter.h	Tue Oct 14 18:50:01 2014 +0100
+++ b/runner/LabFeatureWriter.h	Wed Oct 15 08:00:01 2014 +0100
@@ -51,10 +51,19 @@
                        const Vamp::Plugin::FeatureList &features,
                        std::string summaryType = "");
 
+    virtual void finish();
+
     virtual QString getWriterTag() const { return "lab"; }
 
 private:
     bool m_forceEnd;
+    
+    typedef map<TrackTransformPair, Vamp::Plugin::Feature> PendingFeatures;
+    PendingFeatures m_pending;
+
+    void writeFeature(QTextStream &,
+                      const Vamp::Plugin::Feature &f,
+                      const Vamp::Plugin::Feature *optionalNextFeature);
 };
 
 #endif
--- a/tests/test-lab-writer/expected/curve-vsr-fill-ends.lab	Tue Oct 14 18:50:01 2014 +0100
+++ b/tests/test-lab-writer/expected/curve-vsr-fill-ends.lab	Wed Oct 15 08:00:01 2014 +0100
@@ -2,7 +2,7 @@
 0.750000000	1.500000000	0.1	"2 of 10: 0.1 at 0.75"
 1.500000000	2.250000000	0.2	"3 of 10: 0.2 at 1.5"
 2.250000000	3.000000000	0.3	"4 of 10: 0.3 at 2.25"
-3.000000000	3.7500000000	0.4	"5 of 10: 0.4 at 3"
+3.000000000	3.750000000	0.4	"5 of 10: 0.4 at 3"
 3.750000000	4.500000000	0.5	"6 of 10: 0.5 at 3.75"
 4.500000000	5.250000000	0.6	"7 of 10: 0.6 at 4.5"
 5.250000000	6.000000000	0.7	"8 of 10: 0.7 at 5.25"