comparison 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
comparison
equal deleted inserted replaced
207:e8f2f530c091 208:c17b184c16db
140 Plugin::Feature f(features[i]); 140 Plugin::Feature f(features[i]);
141 141
142 QString timestr = f.timestamp.toString().c_str(); 142 QString timestr = f.timestamp.toString().c_str();
143 timestr.replace(QRegExp("^ +"), ""); 143 timestr.replace(QRegExp("^ +"), "");
144 144
145 QString durstr = "0.0";
145 if (f.hasDuration) { 146 if (f.hasDuration) {
146 147 durstr = f.duration.toString().c_str();
147 QString endstr = (f.timestamp + f.duration).toString().c_str(); 148 durstr.replace(QRegExp("^ +"), "");
148 endstr.replace(QRegExp("^ +"), ""); 149 }
149 150
150 d += QString 151 d += QString("\"time\": %1, \"duration\": %2, \"confidence\": 1.0")
151 ("\"start\": { \"value\": %1 }, " 152 .arg(timestr).arg(durstr);
152 "\"end\": { \"value\": %2 }").arg(timestr).arg(endstr); 153
153 } else { 154 // here we have to differ from the JAMS 0.2.0 spec. It allows
154 d += QString("\"time\": { \"value\": %1 }").arg(timestr); 155 // a single "value" element which can be either a number or a
155 } 156 // string, depending on the selected task. But we may have
157 // many values and may have a label as well, and no way to
158 // know whether these can be made to conform to the JAMS task
159 // schema. We should just write what we have. If we only have
160 // a label, we can write that out as "value" as JAMS requests,
161 // but if we have a (numerical) value and a label, we really
162 // have to write them separately, and if we have multiple
163 // values we'll have to use an array. The chances of actually
164 // ending up with a schema-compliant JAMS format are quite
165 // small, which suggests JAMS isn't a great idea for this
166 // after all!
156 167
157 if (f.label != "") { 168 if (f.label != "") {
158 d += QString(", \"label\": { \"value\": \"%2\" }") 169 if (f.values.empty()) {
159 .arg(f.label.c_str()); 170 d += QString(", \"value\": \"%2\"").arg(f.label.c_str());
160 } 171 } else {
161 172 d += QString(", \"label\": \"%2\"").arg(f.label.c_str());
162 if (f.values.size() > 0) { 173 }
163 d += QString(", \"value\": [ "); 174 }
175
176 if (!f.values.empty()) {
177 d += QString(", \"value\": ");
178 if (f.values.size() > 1) {
179 d += "[ ";
180 }
164 for (int j = 0; j < int(f.values.size()); ++j) { 181 for (int j = 0; j < int(f.values.size()); ++j) {
165 if (isnan(f.values[j])) { 182 if (isnan(f.values[j])) {
166 d += "\"NaN\""; 183 d += "\"NaN\"";
167 } else if (isinf(f.values[j])) { 184 } else if (isinf(f.values[j])) {
168 d += "\"Inf\""; 185 d += "\"Inf\"";
171 } 188 }
172 if (j + 1 < int(f.values.size())) { 189 if (j + 1 < int(f.values.size())) {
173 d += ", "; 190 d += ", ";
174 } 191 }
175 } 192 }
176 d += " ]"; 193 if (f.values.size() > 1) {
194 d += " ]";
195 }
177 } 196 }
178 197
179 d += " }"; 198 d += " }";
180 } 199 }
181 200
220 stream << ",\n"; 239 stream << ",\n";
221 } 240 }
222 241
223 stream << "{\n" 242 stream << "{\n"
224 << QString("\"file_metadata\": {\n" 243 << QString("\"file_metadata\": {\n"
225 " \"filename\": \"%1\"") 244 " \"jams_version\": \"0.2.0\",\n"
245 " \"identifiers\": { \"filename\": \"%1\" }")
226 .arg(QFileInfo(trackId).fileName()); 246 .arg(QFileInfo(trackId).fileName());
227 247
228 if (m_trackMetadata.find(trackId) != m_trackMetadata.end()) { 248 if (m_trackMetadata.find(trackId) != m_trackMetadata.end()) {
249
250 QString durstr = m_trackMetadata[trackId].duration.toString().c_str();
251 durstr.replace(QRegExp("^ +"), "");
252 stream << QString(",\n \"duration\": %1").arg(durstr);
253
229 if (m_trackMetadata[trackId].maker != "") { 254 if (m_trackMetadata[trackId].maker != "") {
230 stream << QString(",\n \"artist\": \"%1\"") 255 stream << QString(",\n \"artist\": \"%1\"")
231 .arg(m_trackMetadata[trackId].maker); 256 .arg(m_trackMetadata[trackId].maker);
232 } 257 }
233 if (m_trackMetadata[trackId].title != "") { 258 if (m_trackMetadata[trackId].title != "") {
235 .arg(m_trackMetadata[trackId].title); 260 .arg(m_trackMetadata[trackId].title);
236 } 261 }
237 } 262 }
238 263
239 stream << "\n},\n"; 264 stream << "\n},\n";
265 stream << "\"annotations\": [\n";
240 266
241 bool firstInTrack = true; 267 bool firstInTrack = true;
242 268
243 for (Tasks::const_iterator ti = m_streamTasks[sptr].begin(); 269 for (Tasks::const_iterator ti = m_streamTasks[sptr].begin();
244 ti != m_streamTasks[sptr].end(); ++ti) { 270 ti != m_streamTasks[sptr].end(); ++ti) {
245 271
246 Task task = *ti; 272 Task task = *ti;
247 273
248 if (!firstInTrack) {
249 stream << ",\n";
250 }
251
252 stream << "\"" << getTaskKey(task) << "\": [\n";
253
254 bool firstInTask = true;
255
256 for (DataIds::const_iterator di = m_streamData[sptr].begin(); 274 for (DataIds::const_iterator di = m_streamData[sptr].begin();
257 di != m_streamData[sptr].end(); ++di) { 275 di != m_streamData[sptr].end(); ++di) {
258 276
259 DataId did = *di; 277 DataId did = *di;
260 278
263 281
264 if (m_tasks[transform.getIdentifier()] != task) continue; 282 if (m_tasks[transform.getIdentifier()] != task) continue;
265 283
266 QString data = m_data[did]; 284 QString data = m_data[did];
267 285
268 if (!firstInTask) { 286 if (!firstInTrack) {
269 stream << ",\n"; 287 stream << ",\n";
270 } 288 }
271 289
290 stream << "{\n \"namespace\": \"" << getTaskKey(task) << "\",\n";
291
272 stream << QString 292 stream << QString
273 ("{ \n" 293 (" \"annotation_metadata\": {\n"
274 " \"annotation_metadata\": {\n"
275 " \"annotation_tools\": \"Sonic Annotator v%2\",\n" 294 " \"annotation_tools\": \"Sonic Annotator v%2\",\n"
276 " \"data_source\": \"Automatic feature extraction\",\n" 295 " \"data_source\": \"Automatic feature extraction\",\n"
277 " \"annotator\": {\n" 296 " \"annotator\": {\n"
278 "%3" 297 "%3"
279 " }\n" 298 " }\n"
283 .arg(writeTransformToObjectContents(transform)); 302 .arg(writeTransformToObjectContents(transform));
284 303
285 stream << data; 304 stream << data;
286 305
287 stream << "\n ]\n}"; 306 stream << "\n ]\n}";
288 firstInTask = false; 307 firstInTrack = false;
289 } 308 }
290 309 }
291 stream << "\n]"; 310
292 firstInTrack = false; 311 stream << "\n]";
293 }
294 312
295 stream << "\n}"; 313 stream << "\n}";
296 firstInStream = false; 314 firstInStream = false;
297 } 315 }
298 316