diff runner/JAMSFeatureWriter.cpp @ 208:c17b184c16db

Update JAMS output to JAMS v0.2.0. We now (for the first time?!) write actual JAMS schema-compliant output when possible, though it isn't possible for many types of plugin. The output for all tested combinations of transforms is valid JSON even where it isn't schema-compliant.
author Chris Cannam
date Wed, 04 Nov 2015 10:07:29 +0000
parents 1f8fef5c6ea2
children e257f776a589
line wrap: on
line diff
--- a/runner/JAMSFeatureWriter.cpp	Tue Nov 03 14:31:59 2015 +0000
+++ b/runner/JAMSFeatureWriter.cpp	Wed Nov 04 10:07:29 2015 +0000
@@ -142,25 +142,42 @@
         QString timestr = f.timestamp.toString().c_str();
         timestr.replace(QRegExp("^ +"), "");
 
+        QString durstr = "0.0";
         if (f.hasDuration) {
-
-            QString endstr = (f.timestamp + f.duration).toString().c_str();
-            endstr.replace(QRegExp("^ +"), "");
-        
-            d += QString
-                ("\"start\": { \"value\": %1 }, "
-                 "\"end\": { \"value\": %2 }").arg(timestr).arg(endstr);
-        } else {
-            d += QString("\"time\": { \"value\": %1 }").arg(timestr);
+            durstr = f.duration.toString().c_str();
+            durstr.replace(QRegExp("^ +"), "");
         }
         
+        d += QString("\"time\": %1, \"duration\": %2, \"confidence\": 1.0")
+            .arg(timestr).arg(durstr);
+
+        // here we have to differ from the JAMS 0.2.0 spec. It allows
+        // a single "value" element which can be either a number or a
+        // string, depending on the selected task. But we may have
+        // many values and may have a label as well, and no way to
+        // know whether these can be made to conform to the JAMS task
+        // schema. We should just write what we have. If we only have
+        // a label, we can write that out as "value" as JAMS requests,
+        // but if we have a (numerical) value and a label, we really
+        // have to write them separately, and if we have multiple
+        // values we'll have to use an array. The chances of actually
+        // ending up with a schema-compliant JAMS format are quite
+        // small, which suggests JAMS isn't a great idea for this
+        // after all!
+        
         if (f.label != "") {
-            d += QString(", \"label\": { \"value\": \"%2\" }")
-                .arg(f.label.c_str());
+            if (f.values.empty()) {
+                d += QString(", \"value\": \"%2\"").arg(f.label.c_str());
+            } else {
+                d += QString(", \"label\": \"%2\"").arg(f.label.c_str());
+            }
         }
 
-        if (f.values.size() > 0) {
-            d += QString(", \"value\": [ ");
+        if (!f.values.empty()) {
+            d += QString(", \"value\": ");
+            if (f.values.size() > 1) {
+                d += "[ ";
+            }
             for (int j = 0; j < int(f.values.size()); ++j) {
                 if (isnan(f.values[j])) {
                     d += "\"NaN\"";
@@ -173,7 +190,9 @@
                     d += ", ";
                 }
             }
-            d += " ]";
+            if (f.values.size() > 1) {
+                d += " ]";
+            }
         }
             
         d += " }";
@@ -222,10 +241,16 @@
 
             stream << "{\n"
                    << QString("\"file_metadata\": {\n"
-                              "  \"filename\": \"%1\"")
+                              "  \"jams_version\": \"0.2.0\",\n"
+                              "  \"identifiers\": { \"filename\": \"%1\" }")
                 .arg(QFileInfo(trackId).fileName());
 
             if (m_trackMetadata.find(trackId) != m_trackMetadata.end()) {
+
+                QString durstr = m_trackMetadata[trackId].duration.toString().c_str();
+                durstr.replace(QRegExp("^ +"), "");
+                stream << QString(",\n  \"duration\": %1").arg(durstr);
+
                 if (m_trackMetadata[trackId].maker != "") {
                     stream << QString(",\n  \"artist\": \"%1\"")
                         .arg(m_trackMetadata[trackId].maker);
@@ -237,6 +262,7 @@
             }
 
             stream << "\n},\n";
+            stream << "\"annotations\": [\n";
 
             bool firstInTrack = true;
 
@@ -245,14 +271,6 @@
                 
                 Task task = *ti;
 
-                if (!firstInTrack) {
-                    stream << ",\n";
-                }
-
-                stream << "\"" << getTaskKey(task) << "\": [\n";
-                
-                bool firstInTask = true;
-
                 for (DataIds::const_iterator di = m_streamData[sptr].begin();
                      di != m_streamData[sptr].end(); ++di) {
                     
@@ -265,13 +283,14 @@
 
                     QString data = m_data[did];
 
-                    if (!firstInTask) {
+                    if (!firstInTrack) {
                         stream << ",\n";
                     }
 
+                    stream << "{\n  \"namespace\": \"" << getTaskKey(task) << "\",\n";
+
                     stream << QString
-                        ("{ \n"
-                         "  \"annotation_metadata\": {\n"
+                        ("  \"annotation_metadata\": {\n"
                          "    \"annotation_tools\": \"Sonic Annotator v%2\",\n"
                          "    \"data_source\": \"Automatic feature extraction\",\n"
                          "    \"annotator\": {\n"
@@ -285,12 +304,11 @@
                     stream << data;
 
                     stream << "\n  ]\n}";
-                    firstInTask = false;
+                    firstInTrack = false;
                 }
+            }
 
-                stream << "\n]";
-                firstInTrack = false;
-            }
+            stream << "\n]";
 
             stream << "\n}";
             firstInStream = false;