# HG changeset patch # User Chris Cannam # Date 1413385787 -3600 # Node ID c1834a31029c14e5bd31556ff4c90e15c6aa5829 # Parent f4f770b4356b31aaffe6fda8069036259e7df0a3# Parent 3536342ac0885a43c5b4aa72a67f5b4ed1d58359 Merge from branch "jams" diff -r f4f770b4356b -r c1834a31029c .hgsubstate --- a/.hgsubstate Wed Oct 15 13:06:27 2014 +0100 +++ b/.hgsubstate Wed Oct 15 16:09:47 2014 +0100 @@ -1,3 +1,3 @@ d16f0fd6db6104d87882bc43788a3bb1b0f8c528 dataquay 879bdc878826bebec67130326f99397c430419b1 sv-dependency-builds -274c4362bda661a7ef7706cf6b160777b88b3abe svcore +6b2a8b34e9d3d9a573af2ae303f33362e4c17784 svcore diff -r f4f770b4356b -r c1834a31029c runner.pro --- a/runner.pro Wed Oct 15 13:06:27 2014 +0100 +++ b/runner.pro Wed Oct 15 16:09:47 2014 +0100 @@ -84,6 +84,7 @@ runner/FeatureWriterFactory.h \ runner/DefaultFeatureWriter.h \ runner/FeatureExtractionManager.h \ + runner/JAMSFeatureWriter.h \ runner/LabFeatureWriter.h \ runner/MIDIFeatureWriter.h \ runner/MultiplexedReader.h @@ -94,6 +95,7 @@ runner/FeatureExtractionManager.cpp \ runner/AudioDBFeatureWriter.cpp \ runner/FeatureWriterFactory.cpp \ + runner/JAMSFeatureWriter.cpp \ runner/LabFeatureWriter.cpp \ runner/MIDIFeatureWriter.cpp \ runner/MultiplexedReader.cpp diff -r f4f770b4356b -r c1834a31029c runner/FeatureExtractionManager.cpp --- a/runner/FeatureExtractionManager.cpp Wed Oct 15 13:06:27 2014 +0100 +++ b/runner/FeatureExtractionManager.cpp Wed Oct 15 16:09:47 2014 +0100 @@ -728,7 +728,7 @@ FeatureWriter::TrackMetadata m; m.title = reader->getTitle(); m.maker = reader->getMaker(); - if (m.title != "" && m.maker != "") { + if (m.title != "" || m.maker != "") { writers[j]->setTrackMetadata(audioSource, m); } } diff -r f4f770b4356b -r c1834a31029c runner/FeatureWriterFactory.cpp --- a/runner/FeatureWriterFactory.cpp Wed Oct 15 13:06:27 2014 +0100 +++ b/runner/FeatureWriterFactory.cpp Wed Oct 15 16:09:47 2014 +0100 @@ -23,6 +23,7 @@ #include "AudioDBFeatureWriter.h" #include "MIDIFeatureWriter.h" +#include "JAMSFeatureWriter.h" #include "LabFeatureWriter.h" set @@ -35,6 +36,7 @@ tags.insert("csv"); tags.insert("lab"); tags.insert("midi"); + tags.insert("json"); return tags; } @@ -53,6 +55,8 @@ return new LabFeatureWriter(); } else if (tag == "midi") { return new MIDIFeatureWriter(); + } else if (tag == "json") { + return new JAMSFeatureWriter(); } return 0; diff -r f4f770b4356b -r c1834a31029c runner/JAMSFeatureWriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/JAMSFeatureWriter.cpp Wed Oct 15 16:09:47 2014 +0100 @@ -0,0 +1,468 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Annotator + A utility for batch feature extraction from audio files. + Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London. + Copyright 2007-2014 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "JAMSFeatureWriter.h" + +using namespace std; +using Vamp::Plugin; +using Vamp::PluginBase; + +#include "base/Exceptions.h" +#include "rdf/PluginRDFIndexer.h" + +#include + +#include "version.h" + +JAMSFeatureWriter::JAMSFeatureWriter() : + FileFeatureWriter(SupportOneFilePerTrackTransform | + SupportOneFilePerTrack | + SupportOneFileTotal | + SupportStdOut, + "json"), + m_network(false), + m_networkRetrieved(false), + m_n(1), + m_m(1) +{ +} + +JAMSFeatureWriter::~JAMSFeatureWriter() +{ +} + +string +JAMSFeatureWriter::getDescription() const +{ + return "Write features to JSON files in JAMS (JSON Annotated Music Specification) format. WARNING: This is a provisional implementation! The output format may change in future releases to comply more effectively with the specification. Please report any problems you find with the current implementation."; +} + +JAMSFeatureWriter::ParameterList +JAMSFeatureWriter::getSupportedParameters() const +{ + ParameterList pl = FileFeatureWriter::getSupportedParameters(); + Parameter p; + + p.name = "network"; + p.description = "Attempt to retrieve RDF descriptions of plugins from network, if not available locally"; + p.hasArg = false; + pl.push_back(p); + + return pl; +} + +void +JAMSFeatureWriter::setParameters(map ¶ms) +{ + FileFeatureWriter::setParameters(params); + + for (map::iterator i = params.begin(); + i != params.end(); ++i) { + if (i->first == "network") { + m_network = true; + } + } +} + +void +JAMSFeatureWriter::setTrackMetadata(QString trackId, TrackMetadata metadata) +{ + m_trackMetadata[trackId] = metadata; +} + +static double +realTime2Sec(const Vamp::RealTime &r) +{ + return r / Vamp::RealTime(1, 0); +} + +void +JAMSFeatureWriter::write(QString trackId, + const Transform &transform, + const Plugin::OutputDescriptor& , + const Plugin::FeatureList& features, + std::string /* summaryType */) +{ + QString transformId = transform.getIdentifier(); + + QTextStream *sptr = getOutputStream(trackId, transformId); + if (!sptr) { + throw FailedToOpenOutputStream(trackId, transformId); + } + + DataId did(trackId, transform); + + if (m_data.find(did) == m_data.end()) { + identifyTask(transform); + m_streamTracks[sptr].insert(trackId); + m_streamTasks[sptr].insert(m_tasks[transformId]); + m_streamData[sptr].insert(did); + } + + QString d = m_data[did]; + + for (int i = 0; i < int(features.size()); ++i) { + + if (d != "") { + d += ",\n"; + } + + d += " { "; + + Plugin::Feature f(features[i]); + + if (f.hasDuration) { + d += QString + ("\"start\": { \"value\": %1 }, " + "\"end\": { \"value\": %2 }") + .arg(realTime2Sec(f.timestamp)) + .arg(realTime2Sec + (f.timestamp + + (f.hasDuration ? f.duration : Vamp::RealTime::zeroTime))); + } else { + d += QString("\"time\": { \"value\": %1 }") + .arg(realTime2Sec(f.timestamp)); + } + + if (f.label != "") { + d += QString(", \"label\": { \"value\": \"%2\" }") + .arg(f.label.c_str()); + } + + if (f.values.size() > 0) { + d += QString(", \"value\": [ "); + for (int j = 0; j < int(f.values.size()); ++j) { + if (isnan(f.values[j])) { + d += "\"NaN\" "; + } else if (isinf(f.values[j])) { + d += "\"Inf\" "; + } else { + d += QString("%1 ").arg(f.values[j]); + } + } + d += "]"; + } + + d += " }"; + } + + m_data[did] = d; +} + +void +JAMSFeatureWriter::setNofM(int n, int m) +{ + if (m_singleFileName != "" || m_stdout) { + m_n = n; + m_m = m; + } else { + m_n = 1; + m_m = 1; + } +} + +void +JAMSFeatureWriter::finish() +{ + for (FileStreamMap::const_iterator stri = m_streams.begin(); + stri != m_streams.end(); ++stri) { + + QTextStream *sptr = stri->second; + QTextStream &stream = *sptr; + + bool firstInStream = true; + + for (TrackIds::const_iterator tri = m_streamTracks[sptr].begin(); + tri != m_streamTracks[sptr].end(); ++tri) { + + TrackId trackId = *tri; + + if (firstInStream) { + if (m_streamTracks[sptr].size() > 1 || (m_m > 1 && m_n == 1)) { + stream << "[\n"; + } + } + + if (!firstInStream || (m_m > 1 && m_n > 1)) { + stream << ",\n"; + } + + stream << "{\n" + << QString("\"file_metadata\": {\n" + " \"filename\": \"%1\"") + .arg(QFileInfo(trackId).fileName()); + + if (m_trackMetadata.find(trackId) != m_trackMetadata.end()) { + if (m_trackMetadata[trackId].maker != "") { + stream << QString(",\n \"artist\": \"%1\"") + .arg(m_trackMetadata[trackId].maker); + } + if (m_trackMetadata[trackId].title != "") { + stream << QString(",\n \"title\": \"%1\"") + .arg(m_trackMetadata[trackId].title); + } + } + + stream << "\n},\n"; + + bool firstInTrack = true; + + for (Tasks::const_iterator ti = m_streamTasks[sptr].begin(); + ti != m_streamTasks[sptr].end(); ++ti) { + + 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) { + + DataId did = *di; + + QString trackId = did.first; + Transform transform = did.second; + + if (m_tasks[transform.getIdentifier()] != task) continue; + + QString data = m_data[did]; + + if (!firstInTask) { + stream << ",\n"; + } + + stream << QString + ("{ \n" + " \"annotation_metadata\": {\n" + " \"annotation_tools\": \"Sonic Annotator v%2\",\n" + " \"data_source\": \"Automatic feature extraction\",\n" + " \"annotator\": {\n" + "%3" + " }\n" + " },\n" + " \"data\": [\n") + .arg(RUNNER_VERSION) + .arg(writeTransformToObjectContents(transform)); + + stream << data; + + stream << "\n ]\n}"; + firstInTask = false; + } + + stream << "\n]"; + firstInTrack = false; + } + + stream << "\n}"; + firstInStream = false; + } + + if (!firstInStream) { + if (m_streamTracks[sptr].size() > 1 || (m_m > 1 && m_n == m_m)) { + stream << "\n]"; + } + stream << "\n"; + } + } + + m_streamTracks.clear(); + m_streamTasks.clear(); + m_streamData.clear(); + m_data.clear(); + + FileFeatureWriter::finish(); +} + +void +JAMSFeatureWriter::loadRDFDescription(const Transform &transform) +{ + QString pluginId = transform.getPluginIdentifier(); + if (m_rdfDescriptions.find(pluginId) != m_rdfDescriptions.end()) return; + + if (m_network && !m_networkRetrieved) { + PluginRDFIndexer::getInstance()->indexConfiguredURLs(); + m_networkRetrieved = true; + } + + m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId); + + if (m_rdfDescriptions[pluginId].haveDescription()) { + cerr << "NOTE: Have RDF description for plugin ID \"" + << pluginId << "\"" << endl; + } else { + cerr << "NOTE: No RDF description for plugin ID \"" + << pluginId << "\"" << endl; + if (!m_network) { + cerr << " Consider using the --json-network option to retrieve plugin descriptions" << endl; + cerr << " from the network where possible." << endl; + } + } +} + +void +JAMSFeatureWriter::identifyTask(const Transform &transform) +{ + QString transformId = transform.getIdentifier(); + if (m_tasks.find(transformId) != m_tasks.end()) return; + + loadRDFDescription(transform); + + Task task = UnknownTask; + + QString pluginId = transform.getPluginIdentifier(); + QString outputId = transform.getOutput(); + + const PluginRDFDescription &desc = m_rdfDescriptions[pluginId]; + + if (desc.haveDescription()) { + + PluginRDFDescription::OutputDisposition disp = + desc.getOutputDisposition(outputId); + + QString af = "http://purl.org/ontology/af/"; + + if (disp == PluginRDFDescription::OutputSparse) { + + QString eventUri = desc.getOutputEventTypeURI(outputId); + + //!!! todo: allow user to prod writer for task type + + if (eventUri == af + "Note") { + task = NoteTask; + } else if (eventUri == af + "Beat") { + task = BeatTask; + } else if (eventUri == af + "ChordSegment") { + task = ChordTask; + } else if (eventUri == af + "KeyChange") { + task = KeyTask; + } else if (eventUri == af + "KeySegment") { + task = KeyTask; + } else if (eventUri == af + "Onset") { + task = OnsetTask; + } else if (eventUri == af + "NonTonalOnset") { + task = OnsetTask; + } else if (eventUri == af + "Segment") { + task = SegmentTask; + } else if (eventUri == af + "SpeechSegment") { + task = SegmentTask; + } else if (eventUri == af + "StructuralSegment") { + task = SegmentTask; + } else { + cerr << "WARNING: Unsupported event type URI <" + << eventUri << ">, proceeding with UnknownTask type" + << endl; + } + + } else { + + cerr << "WARNING: Cannot currently write dense or track-level outputs to JSON format (only sparse ones). Will proceed using UnknownTask type, but this probably isn't going to work" << endl; + } + } + + m_tasks[transformId] = task; +} + +QString +JAMSFeatureWriter::getTaskKey(Task task) +{ + switch (task) { + case UnknownTask: return "unknown"; + case BeatTask: return "beat"; + case OnsetTask: return "onset"; + case ChordTask: return "chord"; + case SegmentTask: return "segment"; + case KeyTask: return "key"; + case NoteTask: return "note"; + case MelodyTask: return "melody"; + case PitchTask: return "pitch"; + } + return "unknown"; +} + +QString +JAMSFeatureWriter::writeTransformToObjectContents(const Transform &t) +{ + QString json; + QString stpl(" \"%1\": \"%2\",\n"); + QString ntpl(" \"%1\": %2,\n"); + + json += stpl.arg("plugin_id").arg(t.getPluginIdentifier()); + json += stpl.arg("output_id").arg(t.getOutput()); + + if (t.getSummaryType() != Transform::NoSummary) { + json += stpl.arg("summary_type") + .arg(Transform::summaryTypeToString(t.getSummaryType())); + } + + if (t.getPluginVersion() != QString()) { + json += stpl.arg("plugin_version").arg(t.getPluginVersion()); + } + + if (t.getProgram() != QString()) { + json += stpl.arg("program").arg(t.getProgram()); + } + + if (t.getStepSize() != 0) { + json += ntpl.arg("step_size").arg(t.getStepSize()); + } + + if (t.getBlockSize() != 0) { + json += ntpl.arg("block_size").arg(t.getBlockSize()); + } + + if (t.getWindowType() != HanningWindow) { + json += stpl.arg("window_type") + .arg(Window::getNameForType(t.getWindowType()).c_str()); + } + + if (t.getStartTime() != RealTime::zeroTime) { + json += ntpl.arg("start").arg(t.getStartTime().toDouble()); + } + + if (t.getDuration() != RealTime::zeroTime) { + json += ntpl.arg("duration").arg(t.getDuration().toDouble()); + } + + if (t.getSampleRate() != 0) { + json += ntpl.arg("sample_rate").arg(t.getSampleRate()); + } + + if (!t.getParameters().empty()) { + json += QString(" \"parameters\": {\n"); + Transform::ParameterMap parameters = t.getParameters(); + for (Transform::ParameterMap::const_iterator i = parameters.begin(); + i != parameters.end(); ++i) { + if (i != parameters.begin()) { + json += ",\n"; + } + QString name = i->first; + float value = i->second; + json += QString(" \"%1\": %2").arg(name).arg(value); + } + json += QString("\n },\n"); + } + + // no trailing comma on final property: + json += QString(" \"transform_id\": \"%1\"\n").arg(t.getIdentifier()); + + return json; +} + diff -r f4f770b4356b -r c1834a31029c runner/JAMSFeatureWriter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/JAMSFeatureWriter.h Wed Oct 15 16:09:47 2014 +0100 @@ -0,0 +1,105 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Annotator + A utility for batch feature extraction from audio files. + + Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London. + Copyright 2007-2014 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _JAMS_FEATURE_WRITER_H_ +#define _JAMS_FEATURE_WRITER_H_ + +#include "transform/FileFeatureWriter.h" + +#include "rdf/PluginRDFDescription.h" + +class JAMSFileWriter; + +class JAMSFeatureWriter : public FileFeatureWriter +{ +public: + JAMSFeatureWriter(); + virtual ~JAMSFeatureWriter(); + + string getDescription() const; + + virtual ParameterList getSupportedParameters() const; + virtual void setParameters(map ¶ms); + + virtual void setTrackMetadata(QString trackid, TrackMetadata metadata); + + virtual void setNofM(int, int); + + virtual void write(QString trackid, + const Transform &transform, + const Vamp::Plugin::OutputDescriptor &output, + const Vamp::Plugin::FeatureList &features, + std::string summaryType = ""); + + virtual void finish(); + + virtual QString getWriterTag() const { return "json"; } + +private: + enum Task { + UnknownTask, + BeatTask, + OnsetTask, + ChordTask, + SegmentTask, + KeyTask, + NoteTask, + MelodyTask, + PitchTask, + }; + + typedef map RDFDescriptionMap; // by plugin id + RDFDescriptionMap m_rdfDescriptions; + + typedef QString TrackId; + typedef pair DataId; + + typedef map TrackMetadataMap; + TrackMetadataMap m_trackMetadata; + + typedef set TrackIds; + typedef map StreamTrackMap; + StreamTrackMap m_streamTracks; + + typedef set Tasks; + typedef map StreamTaskMap; + StreamTaskMap m_streamTasks; + + typedef set DataIds; + typedef map StreamDataMap; + StreamDataMap m_streamData; + + typedef map DataMap; + DataMap m_data; + + typedef map TaskMap; + TaskMap m_tasks; + + void loadRDFDescription(const Transform &); + void identifyTask(const Transform &); + + QString getTaskKey(Task); + + QString writeTransformToObjectContents(const Transform &); + + bool m_network; + bool m_networkRetrieved; + int m_n; + int m_m; +}; + +#endif + diff -r f4f770b4356b -r c1834a31029c runner/MIDIFeatureWriter.cpp --- a/runner/MIDIFeatureWriter.cpp Wed Oct 15 13:06:27 2014 +0100 +++ b/runner/MIDIFeatureWriter.cpp Wed Oct 15 16:09:47 2014 +0100 @@ -165,5 +165,9 @@ } } } + + m_notes.clear(); + + FileFeatureWriter::finish(); } diff -r f4f770b4356b -r c1834a31029c runner/main.cpp --- a/runner/main.cpp Wed Oct 15 13:06:27 2014 +0100 +++ b/runner/main.cpp Wed Oct 15 16:09:47 2014 +0100 @@ -939,17 +939,25 @@ } if (multiplex) { try { + for (int i = 0; i < (int)writers.size(); ++i) { + writers[i]->setNofM(1, 1); + } manager.extractFeaturesMultiplexed(goodSources); } catch (const std::exception &e) { cerr << "ERROR: Feature extraction failed: " << e.what() << endl; } } else { + int n = 0; for (QStringList::const_iterator i = goodSources.begin(); i != goodSources.end(); ++i) { std::cerr << "Extracting features for: \"" << i->toStdString() << "\"" << std::endl; + ++n; try { + for (int j = 0; j < (int)writers.size(); ++j) { + writers[j]->setNofM(n, goodSources.size()); + } manager.extractFeatures(*i); } catch (const std::exception &e) { cerr << "ERROR: Feature extraction failed for \"" diff -r f4f770b4356b -r c1834a31029c tests/test-json-destinations/test-json-destinations.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-json-destinations/test-json-destinations.sh Wed Oct 15 16:09:47 2014 +0100 @@ -0,0 +1,192 @@ +#!/bin/bash + +. ../include.sh + +infile1=$audiopath/3clicks8.wav +infile2=$audiopath/6clicks8.wav + +infile1dot=$audiopath/3.clicks.8.wav + +outfile1=3clicks8.json +outfile2=6clicks8.json + +outfile3=3clicks8_vamp_vamp-example-plugins_percussiononsets_onsets.json +outfile4=3clicks8_vamp_vamp-example-plugins_percussiononsets_detectionfunction.json +outfile5=6clicks8_vamp_vamp-example-plugins_percussiononsets_onsets.json +outfile6=6clicks8_vamp_vamp-example-plugins_percussiononsets_detectionfunction.json + +outfile1dot=3.clicks.8.json + +tmpjson=$mypath/tmp_1_$$.json + +trap "rm -f $tmpjson $outfile1 $outfile2 $outfile3 $outfile4 $outfile5 $outfile6 $infile1dot $outfile1dot $audiopath/$outfile1 $audiopath/$outfile2 $audiopath/$outfile3 $audiopath/$outfile4 $audiopath/$outfile5 $audiopath/$outfile6 $audiopath/$outfile1dot" 0 + +transformdir=$mypath/transforms + +failshow() { + echo "Test failed: $1" + if [ -n "$2" ]; then + echo "Output follows:" + echo "--" + cat $2 + echo "--" + fi + exit 1 +} + +check_json() { + test -f $1 || \ + fail "Fails to write output to expected location $1 for $2" + cat $1 | json_verify -q || \ + failshow "Writes invalid JSON to location $1 for $2" $1 + rm -f $1 +} + + +ctx="onsets transform, one audio file, default JSON writer destination" + +rm -f $audiopath/$outfile1 + +$r -t $transformdir/onsets.n3 -w json $infile1 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json $audiopath/$outfile1 "$ctx" + + +ctx="onsets transform, one audio file with dots in filename, default JSON writer destination" + +rm -f $audiopath/$outfile1 + +cp $infile1 $infile1dot + +$r -t $transformdir/onsets.n3 -w json $infile1dot 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json $audiopath/$outfile1dot "$ctx" + +rm -f $infile1dot $audiopath/$outfile1dot + + +ctx="onsets and df transforms, one audio file, default JSON writer destination" + +rm -f $audiopath/$outfile1 + +$r -t $transformdir/onsets.n3 -t $transformdir/detectionfunction.n3 -w json $infile1 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json $audiopath/$outfile1 "$ctx" + + +ctx="onsets transform, two audio files, default JSON writer destination" + +rm -f $audiopath/$outfile1 +rm -f $audiopath/$outfile2 + +$r -t $transformdir/onsets.n3 -w json $infile1 $infile2 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json $audiopath/$outfile1 "$ctx" +check_json $audiopath/$outfile2 "$ctx" + + +ctx="onsets transform, two audio files, one-file JSON writer" + +$r -t $transformdir/onsets.n3 -w json --json-one-file $tmpjson $infile1 $infile2 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json $tmpjson "$ctx" + + +ctx="onsets transform, two audio files, stdout JSON writer" + +$r -t $transformdir/onsets.n3 -w json --json-stdout $infile1 $infile2 2>/dev/null >$tmpjson || \ + fail "Fails to run with $ctx" + +check_json $tmpjson "$ctx" + + +ctx="onsets transform, one audio file, many-files JSON writer" + +rm -f $audiopath/$outfile3 + +$r -t $transformdir/onsets.n3 -w json --json-many-files $infile1 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json $audiopath/$outfile3 "$ctx" + + +ctx="onsets transform, two audio files, many-files JSON writer" + +rm -f $audiopath/$outfile3 +rm -f $audiopath/$outfile5 + +$r -t $transformdir/onsets.n3 -w json --json-many-files $infile1 $infile2 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json $audiopath/$outfile3 "$ctx" +check_json $audiopath/$outfile5 "$ctx" + + +ctx="onsets and df transforms, two audio files, many-files JSON writer" + +rm -f $audiopath/$outfile3 +rm -f $audiopath/$outfile4 +rm -f $audiopath/$outfile5 +rm -f $audiopath/$outfile6 + +$r -t $transformdir/onsets.n3 -t $transformdir/detectionfunction.n3 -w json --json-many-files $infile1 $infile2 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json $audiopath/$outfile3 "$ctx" +check_json $audiopath/$outfile4 "$ctx" +check_json $audiopath/$outfile5 "$ctx" +check_json $audiopath/$outfile6 "$ctx" + + +ctx="output base directory" + +rm -f ./$outfile1 + +$r -t $transformdir/onsets.n3 -t $transformdir/detectionfunction.n3 -w json --json-basedir . $infile1 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json ./$outfile1 "$ctx" + + +ctx="output base directory and many-files" + +rm -f ./$outfile3 +rm -f ./$outfile5 + +$r -t $transformdir/onsets.n3 -w json --json-basedir . --json-many-files $infile1 $infile2 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json ./$outfile3 "$ctx" +check_json ./$outfile5 "$ctx" + + +ctx="nonexistent output base directory" + +$r -t $transformdir/onsets.n3 -w json --json-basedir ./DOES_NOT_EXIST $infile1 2>/dev/null && \ + fail "Fails with $ctx by completing successfully (should refuse and bail out)" + + +ctx="existing output file and no --json-force" + +touch $audiopath/$outfile1 + +$r -t $transformdir/onsets.n3 -w json $infile1 2>/dev/null && \ + fail "Fails by completing successfully when output file already exists (should refuse and bail out)" + + +ctx="existing output file and --json-force" + +touch $audiopath/$outfile1 + +$r -t $transformdir/onsets.n3 -w json --json-force $infile1 2>/dev/null || \ + fail "Fails to run with $ctx" + +check_json $audiopath/$outfile1 "$ctx" + + +exit 0 diff -r f4f770b4356b -r c1834a31029c tests/test-json-destinations/transforms/detectionfunction.n3 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-json-destinations/transforms/detectionfunction.n3 Wed Oct 15 16:09:47 2014 +0100 @@ -0,0 +1,11 @@ +@prefix rdf: . +@prefix vamp: . +@prefix examples: . +@prefix : <#>. + +:transform0 a vamp:Transform; + vamp:plugin examples:percussiononsets ; + vamp:output examples:percussiononsets_output_detectionfunction . + + + diff -r f4f770b4356b -r c1834a31029c tests/test-json-destinations/transforms/onsets.n3 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-json-destinations/transforms/onsets.n3 Wed Oct 15 16:09:47 2014 +0100 @@ -0,0 +1,10 @@ +@prefix rdf: . +@prefix vamp: . +@prefix examples: . +@prefix : <#>. + +:transform0 a vamp:Transform; + vamp:plugin examples:percussiononsets. + + + diff -r f4f770b4356b -r c1834a31029c tests/test-supportprogs/test-supportprogs.sh --- a/tests/test-supportprogs/test-supportprogs.sh Wed Oct 15 13:06:27 2014 +0100 +++ b/tests/test-supportprogs/test-supportprogs.sh Wed Oct 15 16:09:47 2014 +0100 @@ -11,5 +11,8 @@ rapper --version >/dev/null || \ fail "Can't find required rapper program" +echo '{}' | json_verify >/dev/null || \ + fail "Can't find required json_verify program, or it doesn't seem to work" + exit 0 diff -r f4f770b4356b -r c1834a31029c tests/test.sh --- a/tests/test.sh Wed Oct 15 13:06:27 2014 +0100 +++ b/tests/test.sh Wed Oct 15 16:09:47 2014 +0100 @@ -16,6 +16,7 @@ lab-writer \ lab-destinations \ midi-destinations \ + json-destinations \ summaries \ multiple-audio \ ; do