Mercurial > hg > sonic-annotator
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 |