Mercurial > hg > sonic-annotator
changeset 1:92911f967a16
* some reorganisation
author | Chris Cannam |
---|---|
date | Thu, 11 Dec 2008 10:26:12 +0000 |
parents | 581b1b150a4d |
children | 475f4623feba |
files | AudioDBFeatureWriter.cpp AudioDBFeatureWriter.h DefaultFeatureWriter.cpp DefaultFeatureWriter.h FeatureExtractionManager.cpp FeatureExtractionManager.h FeatureWriterFactory.cpp FeatureWriterFactory.h deploy_mac.sh main.cpp pull-svn.sh qm-keydetector.n3 runner.pro runner/AudioDBFeatureWriter.cpp runner/AudioDBFeatureWriter.h runner/DefaultFeatureWriter.cpp runner/DefaultFeatureWriter.h runner/FeatureExtractionManager.cpp runner/FeatureExtractionManager.h runner/FeatureWriterFactory.cpp runner/FeatureWriterFactory.h runner/main.cpp runner/runner.pro test-queries/test-query test-queries/test-query-dense-output test-queries/test-query-plugin-for-transform test-queries/test-query-plugin-output-types test-queries/test-query-pluginid-for-rdf test-queries/test-query-transform-params test-queries/test-query-transforms test-queries/test-roqet tests/misc-queries/test-query tests/misc-queries/test-query-dense-output tests/misc-queries/test-query-plugin-for-transform tests/misc-queries/test-query-plugin-output-types tests/misc-queries/test-query-pluginid-for-rdf tests/misc-queries/test-query-transform-params tests/misc-queries/test-query-transforms tests/misc-queries/test-roqet vamp-example-plugins:percussiononsets.n3 |
diffstat | 40 files changed, 2238 insertions(+), 2406 deletions(-) [+] |
line wrap: on
line diff
--- a/AudioDBFeatureWriter.cpp Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,214 +0,0 @@ -/* -*- 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-2008 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 <fstream> - -#include <QFileInfo> - -#include "AudioDBFeatureWriter.h" - -using namespace std; -using namespace Vamp; - -string -AudioDBFeatureWriter::catalogueIdParam = "catid"; - -string -AudioDBFeatureWriter::baseDirParam = "basedir"; - -struct AudioDBFeatureWriter::TrackStream -{ - QString trackid; - ofstream* ofs; -}; - -AudioDBFeatureWriter::AudioDBFeatureWriter() : - catalogueId("catalog"), baseDir("audiodb") -{ - -} - -AudioDBFeatureWriter::~AudioDBFeatureWriter() -{ - // close all open files - for (map<string, TrackStream>::iterator iter = dbfiles.begin(); iter != dbfiles.end(); ++iter) - { - if (iter->second.ofs) { - iter->second.ofs->close(); - delete iter->second.ofs; - } - } - - // TODO: error handling on close -} - -AudioDBFeatureWriter::ParameterList -AudioDBFeatureWriter::getSupportedParameters() const -{ - ParameterList pl; - Parameter p; - - p.name = catalogueIdParam; - p.description = "Catalogue ID"; - p.hasArg = true; - pl.push_back(p); - - p.name = baseDirParam; - p.description = "Base output directory path"; - p.hasArg = true; - pl.push_back(p); - - return pl; -} - -void -AudioDBFeatureWriter::setParameters(map<string, string> ¶ms) -{ - if (params.find(catalogueIdParam) != params.end()) { - setCatalogueId(params[catalogueIdParam]); - params.erase(catalogueIdParam); - } - if (params.find(baseDirParam) != params.end()) { - setBaseDirectory(params[baseDirParam]); - params.erase(baseDirParam); - } -} - -void -AudioDBFeatureWriter::setCatalogueId(const string &catid) -{ - catalogueId = catid; -} - -void -AudioDBFeatureWriter::setBaseDirectory(const string &base) -{ - baseDir = base; -} - -void AudioDBFeatureWriter::write(QString trackid, - const Transform &transform, - const Vamp::Plugin::OutputDescriptor& output, - const Vamp::Plugin::FeatureList& featureList, - std::string summaryType) -{ - //!!! use summaryType - if (summaryType != "") { - //!!! IMPLEMENT - cerr << "ERROR: AudioDBFeatureWriter::write: Writing summaries is not yet implemented!" << endl; - exit(1); - } - - - // binary output for FeatureSet - - // feature-dimension feature-1 feature-2 ... - // timestamp-1 timestamp-2 ... - - // audioDB has to write each feature to a different file - // assume a simple naming convention of - // <catalog-id>/<track-id>.<feature-id> - // with timestamps in a corresponding <catalog-id>/<track-id>.<feature-id>.timestamp file - // (start and end times in seconds for each frame -- somewhat optional) - - // the feature writer holds a map of open file descriptors - // the catalog-id is passed in to the feature writer's constructor - - // NB -- all "floats" in the file should in fact be doubles - - // TODO: - // - write feature end rather than start times, once end time is available in vamp - // - write a power file, probably by wrapping plugin in a PluginPowerAdapter :) - - if (output.binCount == 0) // this kind of feature just outputs timestamps and labels, assume of no interest to audioDB - return; - - for (int i = 0; i < featureList.size(); ++i) - { - // replace output files if necessary - if (replaceDBFile(trackid, output.identifier)) - { - // write the feature length for the next track feature record - // binCount has to be set - // - it can be zero, i.e. if the output is really a set of labels + timestamps - *dbfiles[output.identifier].ofs /*<< ios::binary*/ << output.binCount; - - cerr << "writing bin count " << output.binCount << " for " << output.identifier << endl; - } - - if (replaceDBFile(trackid, output.identifier + ".timestamp")) - { - // write the start time to the timestamp file - // as we want it for the first feature in the file - *dbfiles[output.identifier + ".timestamp"].ofs << featureList[i].timestamp.toString() << endl; - } - - if (dbfiles[output.identifier].ofs) { - for (int j = 0; j < featureList[i].values.size(); ++j) - *dbfiles[output.identifier].ofs /*<< ios::binary*/ << featureList[i].values[j]; - - // write the *end* time of each feature to the timestamp file - // NOT IMPLEMENTED YET -// *dbfiles[output.identifier + ".timestamp"].ofs << featureList[i].timestamp.toString() << endl; - } - } -} - -bool AudioDBFeatureWriter::openDBFile(QString trackid, const string& identifier) -{ - QString trackBase = QFileInfo(trackid).fileName(); - string filepath = baseDir + "/" + catalogueId + "/" - + trackBase.toStdString() + "." + identifier; - cerr << "AudioDBFeatureWriter::openDBFile: filepath is \"" << filepath << "\"" << endl; - ofstream* ofs = new ofstream(filepath.c_str()); - if (!*ofs) - { - cerr << "ERROR AudioDBFeatureWriter::openDBFile(): can't open file " << filepath << endl; - return false; - } - TrackStream ts; - ts.trackid = trackid; - ts.ofs = ofs; - dbfiles[identifier] = ts; - return true; -} - -// replace file if no file open for this track, else return false -bool AudioDBFeatureWriter::replaceDBFile(QString trackid, - const string& identifier) -{ - if (dbfiles.find(identifier) != dbfiles.end() && dbfiles[identifier].trackid == trackid) - return false; // have an open file for this track - - if (dbfiles.find(identifier) != dbfiles.end() && dbfiles[identifier].trackid != trackid) - { - // close the current file - if (dbfiles[identifier].ofs) { - dbfiles[identifier].ofs->close(); - delete dbfiles[identifier].ofs; - dbfiles[identifier].ofs = 0; - } - } - - // open a new file - if (!openDBFile(trackid, identifier)) { - dbfiles[identifier].ofs = 0; - return false; //!!! should throw an exception, otherwise we'll try to open the file again and again every time we want to write to it - } - - return true; -} - -
--- a/AudioDBFeatureWriter.h Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* -*- 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-2008 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 _AUDIO_DB_FEATURE_WRITER_H_ -#define _AUDIO_DB_FEATURE_WRITER_H_ - -#include <string> -#include <map> - -using std::string; -using std::map; - -#include "transform/FeatureWriter.h" - -class AudioDBFeatureWriter : public FeatureWriter -{ -public: - AudioDBFeatureWriter(); - virtual ~AudioDBFeatureWriter(); - - virtual ParameterList getSupportedParameters() const; - virtual void setParameters(map<string, string> ¶ms); - - virtual void setCatalogueId(const string &); - virtual void setBaseDirectory(const string &); - - virtual void write(QString trackid, - const Transform &transform, - const Vamp::Plugin::OutputDescriptor &output, - const Vamp::Plugin::FeatureList &features, - std::string summaryType = ""); - - virtual void finish() { } - -private: - string catalogueId; - string baseDir; - - static string catalogueIdParam; - static string baseDirParam; - - struct TrackStream; - map<string, TrackStream> dbfiles; - - bool openDBFile(QString trackid, const string& identifier); - bool replaceDBFile(QString trackid, const string& identifier); -}; - -#endif
--- a/DefaultFeatureWriter.cpp Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/* -*- 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-2008 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 <iostream> -#include <map> - -using namespace std; - -#include "DefaultFeatureWriter.h" - -void DefaultFeatureWriter::write(QString trackid, - const Transform &transform, - const Vamp::Plugin::OutputDescriptor& output, - const Vamp::Plugin::FeatureList& featureList, - std::string summaryType) -{ - // generic XML output - - /* - - <feature> - <name>output.name</name> - <timestamp>feature.timestamp</timestamp> - <values>output.binName[0]:feature.value[0]...</values> - <label>feature.label</label> - </feature> - - */ - - for (int i = 0; i < featureList.size(); ++i) - { - if (summaryType == "") { - cout << "<feature>" << endl; - } else { - cout << "<summary type=\"" << summaryType << "\">" << endl; - } - cout << "\t<name>" << output.name << "</name>" << endl; - if (featureList[i].hasTimestamp) { - cout << "\t<timestamp>" << featureList[i].timestamp << "</timestamp>" << endl; - } - if (featureList[i].hasDuration) { - cout << "\t<duration>" << featureList[i].duration << "</duration>" << endl; - } - if (featureList[i].values.size() > 0) - { - cout << "\t<values>"; - for (int j = 0; j < featureList[i].values.size(); ++j) - { - if (j > 0) - cout << " "; - if (output.binNames.size() > 0) - cout << output.binNames[j] << ":"; - cout << featureList[i].values[j]; - } - cout << "</values>" << endl; - } - if (featureList[i].label.length() > 0) - cout << "\t<label>" << featureList[i].label << "</label>" << endl; - if (summaryType == "") { - cout << "</feature>" << endl; - } else { - cout << "</summary>" << endl; - } - } -}
--- a/DefaultFeatureWriter.h Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* -*- 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-2008 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 _DEFAULT_FEATURE_WRITER_H_ -#define _DEFAULT_FEATURE_WRITER_H_ - - -#include "transform/FeatureWriter.h" - -class DefaultFeatureWriter : public FeatureWriter -{ -public: - virtual ~DefaultFeatureWriter() { } - virtual void write(QString trackid, - const Transform &transform, - const Vamp::Plugin::OutputDescriptor &output, - const Vamp::Plugin::FeatureList &features, - std::string summaryType = ""); - virtual void finish() { } -}; - -#endif
--- a/FeatureExtractionManager.cpp Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,702 +0,0 @@ -/* -*- 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-2008 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 "FeatureExtractionManager.h" - -#include <vamp-hostsdk/PluginChannelAdapter.h> -#include <vamp-hostsdk/PluginBufferingAdapter.h> -#include <vamp-hostsdk/PluginInputDomainAdapter.h> -#include <vamp-hostsdk/PluginSummarisingAdapter.h> -#include <vamp-hostsdk/PluginLoader.h> - -#include <iostream> - -using namespace std; - -using Vamp::Plugin; -using Vamp::PluginBase; -using Vamp::HostExt::PluginLoader; -using Vamp::HostExt::PluginChannelAdapter; -using Vamp::HostExt::PluginBufferingAdapter; -using Vamp::HostExt::PluginInputDomainAdapter; -using Vamp::HostExt::PluginSummarisingAdapter; - -#include "data/fileio/FileSource.h" -#include "data/fileio/AudioFileReader.h" -#include "data/fileio/AudioFileReaderFactory.h" -#include "data/fileio/PlaylistFileReader.h" -#include "base/TempDirectory.h" -#include "base/ProgressPrinter.h" -#include "transform/TransformFactory.h" -#include "rdf/RDFTransformFactory.h" -#include "transform/FeatureWriter.h" - -#include <QTextStream> -#include <QFile> -#include <QFileInfo> - -FeatureExtractionManager::FeatureExtractionManager() : - m_summariesOnly(false), - // We can read using an arbitrary fixed block size -- - // PluginBufferingAdapter handles this for us. It's likely to be - // quicker to use larger sizes than smallish ones like 1024 - m_blockSize(16384), - m_defaultSampleRate(0), - m_sampleRate(0), - m_channels(1) -{ -} - -FeatureExtractionManager::~FeatureExtractionManager() -{ - for (PluginMap::iterator pi = m_plugins.begin(); - pi != m_plugins.end(); ++pi) { - delete pi->first; - } -} - -void FeatureExtractionManager::setChannels(int channels) -{ - m_channels = channels; -} - -void FeatureExtractionManager::setDefaultSampleRate(int sampleRate) -{ - m_defaultSampleRate = sampleRate; -} - -static PluginSummarisingAdapter::SummaryType -getSummaryType(string name) -{ - if (name == "min") return PluginSummarisingAdapter::Minimum; - if (name == "max") return PluginSummarisingAdapter::Maximum; - if (name == "mean") return PluginSummarisingAdapter::Mean; - if (name == "median") return PluginSummarisingAdapter::Median; - if (name == "mode") return PluginSummarisingAdapter::Mode; - if (name == "sum") return PluginSummarisingAdapter::Sum; - if (name == "variance") return PluginSummarisingAdapter::Variance; - if (name == "sd") return PluginSummarisingAdapter::StandardDeviation; - if (name == "count") return PluginSummarisingAdapter::Count; - return PluginSummarisingAdapter::UnknownSummaryType; -} - -bool FeatureExtractionManager::setSummaryTypes(const set<string> &names, - bool summariesOnly, - const PluginSummarisingAdapter::SegmentBoundaries &boundaries) -{ - for (SummaryNameSet::const_iterator i = names.begin(); - i != names.end(); ++i) { - if (getSummaryType(*i) == PluginSummarisingAdapter::UnknownSummaryType) { - cerr << "ERROR: Unknown summary type \"" << *i << "\"" << endl; - return false; - } - } - m_summaries = names; - m_summariesOnly = summariesOnly; - m_boundaries = boundaries; - return true; -} - -bool FeatureExtractionManager::addFeatureExtractor -(Transform transform, const vector<FeatureWriter*> &writers) -{ - //!!! exceptions rather than return values? - - if (transform.getSampleRate() == 0) { - if (m_sampleRate == 0) { - cerr << "NOTE: Transform does not specify a sample rate, using default rate of " << m_defaultSampleRate << endl; - transform.setSampleRate(m_defaultSampleRate); - m_sampleRate = m_defaultSampleRate; - } else { - cerr << "NOTE: Transform does not specify a sample rate, using previous transform's rate of " << m_sampleRate << endl; - transform.setSampleRate(m_sampleRate); - } - } - - if (m_sampleRate == 0) { - m_sampleRate = transform.getSampleRate(); - } - - if (transform.getSampleRate() != m_sampleRate) { - cerr << "WARNING: Transform sample rate " << transform.getSampleRate() << " does not match previously specified transform rate of " << m_sampleRate << " -- only a single rate is supported for each run" << endl; - cerr << "WARNING: Using previous rate of " << m_sampleRate << " for this transform as well" << endl; - transform.setSampleRate(m_sampleRate); - } - - Plugin *plugin = 0; - - // Remember what the original transform looked like, and index - // based on this -- because we may be about to fill in the zeros - // for step and block size, but we want any further copies with - // the same zeros to match this one - Transform originalTransform = transform; - - if (m_transformPluginMap.find(transform) == m_transformPluginMap.end()) { - - // Test whether we already have a transform that is identical - // to this, except for the output requested and/or the summary - // type -- if so, they should share plugin instances (a vital - // optimisation) - - for (TransformPluginMap::iterator i = m_transformPluginMap.begin(); - i != m_transformPluginMap.end(); ++i) { - Transform test = i->first; - test.setOutput(transform.getOutput()); - test.setSummaryType(transform.getSummaryType()); - if (transform == test) { - cerr << "NOTE: Already have transform identical to this one (for \"" - << transform.getIdentifier().toStdString() - << "\") in every detail except output identifier and/or " - << "summary type; sharing its plugin instance" << endl; - plugin = i->second; - if (transform.getSummaryType() != Transform::NoSummary && - !dynamic_cast<PluginSummarisingAdapter *>(plugin)) { - plugin = new PluginSummarisingAdapter(plugin); - i->second = plugin; - } - break; - } - } - - if (!plugin) { - - TransformFactory *tf = TransformFactory::getInstance(); - - PluginBase *pb = tf->instantiatePluginFor(transform); - plugin = tf->downcastVampPlugin(pb); - if (!plugin) { - //!!! todo: handle non-Vamp plugins too, or make the main --list - // option print out only Vamp transforms - cerr << "ERROR: Failed to load plugin for transform \"" - << transform.getIdentifier().toStdString() << "\"" << endl; - delete pb; - return false; - } - - // We will provide the plugin with arbitrary step and - // block sizes (so that we can use the same read/write - // block size for all transforms), and to that end we use - // a PluginBufferingAdapter. However, we need to know the - // underlying step size so that we can provide the right - // context for dense outputs. (Although, don't forget - // that the PluginBufferingAdapter rewrites - // OneSamplePerStep outputs so as to use FixedSampleRate - // -- so it supplies the sample rate in the output - // feature. I'm not sure whether we can easily use that.) - - size_t pluginStepSize = plugin->getPreferredStepSize(); - size_t pluginBlockSize = plugin->getPreferredBlockSize(); - - // adapt the plugin for buffering, channels, etc. - if (plugin->getInputDomain() == Plugin::FrequencyDomain) { - plugin = new PluginInputDomainAdapter(plugin); - } - - PluginBufferingAdapter *pba = new PluginBufferingAdapter(plugin); - plugin = pba; - - if (transform.getStepSize() != 0) { - pba->setPluginStepSize(transform.getStepSize()); - } else { - transform.setStepSize(pluginStepSize); - } - - if (transform.getBlockSize() != 0) { - pba->setPluginBlockSize(transform.getBlockSize()); - } else { - transform.setBlockSize(pluginBlockSize); - } - - plugin = new PluginChannelAdapter(plugin); - - if (!m_summaries.empty() || - transform.getSummaryType() != Transform::NoSummary) { - PluginSummarisingAdapter *adapter = - new PluginSummarisingAdapter(plugin); - adapter->setSummarySegmentBoundaries(m_boundaries); - plugin = adapter; - } - - if (!plugin->initialise(m_channels, m_blockSize, m_blockSize)) { - cerr << "ERROR: Plugin initialise (channels = " << m_channels << ", stepSize = " << m_blockSize << ", blockSize = " << m_blockSize << ") failed." << endl; - delete plugin; - return false; - } - -// cerr << "Initialised plugin" << endl; - - size_t actualStepSize = 0; - size_t actualBlockSize = 0; - pba->getActualStepAndBlockSizes(actualStepSize, actualBlockSize); - transform.setStepSize(actualStepSize); - transform.setBlockSize(actualBlockSize); - - Plugin::OutputList outputs = plugin->getOutputDescriptors(); - for (int i = 0; i < (int)outputs.size(); ++i) { - -// cerr << "Newly initialised plugin output " << i << " has bin count " << outputs[i].binCount << endl; - - m_pluginOutputs[plugin][outputs[i].identifier] = outputs[i]; - m_pluginOutputIndices[outputs[i].identifier] = i; - } - - cerr << "NOTE: Loaded and initialised plugin " << plugin - << " for transform \"" - << transform.getIdentifier().toStdString() << "\"" << endl; - } - - if (transform.getOutput() == "") { - transform.setOutput - (plugin->getOutputDescriptors()[0].identifier.c_str()); - } - - m_transformPluginMap[transform] = plugin; - - if (!(originalTransform == transform)) { - m_transformPluginMap[originalTransform] = plugin; - } - - } else { - - plugin = m_transformPluginMap[transform]; - } - - m_plugins[plugin][transform] = writers; - - return true; -} - -bool FeatureExtractionManager::addDefaultFeatureExtractor -(TransformId transformId, const vector<FeatureWriter*> &writers) -{ - TransformFactory *tf = TransformFactory::getInstance(); - - if (m_sampleRate == 0) { - if (m_defaultSampleRate == 0) { - cerr << "ERROR: Default transform requested, but no default sample rate available" << endl; - return false; - } else { - cerr << "NOTE: Using default sample rate of " << m_defaultSampleRate << " for default transform" << endl; - m_sampleRate = m_defaultSampleRate; - } - } - - Transform transform = tf->getDefaultTransformFor(transformId, m_sampleRate); - - return addFeatureExtractor(transform, writers); -} - -bool FeatureExtractionManager::addFeatureExtractorFromFile -(QString transformXmlFile, const vector<FeatureWriter*> &writers) -{ - RDFTransformFactory factory - (QUrl::fromLocalFile(QFileInfo(transformXmlFile).absoluteFilePath()) - .toString()); - ProgressPrinter printer("Parsing transforms RDF file"); - std::vector<Transform> transforms = factory.getTransforms(&printer); - if (!factory.isOK()) { - cerr << "WARNING: FeatureExtractionManager::addFeatureExtractorFromFile: Failed to parse transforms file: " << factory.getErrorString().toStdString() << endl; - if (factory.isRDF()) { - return false; // no point trying it as XML - } - } - if (!transforms.empty()) { - bool success = true; - for (int i = 0; i < (int)transforms.size(); ++i) { - if (!addFeatureExtractor(transforms[i], writers)) { - success = false; - } - } - return success; - } - - QFile file(transformXmlFile); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - cerr << "ERROR: Failed to open transform XML file \"" - << transformXmlFile.toStdString() << "\" for reading" << endl; - return false; - } - - QTextStream *qts = new QTextStream(&file); - QString qs = qts->readAll(); - delete qts; - file.close(); - - Transform transform(qs); - - return addFeatureExtractor(transform, writers); -} - -void FeatureExtractionManager::extractFeatures(QString audioSource) -{ - if (m_plugins.empty()) return; - - ProgressPrinter printer("Retrieving audio data..."); - - FileSource source(audioSource, &printer); - if (!source.isAvailable()) { - cerr << "ERROR: File or URL \"" << audioSource.toStdString() - << "\" could not be located" << endl; - exit(1); - } - - source.waitForData(); - - if (QFileInfo(audioSource).suffix().toLower() == "m3u") { - PlaylistFileReader reader(source); - if (reader.isOK()) { - vector<QString> files = reader.load(); - for (int i = 0; i < (int)files.size(); ++i) { - extractFeatures(files[i]); - } - return; - } else { - cerr << "ERROR: Playlist \"" << audioSource.toStdString() - << "\" could not be opened" << endl; - exit(1); - } - } - - if (m_sampleRate == 0) { - cerr << "ERROR: Internal error in FeatureExtractionManager::extractFeatures: Plugin list is non-empty, but no sample rate set" << endl; - exit(1); - } - - AudioFileReader *reader = - AudioFileReaderFactory::createReader(source, m_sampleRate, &printer); - - if (!reader) { - cerr << "ERROR: File or URL \"" << audioSource.toStdString() - << "\" could not be opened" << endl; - exit(1); - } - - size_t channels = reader->getChannelCount(); - - cerr << "Opened " << channels << "-channel file or URL \"" << audioSource.toStdString() << "\"" << endl; - - // reject file if it has too few channels, plugin will handle if it has too many - if ((int)channels < m_channels) { - //!!! should not be terminating here! - cerr << "ERROR: File or URL \"" << audioSource.toStdString() << "\" has less than " << m_channels << " channels" << endl; - exit(1); - } - - // allocate audio buffers - float **data = new float *[m_channels]; - for (int c = 0; c < m_channels; ++c) { - data[c] = new float[m_blockSize]; - } - - size_t frameCount = reader->getFrameCount(); - -// cerr << "file has " << frameCount << " frames" << endl; - - for (PluginMap::iterator pi = m_plugins.begin(); - pi != m_plugins.end(); ++pi) { - - Plugin *plugin = pi->first; - -// std::cerr << "Calling reset on " << plugin << std::endl; - plugin->reset(); - - for (TransformWriterMap::iterator ti = pi->second.begin(); - ti != pi->second.end(); ++ti) { - - const Transform &transform = ti->first; - - //!!! we may want to set the start and duration times for extraction - // in the transform record (defaults of zero indicate extraction - // from the whole file) -// transform.setStartTime(RealTime::zeroTime); -// transform.setDuration -// (RealTime::frame2RealTime(reader->getFrameCount(), m_sampleRate)); - - string outputId = transform.getOutput().toStdString(); - if (m_pluginOutputs[plugin].find(outputId) == - m_pluginOutputs[plugin].end()) { - //!!! throw? - cerr << "WARNING: Nonexistent plugin output \"" << outputId << "\" requested for transform \"" - << transform.getIdentifier().toStdString() << "\", ignoring this transform" - << endl; -/* - cerr << "Known outputs for all plugins are as follows:" << endl; - for (PluginOutputMap::const_iterator k = m_pluginOutputs.begin(); - k != m_pluginOutputs.end(); ++k) { - cerr << "Plugin " << k->first << ": "; - if (k->second.empty()) { - cerr << "(none)"; - } - for (OutputMap::const_iterator i = k->second.begin(); - i != k->second.end(); ++i) { - cerr << "\"" << i->first << "\" "; - } - cerr << endl; - } -*/ - } - } - } - - long startFrame = 0; - long endFrame = frameCount; - -/*!!! No -- there is no single transform to pull this stuff from -- - * the transforms may have various start and end times, need to be far - * cleverer about this if we're going to support them - - RealTime trStartRT = transform.getStartTime(); - RealTime trDurationRT = transform.getDuration(); - - long trStart = RealTime::realTime2Frame(trStartRT, m_sampleRate); - long trDuration = RealTime::realTime2Frame(trDurationRT, m_sampleRate); - - if (trStart == 0 || trStart < startFrame) { - trStart = startFrame; - } - - if (trDuration == 0) { - trDuration = endFrame - trStart; - } - if (trStart + trDuration > endFrame) { - trDuration = endFrame - trStart; - } - - startFrame = trStart; - endFrame = trStart + trDuration; -*/ - - for (PluginMap::iterator pi = m_plugins.begin(); - pi != m_plugins.end(); ++pi) { - - for (TransformWriterMap::const_iterator ti = pi->second.begin(); - ti != pi->second.end(); ++ti) { - - const vector<FeatureWriter *> &writers = ti->second; - - for (int j = 0; j < (int)writers.size(); ++j) { - FeatureWriter::TrackMetadata m; - m.title = reader->getTitle(); - m.maker = reader->getMaker(); - writers[j]->setTrackMetadata(audioSource, m); - } - } - } - - ProgressPrinter extractionProgress("Extracting and writing features..."); - int progress = 0; - - for (long i = startFrame; i < endFrame; i += m_blockSize) { - - //!!! inefficient, although much of the inefficiency may be - // susceptible to optimisation - - SampleBlock frames; - reader->getInterleavedFrames(i, m_blockSize, frames); - - // We have to do our own channel handling here; we can't just - // leave it to the plugin adapter because the same plugin - // adapter may have to serve for input files with various - // numbers of channels (so the adapter is simply configured - // with a fixed channel count, generally 1). - - int rc = reader->getChannelCount(); - - for (int j = 0; j < m_blockSize; ++j) { - for (int c = 0; c < m_channels; ++c) { - int index; - if (c < rc) { - index = j * rc + c; - data[c][j] = 0.f; - } else { - index = j * rc + (c % rc); - } - if (index < (int)frames.size()) { - data[c][j] += frames[index]; - } - } - } - - Vamp::RealTime timestamp = Vamp::RealTime::frame2RealTime - (i, m_sampleRate); - - for (PluginMap::iterator pi = m_plugins.begin(); - pi != m_plugins.end(); ++pi) { - - Plugin *plugin = pi->first; - Plugin::FeatureSet featureSet = plugin->process(data, timestamp); - - if (!m_summariesOnly) { - writeFeatures(audioSource, plugin, featureSet); - } - } - - int pp = progress; - progress = ((i - startFrame) * 100) / (endFrame - startFrame); - if (progress > pp) extractionProgress.setProgress(progress); - } - - for (PluginMap::iterator pi = m_plugins.begin(); - pi != m_plugins.end(); ++pi) { - - Plugin *plugin = pi->first; - Plugin::FeatureSet featureSet = plugin->getRemainingFeatures(); - - if (!m_summariesOnly) { - writeFeatures(audioSource, plugin, featureSet); - } - - if (!m_summaries.empty()) { - PluginSummarisingAdapter *adapter = - dynamic_cast<PluginSummarisingAdapter *>(plugin); - if (!adapter) { - cerr << "WARNING: Summaries requested, but plugin is not a summarising adapter" << endl; - } else { - for (SummaryNameSet::const_iterator sni = m_summaries.begin(); - sni != m_summaries.end(); ++sni) { - featureSet.clear(); - //!!! problem here -- we are requesting summaries - //!!! for all outputs, but they in principle have - //!!! different averaging requirements depending - //!!! on whether their features have duration or - //!!! not - featureSet = adapter->getSummaryForAllOutputs - (getSummaryType(*sni), - PluginSummarisingAdapter::ContinuousTimeAverage); - writeFeatures(audioSource, plugin, featureSet,//!!! *sni); - Transform::stringToSummaryType(sni->c_str())); - } - } - } - - writeSummaries(audioSource, plugin); - } - - finish(); - - extractionProgress.setProgress(100); - - TempDirectory::getInstance()->cleanup(); -} - -void -FeatureExtractionManager::writeSummaries(QString audioSource, Plugin *plugin) -{ - // caller should have ensured plugin is in m_plugins - PluginMap::iterator pi = m_plugins.find(plugin); - - for (TransformWriterMap::const_iterator ti = pi->second.begin(); - ti != pi->second.end(); ++ti) { - - const Transform &transform = ti->first; - const vector<FeatureWriter *> &writers = ti->second; - - Transform::SummaryType summaryType = transform.getSummaryType(); - PluginSummarisingAdapter::SummaryType pType = - (PluginSummarisingAdapter::SummaryType)summaryType; - - if (transform.getSummaryType() == Transform::NoSummary) { - continue; - } - - PluginSummarisingAdapter *adapter = - dynamic_cast<PluginSummarisingAdapter *>(plugin); - if (!adapter) { - cerr << "FeatureExtractionManager::writeSummaries: INTERNAL ERROR: Summary requested for transform, but plugin is not a summarising adapter" << endl; - continue; - } - - Plugin::FeatureSet featureSet = adapter->getSummaryForAllOutputs - (pType, PluginSummarisingAdapter::ContinuousTimeAverage); - -// cout << "summary type " << int(pType) << " for transform:" << endl << transform.toXmlString().toStdString()<< endl << "... feature set with " << featureSet.size() << " elts" << endl; - - writeFeatures(audioSource, plugin, featureSet, summaryType); - } -} - -void FeatureExtractionManager::writeFeatures(QString audioSource, - Plugin *plugin, - const Plugin::FeatureSet &features, - Transform::SummaryType summaryType) -{ - // caller should have ensured plugin is in m_plugins - PluginMap::iterator pi = m_plugins.find(plugin); - - for (TransformWriterMap::const_iterator ti = pi->second.begin(); - ti != pi->second.end(); ++ti) { - - const Transform &transform = ti->first; - const vector<FeatureWriter *> &writers = ti->second; - - if (transform.getSummaryType() != Transform::NoSummary && - m_summaries.empty() && - summaryType == Transform::NoSummary) { - continue; - } - - if (transform.getSummaryType() != Transform::NoSummary && - summaryType != Transform::NoSummary && - transform.getSummaryType() != summaryType) { - continue; - } - - string outputId = transform.getOutput().toStdString(); - - if (m_pluginOutputs[plugin].find(outputId) == - m_pluginOutputs[plugin].end()) { - continue; - } - - const Plugin::OutputDescriptor &desc = - m_pluginOutputs[plugin][outputId]; - - int outputIndex = m_pluginOutputIndices[outputId]; - Plugin::FeatureSet::const_iterator fsi = features.find(outputIndex); - if (fsi == features.end()) continue; - - for (int j = 0; j < (int)writers.size(); ++j) { - writers[j]->write - (audioSource, transform, desc, fsi->second, - Transform::summaryTypeToString(summaryType).toStdString()); - } - } -} - -void FeatureExtractionManager::finish() -{ - for (PluginMap::iterator pi = m_plugins.begin(); - pi != m_plugins.end(); ++pi) { - - for (TransformWriterMap::iterator ti = pi->second.begin(); - ti != pi->second.end(); ++ti) { - - vector<FeatureWriter *> &writers = ti->second; - - for (int i = 0; i < (int)writers.size(); ++i) { - writers[i]->flush(); - writers[i]->finish(); - } - } - } -} - -void FeatureExtractionManager::print(Transform transform) const -{ - QString qs; - QTextStream qts(&qs); - transform.toXml(qts); - cerr << qs.toStdString() << endl; -}
--- a/FeatureExtractionManager.h Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/* -*- 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-2008 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 _FEATURE_EXTRACTION_MANAGER_H_ -#define _FEATURE_EXTRACTION_MANAGER_H_ - -#include <vector> -#include <set> -#include <string> - -#include <vamp-hostsdk/Plugin.h> -#include <vamp-hostsdk/PluginSummarisingAdapter.h> -#include <transform/Transform.h> - -using std::vector; -using std::set; -using std::string; -using std::pair; -using std::map; - -class FeatureWriter; - -class FeatureExtractionManager -{ -public: - FeatureExtractionManager(); - virtual ~FeatureExtractionManager(); - - void setChannels(int channels); - void setDefaultSampleRate(int sampleRate); - - bool setSummaryTypes(const set<string> &summaryTypes, - bool summariesOnly, - const Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries &boundaries); - - bool addFeatureExtractor(Transform transform, - const vector<FeatureWriter*> &writers); - - bool addFeatureExtractorFromFile(QString transformXmlFile, - const vector<FeatureWriter*> &writers); - - bool addDefaultFeatureExtractor(TransformId transformId, - const vector<FeatureWriter*> &writers); - - void extractFeatures(QString audioSource); - -private: - // A plugin may have many outputs, so we can have more than one - // transform requested for a single plugin. The things we want to - // run in our process loop are plugins rather than their outputs, - // so we maintain a map from the plugins to the transforms desired - // of them and then iterate through this map - - typedef map<Transform, vector<FeatureWriter *> > TransformWriterMap; - typedef map<Vamp::Plugin *, TransformWriterMap> PluginMap; - PluginMap m_plugins; - - // And a map back from transforms to their plugins. Note that - // this is keyed by transform, not transform ID -- two differently - // configured transforms with the same ID must use different - // plugin instances. - - typedef map<Transform, Vamp::Plugin *> TransformPluginMap; - TransformPluginMap m_transformPluginMap; - - // Cache the plugin output descriptors, mapping from plugin to a - // map from output ID to output descriptor. - typedef map<string, Vamp::Plugin::OutputDescriptor> OutputMap; - typedef map<Vamp::Plugin *, OutputMap> PluginOutputMap; - PluginOutputMap m_pluginOutputs; - - // Map from plugin output identifier to plugin output index - typedef map<string, int> OutputIndexMap; - OutputIndexMap m_pluginOutputIndices; - - typedef set<std::string> SummaryNameSet; - SummaryNameSet m_summaries; - bool m_summariesOnly; - Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries m_boundaries; - - void writeSummaries(QString audioSource, Vamp::Plugin *); - - void writeFeatures(QString audioSource, - Vamp::Plugin *, - const Vamp::Plugin::FeatureSet &, - Transform::SummaryType summaryType = - Transform::NoSummary); - void finish(); - - int m_blockSize; - int m_defaultSampleRate; - int m_sampleRate; - int m_channels; - - void print(Transform transform) const; -}; - -#endif
--- a/FeatureWriterFactory.cpp Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* -*- 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-2008 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 "FeatureWriterFactory.h" - -#include "DefaultFeatureWriter.h" -#include "rdf/RDFFeatureWriter.h" -#include "AudioDBFeatureWriter.h" -#include "transform/CSVFeatureWriter.h" - -set<string> -FeatureWriterFactory::getWriterTags() -{ - set<string> tags; - tags.insert("default"); - tags.insert("rdf"); - tags.insert("audiodb"); - tags.insert("csv"); - return tags; -} - -FeatureWriter * -FeatureWriterFactory::createWriter(string tag) -{ - if (tag == "default") { - return new DefaultFeatureWriter(); - } else if (tag == "rdf") { - return new RDFFeatureWriter(); - } else if (tag == "audiodb") { - return new AudioDBFeatureWriter(); - } else if (tag == "csv") { - return new CSVFeatureWriter(); - } - - return 0; -}
--- a/FeatureWriterFactory.h Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* -*- 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-2008 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 _FEATURE_WRITER_FACTORY_H_ -#define _FEATURE_WRITER_FACTORY_H_ - -#include <set> -#include <string> - -using std::set; -using std::string; - -class FeatureWriter; - -class FeatureWriterFactory -{ -public: - static set<string> getWriterTags(); - static FeatureWriter *createWriter(string tag); -}; - - -#endif
--- a/deploy_mac.sh Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -#!/bin/bash - -# this script should be executed from the directory that contains the app directory (application bundle) -# it copies the required 3rd party libraries into the application bundle and corrects the library install names and references - -TARGETPATH="sonic-annotator.app/Contents/Frameworks/" - -mkdir "$TARGETPATH" - -QTPREFIX=/Library/Frameworks/ -QTFWKS="QtXml QtCore QtNetwork" - -# copy the dynamic libraries into the app bundle - -for FWK in $QTFWKS; do - cp ${QTPREFIX}${FWK}.framework/Versions/4/${FWK} "${TARGETPATH}" -done - -# change the id's of the dylibs -for FWK in $QTFWKS; do - install_name_tool -id @executable_path/../Frameworks/${FWK} "$TARGETPATH/$FWK" -done - -# tell the linker to look for dylibs in the app bundle -for FWK in $QTFWKS; do - install_name_tool -change ${FWK}.framework/Versions/4/${FWK} @executable_path/../Frameworks/${FWK} "sonic-annotator.app/Contents/MacOS/sonic-annotator" -done - -# correct dependencies between QT dylibs -for FWK in $QTFWKS; do - case $FWK in QtCore) continue;; esac - install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore "$TARGETPATH/${FWK}" -done -
--- a/main.cpp Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,735 +0,0 @@ -/* -*- 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-2008 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 <vector> -#include <string> -#include <iostream> - -#include <QCoreApplication> -#include <QSettings> -#include <QStringList> -#include <QString> -#include <QFileInfo> -#include <QDir> - -using std::cout; -using std::cerr; -using std::endl; -using std::vector; -using std::string; - -#include "base/Exceptions.h" -#include "base/TempDirectory.h" - -#include "data/fileio/AudioFileReaderFactory.h" -#include "data/fileio/PlaylistFileReader.h" - -#include "transform/Transform.h" -#include "transform/TransformFactory.h" - -#include "FeatureExtractionManager.h" -#include "transform/FeatureWriter.h" -#include "FeatureWriterFactory.h" - -#include "rdf/RDFTransformFactory.h" - -#include <vamp-hostsdk/PluginSummarisingAdapter.h> - -#ifdef HAVE_FFTW3 -#include <fftw3.h> -#endif - -// Desired options: -// -// * output preference: -// - all data in one file -// - one file per input file -// - one file per input file per transform -// - (any use for: one file per transform?) -// -// * output location: -// - same directory as input file -// - current directory -// -// * output filename: -// - based on input (obvious choice for one file per input file modes) -// - specified on command line (obvious choice for all in one file mode) -// -// * output format: one or more of -// - RDF -// - AudioDB -// - Vamp Simple Host format -// - CSV -// -// * input handling: -// - run each transform on each input file separately -// - provide all input files to the same transform, one per channel -// -// * format-specific options: -// - RDF format: fancy/plain RDF -// - CSV format: separator, timestamp type -// note: do the output file/location also count as format-specific options? -// an output writer that wrote to a database would have different options... -// -// * debug level and progress output -// -// * other potential options: -// - ignore version mismatches in Transform specifications -// - sample rate: force a given rate; use file rate instead of rate in -// Transform spec -// -// * other potential instructions: -// - write out a skeleton Transform file for a specified plugin -// - write out skeleton RDF for a plugin library (i.e. do the job of -// RDF template_generator) -// - verify that RDF for a plugin library matches the plugin -// -// MAYBE: -// * transform(s) to run: -// - supply transform file names on command line -// - use all transforms found in a given directory? -// -// MAYBE: -// * input files to transform: -// - supply file names or URIs on command line -// - use all files in a given directory or tree - -static QString -wrap(QString s, int len, int pfx = 0) -{ - QString ws; - QStringList sl(s.split(' ')); - int i = 0, c = 0; - while (i < sl.size()) { - int wl = sl[i].length(); - if (c + wl < len) { - if (c > 0) { - ws += ' '; - ++c; - } - } else { - if (c > 0) { - ws += '\n'; - for (int j = 0; j < pfx; ++j) ws += ' '; - c = 0; - } - } - ws += sl[i]; - c += wl; - ++i; - } - return ws; -} - -void usage(QString myname) -{ - set<string> writers = FeatureWriterFactory::getWriterTags(); - - cerr << endl; - cerr << "Sonic Annotator" << endl; - cerr << "A utility for batch feature extraction from audio files." << endl; - cerr << "Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London." << endl; - cerr << "Copyright 2007-2008 Queen Mary, University of London." << endl; - cerr << endl; - cerr << "This program is free software. You may redistribute copies of it under the" << endl; - cerr << "terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>." << endl; - cerr << "This program is supplied with NO WARRANTY, to the extent permitted by law." << endl; - cerr << endl; - cerr << " Usage: " << myname.toStdString() - << " [-mr] -t trans.xml [...] -w <writer> [...] <audio> [...]" << endl; - cerr << " " << myname.toStdString() - << " [-mr] -T trans.txt [...] -w <writer> [...] <audio> [...]" << endl; - cerr << " " << myname.toStdString() - << " -s <transform>" << endl; - cerr << " " << myname.toStdString() - << " [-lh]" << endl; - cerr << endl; - cerr << "Where <audio> is an audio file or URL to use as input: either a local file" << endl; - cerr << "path, local \"file://\" URL, or remote \"http://\" or \"ftp://\" URL." << endl; - cerr << endl; - - QString extensions = AudioFileReaderFactory::getKnownExtensions(); - QStringList extlist = extensions.split(" ", QString::SkipEmptyParts); - if (!extlist.empty()) { - cerr << "The following audio file extensions are recognised:" << endl; - cerr << " "; - int c = 2; - for (int i = 0; i < extlist.size(); ++i) { - QString ext = extlist[i]; - if (ext.startsWith("*.")) ext = ext.right(ext.length()-2); - c += ext.length() + 2; - if (c >= 80) { - cerr << "\n "; - c -= 78; - } - cerr << ext.toStdString(); - if (i + 1 == extlist.size()) cerr << "."; - else cerr << ", "; - } - cerr << endl; - } - - cerr << "Playlist files in M3U format are also supported." << endl; - cerr << endl; - cerr << "Transformation options:" << endl; - cerr << endl; - cerr << " -t, --transform <T> Apply transform described in transform file <T> to" << endl; - cerr << " all input audio files. You may supply this option" << endl; - cerr << " multiple times. You must supply this option or -T at" << endl; - cerr << " least once for any work to be done. Transform format" << endl; - cerr << " may be SV transform XML or Vamp transform RDF. See" << endl; - cerr << " documentation for examples." << endl; - cerr << endl; - cerr << " -T, --transforms <T> Apply all transforms described in transform files" << endl; - cerr << " whose names are listed in text file <T>. You may supply" << endl; - cerr << " this option multiple times." << endl; - cerr << endl; - cerr << " -d, --default <I> Apply the default transform for transform id <I>. This" << endl; - cerr << " is equivalent to generating a skeleton transform for this" << endl; - cerr << " id (using the -s option, below) and then applying that," << endl; - cerr << " unmodified, with the -t option in the normal way. Note" << endl; - cerr << " that the results may vary as the implementation's default" << endl; - cerr << " processing parameters are not guaranteed. Do not use" << endl; - cerr << " this in production systems. You may supply this option" << endl; - cerr << " multiple times, and mix it with -t and -T." << endl; - cerr << endl; - cerr << " -w, --writer <W> Write output using writer type <W>." << endl; - cerr << " Supported writer types are: "; - for (set<string>::const_iterator i = writers.begin(); - i != writers.end(); ) { - cerr << *i; - if (++i != writers.end()) cerr << ", "; - else cerr << "."; - } - cerr << endl; - cerr << " You may supply this option multiple times. You must" << endl; - cerr << " supply this option at least once for any work to be done." << endl; - cerr << endl; - cerr << " -S, --summary <S> In addition to the result features, write summary feature" << endl; - cerr << " of summary type <S>." << endl; - cerr << " Supported summary types are: min, max, mean, median, mode," << endl; - cerr << " sum, variance, sd, count." << endl; - cerr << " You may supply this option multiple times." << endl; - cerr << endl; - cerr << " --summary-only Write only summary features; do not write the regular" << endl; - cerr << " result features." << endl; - cerr << endl; - cerr << " --segments <A>,<B>[,...]" << endl; - cerr << " Summarise in segments, with segment boundaries" << endl; - cerr << " at A, B, ... seconds." << endl; - cerr << endl; - -/*!!! This feature not implemented yet (sniff) - cerr << " -m, --multiplex If multiple input audio files are given, use mono" << endl; - cerr << " mixdowns of all files as the input channels for a single" << endl; - cerr << " invocation of each transform, instead of running the" << endl; - cerr << " transform against all files separately." << endl; - cerr << endl; -*/ - - cerr << " -r, --recursive If any of the <audio> arguments is found to be a local" << endl; - cerr << " directory, search the tree starting at that directory" << endl; - cerr << " for all supported audio files and take all of those as" << endl; - cerr << " input instead." << endl; - cerr << endl; - cerr << "Housekeeping options:" << endl; - cerr << endl; - cerr << " -l, --list List all known transform ids to standard output." << endl; - cerr << endl; - cerr << " -s, --skeleton <I> Generate a skeleton transform file for transform id <I>" << endl; - cerr << " and write it to standard output." << endl; - cerr << endl; - cerr << " -h, --help Show this help." << endl; - - cerr << endl; - cerr << "If no -w (or --writer) options are supplied, either the -l -s or -h option (or" << endl; - cerr << "long equivalent) must be given instead." << endl; - - for (set<string>::const_iterator i = writers.begin(); - i != writers.end(); ++i) { - FeatureWriter *w = FeatureWriterFactory::createWriter(*i); - if (!w) { - cerr << " (Internal error: failed to create writer of this type)" << endl; - continue; - } - FeatureWriter::ParameterList params = w->getSupportedParameters(); - delete w; - if (params.empty()) { - continue; - } - cerr << endl; - cerr << "Additional options for writer type \"" << *i << "\":" << endl; - cerr << endl; - for (FeatureWriter::ParameterList::const_iterator j = params.begin(); - j != params.end(); ++j) { - cerr << " --" << *i << "-" << j->name << " "; - int spaceage = 16 - int(i->length()) - int(j->name.length()); - if (j->hasArg) { cerr << "<X> "; spaceage -= 4; } - for (int k = 0; k < spaceage; ++k) cerr << " "; - QString s(j->description.c_str()); - s = wrap(s, 56, 22); - cerr << s.toStdString() << endl; - } - } - - cerr << endl; - exit(0); -} - -void -listTransforms() -{ - TransformList transforms = - TransformFactory::getInstance()->getAllTransformDescriptions(); - - for (TransformList::const_iterator iter = transforms.begin(); - iter != transforms.end(); ++iter) { - const TransformDescription &transform = *iter; - if (transform.type == TransformDescription::Analysis) { - cout << transform.identifier.toStdString() << endl; - } - } -} - -void -printSkeleton(QString id) -{ - Transform transform = - TransformFactory::getInstance()->getDefaultTransformFor(id); - cout << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> ." << endl - << "@prefix vamp: <http://purl.org/ontology/vamp/> ." << endl - << "@prefix : <#> ." << endl << endl; - QString rdf = RDFTransformFactory::writeTransformToRDF - (transform, ":transform"); - cout << rdf.toStdString(); -} - -void -findSourcesRecursive(QString dirname, QStringList &addTo, int &found) -{ - QDir dir(dirname); - - QString printable = dir.dirName().left(20); - cerr << "\rScanning \"" << printable.toStdString() << "\"..." - << QString(" ").left(20 - printable.length()).toStdString() - << " [" << found << " audio file(s)]"; - - QString extensions = AudioFileReaderFactory::getKnownExtensions(); - QStringList extlist = extensions.split(" ", QString::SkipEmptyParts); - - QStringList files = dir.entryList - (extlist, QDir::Files | QDir::Readable); - for (int i = 0; i < files.size(); ++i) { - addTo.push_back(dir.filePath(files[i])); - ++found; - } - - QStringList subdirs = dir.entryList - (QStringList(), QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); - for (int i = 0; i < subdirs.size(); ++i) { - findSourcesRecursive(dir.filePath(subdirs[i]), addTo, found); - } -} - - -int main(int argc, char **argv) -{ - QCoreApplication application(argc, argv); - - QCoreApplication::setOrganizationName("QMUL"); - QCoreApplication::setOrganizationDomain("qmul.ac.uk"); - QCoreApplication::setApplicationName("Sonic Annotator"); - - QStringList args = application.arguments(); - set<string> requestedWriterTags; - set<string> requestedTransformFiles; - set<string> requestedTransformListFiles; - set<string> requestedDefaultTransforms; - set<string> requestedSummaryTypes; -//!!! bool multiplex = false; - bool recursive = false; - bool list = false; - bool summaryOnly = false; - QString skeletonFor = ""; - QString myname = args[0]; - myname = QFileInfo(myname).baseName(); - QStringList otherArgs; - Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries boundaries; - - QString helpStr = myname + ": use -h or --help option for help"; - - for (int i = 1; i < args.size(); ++i) { - - QString arg = args[i]; - bool last = ((i + 1) == args.size()); - - if (arg == "-h" || arg == "--help" || arg == "-?") { - usage(myname); - } - - if (arg == "-w" || arg == "--writer") { - if (last || args[i+1].startsWith("-")) { - cerr << myname.toStdString() << ": argument expected for \"" - << arg.toStdString() << "\" option" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } else { - string tag = args[++i].toStdString(); - if (requestedWriterTags.find(tag) != requestedWriterTags.end()) { - cerr << myname.toStdString() << ": NOTE: duplicate specification of writer type \"" << tag << "\" ignored" << endl; - } else { - requestedWriterTags.insert(tag); - } - continue; - } - } else if (arg == "-t" || arg == "--transform") { - if (last || args[i+1].startsWith("-")) { - cerr << myname.toStdString() << ": argument expected for \"" - << arg.toStdString() << "\" option" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } else { - string transform = args[++i].toStdString(); - if (requestedTransformFiles.find(transform) != - requestedTransformFiles.end()) { - cerr << myname.toStdString() << ": NOTE: duplicate specification of transform file \"" << transform << "\" ignored" << endl; - } else { - requestedTransformFiles.insert(transform); - } - continue; - } - } else if (arg == "-T" || arg == "--transforms") { - if (last || args[i+1].startsWith("-")) { - cerr << myname.toStdString() << ": argument expected for \"" - << arg.toStdString() << "\" option" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } else { - string transform = args[++i].toStdString(); - if (requestedTransformListFiles.find(transform) != - requestedTransformListFiles.end()) { - cerr << myname.toStdString() << ": NOTE: duplicate specification of transform list file \"" << transform << "\" ignored" << endl; - } else { - requestedTransformListFiles.insert(transform); - } - continue; - } - } else if (arg == "-d" || arg == "--default") { - if (last || args[i+1].startsWith("-")) { - cerr << myname.toStdString() << ": argument expected for \"" - << arg.toStdString() << "\" option" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } else { - string deft = args[++i].toStdString(); - if (requestedDefaultTransforms.find(deft) != - requestedDefaultTransforms.end()) { - cerr << myname.toStdString() << ": NOTE: duplicate specification of default transform \"" << deft << "\" ignored" << endl; - } else { - requestedDefaultTransforms.insert(deft); - } - continue; - } - } else if (arg == "-S" || arg == "--summary") { - if (last || args[i+1].startsWith("-")) { - cerr << myname.toStdString() << ": argument expected for \"" - << arg.toStdString() << "\" option" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } else { - string summary = args[++i].toStdString(); - requestedSummaryTypes.insert(summary); - continue; - } - } else if (arg == "--summary-only") { - summaryOnly = true; - continue; - } else if (arg == "--segments") { - if (last) { - cerr << myname.toStdString() << ": argument expected for \"" - << arg.toStdString() << "\" option" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } else { - string segmentSpec = args[++i].toStdString(); - QStringList segmentStrs = QString(segmentSpec.c_str()).split(','); - for (int j = 0; j < segmentStrs.size(); ++j) { - bool good = false; - boundaries.insert(Vamp::RealTime::fromSeconds - (segmentStrs[j].toDouble(&good))); - if (!good) { - cerr << myname.toStdString() << ": segment boundaries must be numeric" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } - } - } -/*!!! - } else if (arg == "-m" || arg == "--multiplex") { - multiplex = true; - cerr << myname.toStdString() - << ": WARNING: Multiplex argument not yet implemented" << endl; //!!! - continue; -*/ - } else if (arg == "-r" || arg == "--recursive") { - recursive = true; - continue; - } else if (arg == "-l" || arg == "--list") { - list = true; - continue; - } else if (arg == "-s" || arg == "--skeleton") { - if (last || args[i+1].startsWith("-")) { - cerr << myname.toStdString() << ": usage: " - << myname.toStdString() << " " << arg.toStdString() - << " <transform>" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } else { - skeletonFor = args[++i]; - continue; - } - } else { - otherArgs.push_back(args[i]); - } - } - - if (list) { - if (!requestedWriterTags.empty() || skeletonFor != "") { - cerr << helpStr.toStdString() << endl; - exit(2); - } - listTransforms(); - exit(0); - } - if (skeletonFor != "") { - if (!requestedWriterTags.empty()) { - cerr << helpStr.toStdString() << endl; - exit(2); - } - printSkeleton(skeletonFor); - exit(0); - } - - if (requestedTransformFiles.empty() && - requestedTransformListFiles.empty() && - requestedDefaultTransforms.empty()) { - cerr << myname.toStdString() - << ": no transform(s) specified" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } - - if (requestedWriterTags.empty()) { - cerr << myname.toStdString() - << ": no writer(s) specified" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } - - if (!boundaries.empty()) { - if (requestedSummaryTypes.empty()) { - cerr << myname.toStdString() - << ": summary segment boundaries provided, but no summary type specified" - << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } - } - -#ifdef HAVE_FFTW3 - QSettings settings; - settings.beginGroup("FFTWisdom"); - QString wisdom = settings.value("wisdom").toString(); - if (wisdom != "") { - fftw_import_wisdom_from_string(wisdom.toLocal8Bit().data()); - } - settings.endGroup(); -#endif - - FeatureExtractionManager manager; - - if (!requestedSummaryTypes.empty()) { - if (!manager.setSummaryTypes(requestedSummaryTypes, - summaryOnly, - boundaries)) { - cerr << myname.toStdString() - << ": failed to set requested summary types" << endl; - exit(1); - } - } - - // the manager dictates the sample rate and number of channels - // to work at - files with too few channels are rejected, - // too many channels are handled as usual by the Vamp plugin - - //!!! Review this: although we probably do want to fix the channel - // count here, we don't necessarily want to fix the rate: it's - // specified in the Transform file. - - manager.setDefaultSampleRate(44100); - manager.setChannels(1); - - vector<FeatureWriter *> writers; - - for (set<string>::const_iterator i = requestedWriterTags.begin(); - i != requestedWriterTags.end(); ++i) { - - FeatureWriter *writer = FeatureWriterFactory::createWriter(*i); - - if (!writer) { - cerr << myname.toStdString() << ": unknown feature writer \"" - << *i << "\"" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } - - map<string, string> writerArgs; - FeatureWriter::ParameterList pl(writer->getSupportedParameters()); - - for (int k = 0; k < pl.size(); ++k) { - - string argbase = pl[k].name; - QString literal = QString("--%1-%2") - .arg(i->c_str()).arg(argbase.c_str()); - - for (int j = 0; j < otherArgs.size(); ) { - - if (otherArgs[j] != literal) { - ++j; - continue; - } - - otherArgs.removeAt(j); - - if (pl[k].hasArg) { - if (j < otherArgs.size()) { - writerArgs[argbase] = otherArgs[j].toStdString(); - otherArgs.removeAt(j); - } else { - cerr << myname.toStdString() << ": " - << "argument required for \"" - << literal.toStdString() << "\" option" - << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } - } else { - writerArgs[argbase] = ""; - } - } - } - - writer->setParameters(writerArgs); - - writers.push_back(writer); - } - - for (int i = 0; i < otherArgs.size(); ++i) { - if (otherArgs[i].startsWith("-")) { - cerr << myname.toStdString() << ": unknown option \"" - << otherArgs[i].toStdString() << "\"" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } - } - - if (otherArgs.empty()) { - cerr << myname.toStdString() << ": no input(s) specified" << endl; - cerr << helpStr.toStdString() << endl; - exit(2); - } - - for (set<string>::const_iterator i = requestedTransformListFiles.begin(); - i != requestedTransformListFiles.end(); ++i) { - PlaylistFileReader reader(i->c_str()); - if (reader.isOK()) { - vector<QString> files = reader.load(); - for (int j = 0; j < files.size(); ++j) { - requestedTransformFiles.insert(files[j].toStdString()); - } - } else { - cerr << myname.toStdString() << ": failed to read template list file \"" << *i << "\"" << endl; - exit(2); - } - } - - bool haveFeatureExtractor = false; - - for (set<string>::const_iterator i = requestedTransformFiles.begin(); - i != requestedTransformFiles.end(); ++i) { - if (manager.addFeatureExtractorFromFile(i->c_str(), writers)) { - haveFeatureExtractor = true; - } - } - - for (set<string>::const_iterator i = requestedDefaultTransforms.begin(); - i != requestedDefaultTransforms.end(); ++i) { - if (manager.addDefaultFeatureExtractor(i->c_str(), writers)) { - haveFeatureExtractor = true; - } - } - - if (!haveFeatureExtractor) { - cerr << myname.toStdString() << ": no feature extractors added" << endl; - exit(2); - } - - QStringList sources; - if (!recursive) { - sources = otherArgs; - } else { - for (QStringList::const_iterator i = otherArgs.begin(); - i != otherArgs.end(); ++i) { - if (QDir(*i).exists()) { - cerr << "Directory found and recursive flag set, scanning for audio files..." << endl; - int found = 0; - findSourcesRecursive(*i, sources, found); - cerr << "\rDone, found " << found << " supported audio file(s) " << endl; - } else { - sources.push_back(*i); - } - } - } - - for (QStringList::const_iterator i = sources.begin(); - i != sources.end(); ++i) { - std::cerr << "Extracting features for: \"" << i->toStdString() << "\"" << std::endl; - try { - manager.extractFeatures(*i); - } catch (FailedToOpenFile f) { - cerr << "ERROR: Failed to open output file for feature writer: " - << f.what() << endl; - break; - } - } - - for (int i = 0; i < writers.size(); ++i) delete writers[i]; - -#ifdef HAVE_FFTW3 - settings.beginGroup("FFTWisdom"); - char *cwisdom = fftw_export_wisdom_to_string(); - if (cwisdom) { - settings.setValue("wisdom", cwisdom); - fftw_free(cwisdom); - } - settings.endGroup(); -#endif - - TempDirectory::getInstance()->cleanup(); - - return 0; -} - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pull-svn.sh Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,13 @@ +#!/bin/sh + +svrepo=https://sv1.svn.sourceforge.net/svnroot/sv1/sonic-visualiser/trunk + +svn co $svrepo/audioio +svn co $svrepo/base +svn co $svrepo/data +svn co $svrepo/plugin +svn co $svrepo/rdf +svn co $svrepo/system +svn co $svrepo/transform + +
--- a/qm-keydetector.n3 Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. -@prefix xsd: <http://www.w3.org/2001/XMLSchema#>. -@prefix vamp: <http://www.vamp-plugins.org/ontology/> . -@prefix vampex: <http://www.vamp-plugins.org/examples/> . -@prefix qvp: <http://vamp-plugins.org/plugin/qm-vamp-plugins/>. -@prefix owl: <http://www.w3.org/2002/07/owl#> . -@prefix dc: <http://purl.org/dc/elements/1.1/> . -@prefix af: <http://purl.org/ontology/af/> . -@prefix foaf: <http://xmlns.com/foaf/0.1/> . -@prefix cc: <http://web.resource.org/cc/> . -@prefix thisplug: <http://vamp-plugins.org/plugin/qm-vamp-plugins/qm-keydetector#> . -@prefix : <> . - -<> a vamp:PluginDescription ; - foaf:maker <http://chrissutton.org/me> ; - foaf:primaryTopic qvp:qm-keydetector . - -qvp:qm-keydetector a vamp:Plugin ; - dc:title "Key Detector" ; - dc:description ""; - foaf:maker :katynoland, :chrislandone ; -# cc:license <http://creativecommons.org/licenses/BSD/> ; what is the license for QM Vamp plugins ? - vamp:identifier "qm-keydetector" ; # The Vamp identifier for the plugin - vamp:vamp_API_version vamp:version_v1.1b ; # Made up - this plugin doesn't actually specify it - owl:versionInfo "2" ; - vamp:input_domain vamp:TimeDomain ; - - vamp:parameter_descriptor thisplug:pd1 ; - vamp:parameter_descriptor thisplug:pd2 ; - vamp:output_descriptor thisplug:od1 ; - vamp:output_descriptor thisplug:od2 ; - vamp:output_descriptor thisplug:od3 . - -:katynoland a foaf:Person; - foaf:name "Katy Noland" . -:chrislandone a foaf:Person; - foaf:name "Christian Landone" . - -# Note : any need for these to have proper URIs ? -thisplug:pd1 a vamp:ParameterDescriptor ; - vamp:identifier "tuning" ; - dc:title "Tuning Frequency" ; - dc:format "Hz" ; - vamp:minValue 420 ; #might be useful when interpreting plugin output - vamp:maxValue 460 ; - vamp:defaultValue 440 . - -thisplug:pd2 a vamp:ParameterDescriptor ; - vamp:identifier "length" ; - dc:title "Window Length" ; - vamp:minValue 1 ; #might be useful when interpreting plugin output - vamp:maxValue 30 ; - vamp:defaultValue 10 . - -thisplug:od1 a vamp:OutputDescriptor ; - vamp:identifier "tonic" ; - dc:title "Tonic Pitch" ; - vamp:fixed_bin_count "true" ; - vamp:bin_count 1 ; - vamp:sample_type vamp:OneSamplePerStep . - - -thisplug:od2 a vamp:OutputDescriptor ; - vamp:identifier "mode" ; - dc:title "Key Mode" ; - vamp:fixed_bin_count "true" ; - vamp:bin_count 1 ; - vamp:bin_names "Major = 0, Minor = 1" ; # might need a rethink - vamp:sample_type vamp:OneSamplePerStep . - -thisplug:od3 a vamp:OutputDescriptor ; - vamp:identifier "key" ; - dc:title "Key" ; - vamp:fixed_bin_count "true" ; - vamp:bin_count 1 ; - vamp:sample_type vamp:OneSamplePerStep ; - vamp:computes_feature_type <http://purl.org/NET/c4dm/keys.owl#Key> .
--- a/runner.pro Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ - -TEMPLATE = app - -SV_UNIT_PACKAGES = vamp vamp-hostsdk samplerate mad id3tag oggz fishsound sndfile lrdf redland rasqal raptor - -#linux-g++:LIBS += -Wl,-Bstatic -#linux-g++:DEFINES += BUILD_STATIC - -load(../sonic-visualiser/sv.prf) - -LIBPATH += /usr/local/lib - -CONFIG += sv qt thread warn_on stl rtti exceptions console -QT += xml network -QT -= gui - -# Using the "console" CONFIG flag above should ensure this happens for -# normal Windows builds, but the console feature doesn't get picked up -# in my local cross-compile setup because qmake itself doesn't know to -# look for win32 features -win32-x-g++:QMAKE_LFLAGS += -Wl,-subsystem,console - -# If you have compiled your Vamp plugin SDK with FFTW (using its -# HAVE_FFTW3 flag), you can define the same flag here to ensure the -# program saves and restores FFTW wisdom in its configuration properly -# -#DEFINES += HAVE_FFTW3 - -TARGET = sonic-annotator - -DEPENDPATH += . ../sonic-visualiser i18n main -INCLUDEPATH += . ../sonic-visualiser main -LIBPATH = ../sonic-visualiser/audioio ../sonic-visualiser/data ../sonic-visualiser/plugin ../sonic-visualiser/rdf ../sonic-visualiser/transform ../sonic-visualiser/base ../sonic-visualiser/system $$LIBPATH - -QMAKE_CXXFLAGS_RELEASE += -fmessage-length=80 -fdiagnostics-show-location=every-line - -contains(DEFINES, BUILD_STATIC):LIBS -= -ljack - -#LIBS = -lsvaudioio -lsvdata -lsvtransform -lsvplugin -lsvrdf -lsvbase -lsvsystem $$LIBS -LIBS = -lsvdata -lsvtransform -lsvplugin -lsvrdf -lsvdata -lsvbase -lsvsystem $$LIBS - -PRE_TARGETDEPS += ../sonic-visualiser/audioio/libsvaudioio.a \ - ../sonic-visualiser/data/libsvdata.a \ - ../sonic-visualiser/transform/libsvtransform.a \ - ../sonic-visualiser/plugin/libsvplugin.a \ - ../sonic-visualiser/rdf/libsvrdf.a \ - ../sonic-visualiser/base/libsvbase.a \ - ../sonic-visualiser/system/libsvsystem.a - -OBJECTS_DIR = tmp_obj -MOC_DIR = tmp_moc - -# Input -HEADERS += \ - AudioDBFeatureWriter.h \ - FeatureWriterFactory.h \ - DefaultFeatureWriter.h \ - FeatureExtractionManager.h - -SOURCES += \ - main.cpp \ - DefaultFeatureWriter.cpp \ - FeatureExtractionManager.cpp \ - AudioDBFeatureWriter.cpp \ - FeatureWriterFactory.cpp - - - - -# Restore dynamic linkage, in case we went static earlier -linux-g++:LIBS += -Wl,-Bdynamic -lpthread -ldl -lz
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/AudioDBFeatureWriter.cpp Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,214 @@ +/* -*- 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-2008 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 <fstream> + +#include <QFileInfo> + +#include "AudioDBFeatureWriter.h" + +using namespace std; +using namespace Vamp; + +string +AudioDBFeatureWriter::catalogueIdParam = "catid"; + +string +AudioDBFeatureWriter::baseDirParam = "basedir"; + +struct AudioDBFeatureWriter::TrackStream +{ + QString trackid; + ofstream* ofs; +}; + +AudioDBFeatureWriter::AudioDBFeatureWriter() : + catalogueId("catalog"), baseDir("audiodb") +{ + +} + +AudioDBFeatureWriter::~AudioDBFeatureWriter() +{ + // close all open files + for (map<string, TrackStream>::iterator iter = dbfiles.begin(); iter != dbfiles.end(); ++iter) + { + if (iter->second.ofs) { + iter->second.ofs->close(); + delete iter->second.ofs; + } + } + + // TODO: error handling on close +} + +AudioDBFeatureWriter::ParameterList +AudioDBFeatureWriter::getSupportedParameters() const +{ + ParameterList pl; + Parameter p; + + p.name = catalogueIdParam; + p.description = "Catalogue ID"; + p.hasArg = true; + pl.push_back(p); + + p.name = baseDirParam; + p.description = "Base output directory path"; + p.hasArg = true; + pl.push_back(p); + + return pl; +} + +void +AudioDBFeatureWriter::setParameters(map<string, string> ¶ms) +{ + if (params.find(catalogueIdParam) != params.end()) { + setCatalogueId(params[catalogueIdParam]); + params.erase(catalogueIdParam); + } + if (params.find(baseDirParam) != params.end()) { + setBaseDirectory(params[baseDirParam]); + params.erase(baseDirParam); + } +} + +void +AudioDBFeatureWriter::setCatalogueId(const string &catid) +{ + catalogueId = catid; +} + +void +AudioDBFeatureWriter::setBaseDirectory(const string &base) +{ + baseDir = base; +} + +void AudioDBFeatureWriter::write(QString trackid, + const Transform &transform, + const Vamp::Plugin::OutputDescriptor& output, + const Vamp::Plugin::FeatureList& featureList, + std::string summaryType) +{ + //!!! use summaryType + if (summaryType != "") { + //!!! IMPLEMENT + cerr << "ERROR: AudioDBFeatureWriter::write: Writing summaries is not yet implemented!" << endl; + exit(1); + } + + + // binary output for FeatureSet + + // feature-dimension feature-1 feature-2 ... + // timestamp-1 timestamp-2 ... + + // audioDB has to write each feature to a different file + // assume a simple naming convention of + // <catalog-id>/<track-id>.<feature-id> + // with timestamps in a corresponding <catalog-id>/<track-id>.<feature-id>.timestamp file + // (start and end times in seconds for each frame -- somewhat optional) + + // the feature writer holds a map of open file descriptors + // the catalog-id is passed in to the feature writer's constructor + + // NB -- all "floats" in the file should in fact be doubles + + // TODO: + // - write feature end rather than start times, once end time is available in vamp + // - write a power file, probably by wrapping plugin in a PluginPowerAdapter :) + + if (output.binCount == 0) // this kind of feature just outputs timestamps and labels, assume of no interest to audioDB + return; + + for (int i = 0; i < featureList.size(); ++i) + { + // replace output files if necessary + if (replaceDBFile(trackid, output.identifier)) + { + // write the feature length for the next track feature record + // binCount has to be set + // - it can be zero, i.e. if the output is really a set of labels + timestamps + *dbfiles[output.identifier].ofs /*<< ios::binary*/ << output.binCount; + + cerr << "writing bin count " << output.binCount << " for " << output.identifier << endl; + } + + if (replaceDBFile(trackid, output.identifier + ".timestamp")) + { + // write the start time to the timestamp file + // as we want it for the first feature in the file + *dbfiles[output.identifier + ".timestamp"].ofs << featureList[i].timestamp.toString() << endl; + } + + if (dbfiles[output.identifier].ofs) { + for (int j = 0; j < featureList[i].values.size(); ++j) + *dbfiles[output.identifier].ofs /*<< ios::binary*/ << featureList[i].values[j]; + + // write the *end* time of each feature to the timestamp file + // NOT IMPLEMENTED YET +// *dbfiles[output.identifier + ".timestamp"].ofs << featureList[i].timestamp.toString() << endl; + } + } +} + +bool AudioDBFeatureWriter::openDBFile(QString trackid, const string& identifier) +{ + QString trackBase = QFileInfo(trackid).fileName(); + string filepath = baseDir + "/" + catalogueId + "/" + + trackBase.toStdString() + "." + identifier; + cerr << "AudioDBFeatureWriter::openDBFile: filepath is \"" << filepath << "\"" << endl; + ofstream* ofs = new ofstream(filepath.c_str()); + if (!*ofs) + { + cerr << "ERROR AudioDBFeatureWriter::openDBFile(): can't open file " << filepath << endl; + return false; + } + TrackStream ts; + ts.trackid = trackid; + ts.ofs = ofs; + dbfiles[identifier] = ts; + return true; +} + +// replace file if no file open for this track, else return false +bool AudioDBFeatureWriter::replaceDBFile(QString trackid, + const string& identifier) +{ + if (dbfiles.find(identifier) != dbfiles.end() && dbfiles[identifier].trackid == trackid) + return false; // have an open file for this track + + if (dbfiles.find(identifier) != dbfiles.end() && dbfiles[identifier].trackid != trackid) + { + // close the current file + if (dbfiles[identifier].ofs) { + dbfiles[identifier].ofs->close(); + delete dbfiles[identifier].ofs; + dbfiles[identifier].ofs = 0; + } + } + + // open a new file + if (!openDBFile(trackid, identifier)) { + dbfiles[identifier].ofs = 0; + return false; //!!! should throw an exception, otherwise we'll try to open the file again and again every time we want to write to it + } + + return true; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/AudioDBFeatureWriter.h Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,61 @@ +/* -*- 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-2008 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 _AUDIO_DB_FEATURE_WRITER_H_ +#define _AUDIO_DB_FEATURE_WRITER_H_ + +#include <string> +#include <map> + +using std::string; +using std::map; + +#include "transform/FeatureWriter.h" + +class AudioDBFeatureWriter : public FeatureWriter +{ +public: + AudioDBFeatureWriter(); + virtual ~AudioDBFeatureWriter(); + + virtual ParameterList getSupportedParameters() const; + virtual void setParameters(map<string, string> ¶ms); + + virtual void setCatalogueId(const string &); + virtual void setBaseDirectory(const string &); + + virtual void write(QString trackid, + const Transform &transform, + const Vamp::Plugin::OutputDescriptor &output, + const Vamp::Plugin::FeatureList &features, + std::string summaryType = ""); + + virtual void finish() { } + +private: + string catalogueId; + string baseDir; + + static string catalogueIdParam; + static string baseDirParam; + + struct TrackStream; + map<string, TrackStream> dbfiles; + + bool openDBFile(QString trackid, const string& identifier); + bool replaceDBFile(QString trackid, const string& identifier); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/DefaultFeatureWriter.cpp Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,77 @@ +/* -*- 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-2008 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 <iostream> +#include <map> + +using namespace std; + +#include "DefaultFeatureWriter.h" + +void DefaultFeatureWriter::write(QString trackid, + const Transform &transform, + const Vamp::Plugin::OutputDescriptor& output, + const Vamp::Plugin::FeatureList& featureList, + std::string summaryType) +{ + // generic XML output + + /* + + <feature> + <name>output.name</name> + <timestamp>feature.timestamp</timestamp> + <values>output.binName[0]:feature.value[0]...</values> + <label>feature.label</label> + </feature> + + */ + + for (int i = 0; i < featureList.size(); ++i) + { + if (summaryType == "") { + cout << "<feature>" << endl; + } else { + cout << "<summary type=\"" << summaryType << "\">" << endl; + } + cout << "\t<name>" << output.name << "</name>" << endl; + if (featureList[i].hasTimestamp) { + cout << "\t<timestamp>" << featureList[i].timestamp << "</timestamp>" << endl; + } + if (featureList[i].hasDuration) { + cout << "\t<duration>" << featureList[i].duration << "</duration>" << endl; + } + if (featureList[i].values.size() > 0) + { + cout << "\t<values>"; + for (int j = 0; j < featureList[i].values.size(); ++j) + { + if (j > 0) + cout << " "; + if (output.binNames.size() > 0) + cout << output.binNames[j] << ":"; + cout << featureList[i].values[j]; + } + cout << "</values>" << endl; + } + if (featureList[i].label.length() > 0) + cout << "\t<label>" << featureList[i].label << "</label>" << endl; + if (summaryType == "") { + cout << "</feature>" << endl; + } else { + cout << "</summary>" << endl; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/DefaultFeatureWriter.h Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,34 @@ +/* -*- 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-2008 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 _DEFAULT_FEATURE_WRITER_H_ +#define _DEFAULT_FEATURE_WRITER_H_ + + +#include "transform/FeatureWriter.h" + +class DefaultFeatureWriter : public FeatureWriter +{ +public: + virtual ~DefaultFeatureWriter() { } + virtual void write(QString trackid, + const Transform &transform, + const Vamp::Plugin::OutputDescriptor &output, + const Vamp::Plugin::FeatureList &features, + std::string summaryType = ""); + virtual void finish() { } +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/FeatureExtractionManager.cpp Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,702 @@ +/* -*- 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-2008 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 "FeatureExtractionManager.h" + +#include <vamp-hostsdk/PluginChannelAdapter.h> +#include <vamp-hostsdk/PluginBufferingAdapter.h> +#include <vamp-hostsdk/PluginInputDomainAdapter.h> +#include <vamp-hostsdk/PluginSummarisingAdapter.h> +#include <vamp-hostsdk/PluginLoader.h> + +#include <iostream> + +using namespace std; + +using Vamp::Plugin; +using Vamp::PluginBase; +using Vamp::HostExt::PluginLoader; +using Vamp::HostExt::PluginChannelAdapter; +using Vamp::HostExt::PluginBufferingAdapter; +using Vamp::HostExt::PluginInputDomainAdapter; +using Vamp::HostExt::PluginSummarisingAdapter; + +#include "data/fileio/FileSource.h" +#include "data/fileio/AudioFileReader.h" +#include "data/fileio/AudioFileReaderFactory.h" +#include "data/fileio/PlaylistFileReader.h" +#include "base/TempDirectory.h" +#include "base/ProgressPrinter.h" +#include "transform/TransformFactory.h" +#include "rdf/RDFTransformFactory.h" +#include "transform/FeatureWriter.h" + +#include <QTextStream> +#include <QFile> +#include <QFileInfo> + +FeatureExtractionManager::FeatureExtractionManager() : + m_summariesOnly(false), + // We can read using an arbitrary fixed block size -- + // PluginBufferingAdapter handles this for us. It's likely to be + // quicker to use larger sizes than smallish ones like 1024 + m_blockSize(16384), + m_defaultSampleRate(0), + m_sampleRate(0), + m_channels(1) +{ +} + +FeatureExtractionManager::~FeatureExtractionManager() +{ + for (PluginMap::iterator pi = m_plugins.begin(); + pi != m_plugins.end(); ++pi) { + delete pi->first; + } +} + +void FeatureExtractionManager::setChannels(int channels) +{ + m_channels = channels; +} + +void FeatureExtractionManager::setDefaultSampleRate(int sampleRate) +{ + m_defaultSampleRate = sampleRate; +} + +static PluginSummarisingAdapter::SummaryType +getSummaryType(string name) +{ + if (name == "min") return PluginSummarisingAdapter::Minimum; + if (name == "max") return PluginSummarisingAdapter::Maximum; + if (name == "mean") return PluginSummarisingAdapter::Mean; + if (name == "median") return PluginSummarisingAdapter::Median; + if (name == "mode") return PluginSummarisingAdapter::Mode; + if (name == "sum") return PluginSummarisingAdapter::Sum; + if (name == "variance") return PluginSummarisingAdapter::Variance; + if (name == "sd") return PluginSummarisingAdapter::StandardDeviation; + if (name == "count") return PluginSummarisingAdapter::Count; + return PluginSummarisingAdapter::UnknownSummaryType; +} + +bool FeatureExtractionManager::setSummaryTypes(const set<string> &names, + bool summariesOnly, + const PluginSummarisingAdapter::SegmentBoundaries &boundaries) +{ + for (SummaryNameSet::const_iterator i = names.begin(); + i != names.end(); ++i) { + if (getSummaryType(*i) == PluginSummarisingAdapter::UnknownSummaryType) { + cerr << "ERROR: Unknown summary type \"" << *i << "\"" << endl; + return false; + } + } + m_summaries = names; + m_summariesOnly = summariesOnly; + m_boundaries = boundaries; + return true; +} + +bool FeatureExtractionManager::addFeatureExtractor +(Transform transform, const vector<FeatureWriter*> &writers) +{ + //!!! exceptions rather than return values? + + if (transform.getSampleRate() == 0) { + if (m_sampleRate == 0) { + cerr << "NOTE: Transform does not specify a sample rate, using default rate of " << m_defaultSampleRate << endl; + transform.setSampleRate(m_defaultSampleRate); + m_sampleRate = m_defaultSampleRate; + } else { + cerr << "NOTE: Transform does not specify a sample rate, using previous transform's rate of " << m_sampleRate << endl; + transform.setSampleRate(m_sampleRate); + } + } + + if (m_sampleRate == 0) { + m_sampleRate = transform.getSampleRate(); + } + + if (transform.getSampleRate() != m_sampleRate) { + cerr << "WARNING: Transform sample rate " << transform.getSampleRate() << " does not match previously specified transform rate of " << m_sampleRate << " -- only a single rate is supported for each run" << endl; + cerr << "WARNING: Using previous rate of " << m_sampleRate << " for this transform as well" << endl; + transform.setSampleRate(m_sampleRate); + } + + Plugin *plugin = 0; + + // Remember what the original transform looked like, and index + // based on this -- because we may be about to fill in the zeros + // for step and block size, but we want any further copies with + // the same zeros to match this one + Transform originalTransform = transform; + + if (m_transformPluginMap.find(transform) == m_transformPluginMap.end()) { + + // Test whether we already have a transform that is identical + // to this, except for the output requested and/or the summary + // type -- if so, they should share plugin instances (a vital + // optimisation) + + for (TransformPluginMap::iterator i = m_transformPluginMap.begin(); + i != m_transformPluginMap.end(); ++i) { + Transform test = i->first; + test.setOutput(transform.getOutput()); + test.setSummaryType(transform.getSummaryType()); + if (transform == test) { + cerr << "NOTE: Already have transform identical to this one (for \"" + << transform.getIdentifier().toStdString() + << "\") in every detail except output identifier and/or " + << "summary type; sharing its plugin instance" << endl; + plugin = i->second; + if (transform.getSummaryType() != Transform::NoSummary && + !dynamic_cast<PluginSummarisingAdapter *>(plugin)) { + plugin = new PluginSummarisingAdapter(plugin); + i->second = plugin; + } + break; + } + } + + if (!plugin) { + + TransformFactory *tf = TransformFactory::getInstance(); + + PluginBase *pb = tf->instantiatePluginFor(transform); + plugin = tf->downcastVampPlugin(pb); + if (!plugin) { + //!!! todo: handle non-Vamp plugins too, or make the main --list + // option print out only Vamp transforms + cerr << "ERROR: Failed to load plugin for transform \"" + << transform.getIdentifier().toStdString() << "\"" << endl; + delete pb; + return false; + } + + // We will provide the plugin with arbitrary step and + // block sizes (so that we can use the same read/write + // block size for all transforms), and to that end we use + // a PluginBufferingAdapter. However, we need to know the + // underlying step size so that we can provide the right + // context for dense outputs. (Although, don't forget + // that the PluginBufferingAdapter rewrites + // OneSamplePerStep outputs so as to use FixedSampleRate + // -- so it supplies the sample rate in the output + // feature. I'm not sure whether we can easily use that.) + + size_t pluginStepSize = plugin->getPreferredStepSize(); + size_t pluginBlockSize = plugin->getPreferredBlockSize(); + + // adapt the plugin for buffering, channels, etc. + if (plugin->getInputDomain() == Plugin::FrequencyDomain) { + plugin = new PluginInputDomainAdapter(plugin); + } + + PluginBufferingAdapter *pba = new PluginBufferingAdapter(plugin); + plugin = pba; + + if (transform.getStepSize() != 0) { + pba->setPluginStepSize(transform.getStepSize()); + } else { + transform.setStepSize(pluginStepSize); + } + + if (transform.getBlockSize() != 0) { + pba->setPluginBlockSize(transform.getBlockSize()); + } else { + transform.setBlockSize(pluginBlockSize); + } + + plugin = new PluginChannelAdapter(plugin); + + if (!m_summaries.empty() || + transform.getSummaryType() != Transform::NoSummary) { + PluginSummarisingAdapter *adapter = + new PluginSummarisingAdapter(plugin); + adapter->setSummarySegmentBoundaries(m_boundaries); + plugin = adapter; + } + + if (!plugin->initialise(m_channels, m_blockSize, m_blockSize)) { + cerr << "ERROR: Plugin initialise (channels = " << m_channels << ", stepSize = " << m_blockSize << ", blockSize = " << m_blockSize << ") failed." << endl; + delete plugin; + return false; + } + +// cerr << "Initialised plugin" << endl; + + size_t actualStepSize = 0; + size_t actualBlockSize = 0; + pba->getActualStepAndBlockSizes(actualStepSize, actualBlockSize); + transform.setStepSize(actualStepSize); + transform.setBlockSize(actualBlockSize); + + Plugin::OutputList outputs = plugin->getOutputDescriptors(); + for (int i = 0; i < (int)outputs.size(); ++i) { + +// cerr << "Newly initialised plugin output " << i << " has bin count " << outputs[i].binCount << endl; + + m_pluginOutputs[plugin][outputs[i].identifier] = outputs[i]; + m_pluginOutputIndices[outputs[i].identifier] = i; + } + + cerr << "NOTE: Loaded and initialised plugin " << plugin + << " for transform \"" + << transform.getIdentifier().toStdString() << "\"" << endl; + } + + if (transform.getOutput() == "") { + transform.setOutput + (plugin->getOutputDescriptors()[0].identifier.c_str()); + } + + m_transformPluginMap[transform] = plugin; + + if (!(originalTransform == transform)) { + m_transformPluginMap[originalTransform] = plugin; + } + + } else { + + plugin = m_transformPluginMap[transform]; + } + + m_plugins[plugin][transform] = writers; + + return true; +} + +bool FeatureExtractionManager::addDefaultFeatureExtractor +(TransformId transformId, const vector<FeatureWriter*> &writers) +{ + TransformFactory *tf = TransformFactory::getInstance(); + + if (m_sampleRate == 0) { + if (m_defaultSampleRate == 0) { + cerr << "ERROR: Default transform requested, but no default sample rate available" << endl; + return false; + } else { + cerr << "NOTE: Using default sample rate of " << m_defaultSampleRate << " for default transform" << endl; + m_sampleRate = m_defaultSampleRate; + } + } + + Transform transform = tf->getDefaultTransformFor(transformId, m_sampleRate); + + return addFeatureExtractor(transform, writers); +} + +bool FeatureExtractionManager::addFeatureExtractorFromFile +(QString transformXmlFile, const vector<FeatureWriter*> &writers) +{ + RDFTransformFactory factory + (QUrl::fromLocalFile(QFileInfo(transformXmlFile).absoluteFilePath()) + .toString()); + ProgressPrinter printer("Parsing transforms RDF file"); + std::vector<Transform> transforms = factory.getTransforms(&printer); + if (!factory.isOK()) { + cerr << "WARNING: FeatureExtractionManager::addFeatureExtractorFromFile: Failed to parse transforms file: " << factory.getErrorString().toStdString() << endl; + if (factory.isRDF()) { + return false; // no point trying it as XML + } + } + if (!transforms.empty()) { + bool success = true; + for (int i = 0; i < (int)transforms.size(); ++i) { + if (!addFeatureExtractor(transforms[i], writers)) { + success = false; + } + } + return success; + } + + QFile file(transformXmlFile); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + cerr << "ERROR: Failed to open transform XML file \"" + << transformXmlFile.toStdString() << "\" for reading" << endl; + return false; + } + + QTextStream *qts = new QTextStream(&file); + QString qs = qts->readAll(); + delete qts; + file.close(); + + Transform transform(qs); + + return addFeatureExtractor(transform, writers); +} + +void FeatureExtractionManager::extractFeatures(QString audioSource) +{ + if (m_plugins.empty()) return; + + ProgressPrinter printer("Retrieving audio data..."); + + FileSource source(audioSource, &printer); + if (!source.isAvailable()) { + cerr << "ERROR: File or URL \"" << audioSource.toStdString() + << "\" could not be located" << endl; + exit(1); + } + + source.waitForData(); + + if (QFileInfo(audioSource).suffix().toLower() == "m3u") { + PlaylistFileReader reader(source); + if (reader.isOK()) { + vector<QString> files = reader.load(); + for (int i = 0; i < (int)files.size(); ++i) { + extractFeatures(files[i]); + } + return; + } else { + cerr << "ERROR: Playlist \"" << audioSource.toStdString() + << "\" could not be opened" << endl; + exit(1); + } + } + + if (m_sampleRate == 0) { + cerr << "ERROR: Internal error in FeatureExtractionManager::extractFeatures: Plugin list is non-empty, but no sample rate set" << endl; + exit(1); + } + + AudioFileReader *reader = + AudioFileReaderFactory::createReader(source, m_sampleRate, &printer); + + if (!reader) { + cerr << "ERROR: File or URL \"" << audioSource.toStdString() + << "\" could not be opened" << endl; + exit(1); + } + + size_t channels = reader->getChannelCount(); + + cerr << "Opened " << channels << "-channel file or URL \"" << audioSource.toStdString() << "\"" << endl; + + // reject file if it has too few channels, plugin will handle if it has too many + if ((int)channels < m_channels) { + //!!! should not be terminating here! + cerr << "ERROR: File or URL \"" << audioSource.toStdString() << "\" has less than " << m_channels << " channels" << endl; + exit(1); + } + + // allocate audio buffers + float **data = new float *[m_channels]; + for (int c = 0; c < m_channels; ++c) { + data[c] = new float[m_blockSize]; + } + + size_t frameCount = reader->getFrameCount(); + +// cerr << "file has " << frameCount << " frames" << endl; + + for (PluginMap::iterator pi = m_plugins.begin(); + pi != m_plugins.end(); ++pi) { + + Plugin *plugin = pi->first; + +// std::cerr << "Calling reset on " << plugin << std::endl; + plugin->reset(); + + for (TransformWriterMap::iterator ti = pi->second.begin(); + ti != pi->second.end(); ++ti) { + + const Transform &transform = ti->first; + + //!!! we may want to set the start and duration times for extraction + // in the transform record (defaults of zero indicate extraction + // from the whole file) +// transform.setStartTime(RealTime::zeroTime); +// transform.setDuration +// (RealTime::frame2RealTime(reader->getFrameCount(), m_sampleRate)); + + string outputId = transform.getOutput().toStdString(); + if (m_pluginOutputs[plugin].find(outputId) == + m_pluginOutputs[plugin].end()) { + //!!! throw? + cerr << "WARNING: Nonexistent plugin output \"" << outputId << "\" requested for transform \"" + << transform.getIdentifier().toStdString() << "\", ignoring this transform" + << endl; +/* + cerr << "Known outputs for all plugins are as follows:" << endl; + for (PluginOutputMap::const_iterator k = m_pluginOutputs.begin(); + k != m_pluginOutputs.end(); ++k) { + cerr << "Plugin " << k->first << ": "; + if (k->second.empty()) { + cerr << "(none)"; + } + for (OutputMap::const_iterator i = k->second.begin(); + i != k->second.end(); ++i) { + cerr << "\"" << i->first << "\" "; + } + cerr << endl; + } +*/ + } + } + } + + long startFrame = 0; + long endFrame = frameCount; + +/*!!! No -- there is no single transform to pull this stuff from -- + * the transforms may have various start and end times, need to be far + * cleverer about this if we're going to support them + + RealTime trStartRT = transform.getStartTime(); + RealTime trDurationRT = transform.getDuration(); + + long trStart = RealTime::realTime2Frame(trStartRT, m_sampleRate); + long trDuration = RealTime::realTime2Frame(trDurationRT, m_sampleRate); + + if (trStart == 0 || trStart < startFrame) { + trStart = startFrame; + } + + if (trDuration == 0) { + trDuration = endFrame - trStart; + } + if (trStart + trDuration > endFrame) { + trDuration = endFrame - trStart; + } + + startFrame = trStart; + endFrame = trStart + trDuration; +*/ + + for (PluginMap::iterator pi = m_plugins.begin(); + pi != m_plugins.end(); ++pi) { + + for (TransformWriterMap::const_iterator ti = pi->second.begin(); + ti != pi->second.end(); ++ti) { + + const vector<FeatureWriter *> &writers = ti->second; + + for (int j = 0; j < (int)writers.size(); ++j) { + FeatureWriter::TrackMetadata m; + m.title = reader->getTitle(); + m.maker = reader->getMaker(); + writers[j]->setTrackMetadata(audioSource, m); + } + } + } + + ProgressPrinter extractionProgress("Extracting and writing features..."); + int progress = 0; + + for (long i = startFrame; i < endFrame; i += m_blockSize) { + + //!!! inefficient, although much of the inefficiency may be + // susceptible to optimisation + + SampleBlock frames; + reader->getInterleavedFrames(i, m_blockSize, frames); + + // We have to do our own channel handling here; we can't just + // leave it to the plugin adapter because the same plugin + // adapter may have to serve for input files with various + // numbers of channels (so the adapter is simply configured + // with a fixed channel count, generally 1). + + int rc = reader->getChannelCount(); + + for (int j = 0; j < m_blockSize; ++j) { + for (int c = 0; c < m_channels; ++c) { + int index; + if (c < rc) { + index = j * rc + c; + data[c][j] = 0.f; + } else { + index = j * rc + (c % rc); + } + if (index < (int)frames.size()) { + data[c][j] += frames[index]; + } + } + } + + Vamp::RealTime timestamp = Vamp::RealTime::frame2RealTime + (i, m_sampleRate); + + for (PluginMap::iterator pi = m_plugins.begin(); + pi != m_plugins.end(); ++pi) { + + Plugin *plugin = pi->first; + Plugin::FeatureSet featureSet = plugin->process(data, timestamp); + + if (!m_summariesOnly) { + writeFeatures(audioSource, plugin, featureSet); + } + } + + int pp = progress; + progress = ((i - startFrame) * 100) / (endFrame - startFrame); + if (progress > pp) extractionProgress.setProgress(progress); + } + + for (PluginMap::iterator pi = m_plugins.begin(); + pi != m_plugins.end(); ++pi) { + + Plugin *plugin = pi->first; + Plugin::FeatureSet featureSet = plugin->getRemainingFeatures(); + + if (!m_summariesOnly) { + writeFeatures(audioSource, plugin, featureSet); + } + + if (!m_summaries.empty()) { + PluginSummarisingAdapter *adapter = + dynamic_cast<PluginSummarisingAdapter *>(plugin); + if (!adapter) { + cerr << "WARNING: Summaries requested, but plugin is not a summarising adapter" << endl; + } else { + for (SummaryNameSet::const_iterator sni = m_summaries.begin(); + sni != m_summaries.end(); ++sni) { + featureSet.clear(); + //!!! problem here -- we are requesting summaries + //!!! for all outputs, but they in principle have + //!!! different averaging requirements depending + //!!! on whether their features have duration or + //!!! not + featureSet = adapter->getSummaryForAllOutputs + (getSummaryType(*sni), + PluginSummarisingAdapter::ContinuousTimeAverage); + writeFeatures(audioSource, plugin, featureSet,//!!! *sni); + Transform::stringToSummaryType(sni->c_str())); + } + } + } + + writeSummaries(audioSource, plugin); + } + + finish(); + + extractionProgress.setProgress(100); + + TempDirectory::getInstance()->cleanup(); +} + +void +FeatureExtractionManager::writeSummaries(QString audioSource, Plugin *plugin) +{ + // caller should have ensured plugin is in m_plugins + PluginMap::iterator pi = m_plugins.find(plugin); + + for (TransformWriterMap::const_iterator ti = pi->second.begin(); + ti != pi->second.end(); ++ti) { + + const Transform &transform = ti->first; + const vector<FeatureWriter *> &writers = ti->second; + + Transform::SummaryType summaryType = transform.getSummaryType(); + PluginSummarisingAdapter::SummaryType pType = + (PluginSummarisingAdapter::SummaryType)summaryType; + + if (transform.getSummaryType() == Transform::NoSummary) { + continue; + } + + PluginSummarisingAdapter *adapter = + dynamic_cast<PluginSummarisingAdapter *>(plugin); + if (!adapter) { + cerr << "FeatureExtractionManager::writeSummaries: INTERNAL ERROR: Summary requested for transform, but plugin is not a summarising adapter" << endl; + continue; + } + + Plugin::FeatureSet featureSet = adapter->getSummaryForAllOutputs + (pType, PluginSummarisingAdapter::ContinuousTimeAverage); + +// cout << "summary type " << int(pType) << " for transform:" << endl << transform.toXmlString().toStdString()<< endl << "... feature set with " << featureSet.size() << " elts" << endl; + + writeFeatures(audioSource, plugin, featureSet, summaryType); + } +} + +void FeatureExtractionManager::writeFeatures(QString audioSource, + Plugin *plugin, + const Plugin::FeatureSet &features, + Transform::SummaryType summaryType) +{ + // caller should have ensured plugin is in m_plugins + PluginMap::iterator pi = m_plugins.find(plugin); + + for (TransformWriterMap::const_iterator ti = pi->second.begin(); + ti != pi->second.end(); ++ti) { + + const Transform &transform = ti->first; + const vector<FeatureWriter *> &writers = ti->second; + + if (transform.getSummaryType() != Transform::NoSummary && + m_summaries.empty() && + summaryType == Transform::NoSummary) { + continue; + } + + if (transform.getSummaryType() != Transform::NoSummary && + summaryType != Transform::NoSummary && + transform.getSummaryType() != summaryType) { + continue; + } + + string outputId = transform.getOutput().toStdString(); + + if (m_pluginOutputs[plugin].find(outputId) == + m_pluginOutputs[plugin].end()) { + continue; + } + + const Plugin::OutputDescriptor &desc = + m_pluginOutputs[plugin][outputId]; + + int outputIndex = m_pluginOutputIndices[outputId]; + Plugin::FeatureSet::const_iterator fsi = features.find(outputIndex); + if (fsi == features.end()) continue; + + for (int j = 0; j < (int)writers.size(); ++j) { + writers[j]->write + (audioSource, transform, desc, fsi->second, + Transform::summaryTypeToString(summaryType).toStdString()); + } + } +} + +void FeatureExtractionManager::finish() +{ + for (PluginMap::iterator pi = m_plugins.begin(); + pi != m_plugins.end(); ++pi) { + + for (TransformWriterMap::iterator ti = pi->second.begin(); + ti != pi->second.end(); ++ti) { + + vector<FeatureWriter *> &writers = ti->second; + + for (int i = 0; i < (int)writers.size(); ++i) { + writers[i]->flush(); + writers[i]->finish(); + } + } + } +} + +void FeatureExtractionManager::print(Transform transform) const +{ + QString qs; + QTextStream qts(&qs); + transform.toXml(qts); + cerr << qs.toStdString() << endl; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/FeatureExtractionManager.h Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,110 @@ +/* -*- 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-2008 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 _FEATURE_EXTRACTION_MANAGER_H_ +#define _FEATURE_EXTRACTION_MANAGER_H_ + +#include <vector> +#include <set> +#include <string> + +#include <vamp-hostsdk/Plugin.h> +#include <vamp-hostsdk/PluginSummarisingAdapter.h> +#include <transform/Transform.h> + +using std::vector; +using std::set; +using std::string; +using std::pair; +using std::map; + +class FeatureWriter; + +class FeatureExtractionManager +{ +public: + FeatureExtractionManager(); + virtual ~FeatureExtractionManager(); + + void setChannels(int channels); + void setDefaultSampleRate(int sampleRate); + + bool setSummaryTypes(const set<string> &summaryTypes, + bool summariesOnly, + const Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries &boundaries); + + bool addFeatureExtractor(Transform transform, + const vector<FeatureWriter*> &writers); + + bool addFeatureExtractorFromFile(QString transformXmlFile, + const vector<FeatureWriter*> &writers); + + bool addDefaultFeatureExtractor(TransformId transformId, + const vector<FeatureWriter*> &writers); + + void extractFeatures(QString audioSource); + +private: + // A plugin may have many outputs, so we can have more than one + // transform requested for a single plugin. The things we want to + // run in our process loop are plugins rather than their outputs, + // so we maintain a map from the plugins to the transforms desired + // of them and then iterate through this map + + typedef map<Transform, vector<FeatureWriter *> > TransformWriterMap; + typedef map<Vamp::Plugin *, TransformWriterMap> PluginMap; + PluginMap m_plugins; + + // And a map back from transforms to their plugins. Note that + // this is keyed by transform, not transform ID -- two differently + // configured transforms with the same ID must use different + // plugin instances. + + typedef map<Transform, Vamp::Plugin *> TransformPluginMap; + TransformPluginMap m_transformPluginMap; + + // Cache the plugin output descriptors, mapping from plugin to a + // map from output ID to output descriptor. + typedef map<string, Vamp::Plugin::OutputDescriptor> OutputMap; + typedef map<Vamp::Plugin *, OutputMap> PluginOutputMap; + PluginOutputMap m_pluginOutputs; + + // Map from plugin output identifier to plugin output index + typedef map<string, int> OutputIndexMap; + OutputIndexMap m_pluginOutputIndices; + + typedef set<std::string> SummaryNameSet; + SummaryNameSet m_summaries; + bool m_summariesOnly; + Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries m_boundaries; + + void writeSummaries(QString audioSource, Vamp::Plugin *); + + void writeFeatures(QString audioSource, + Vamp::Plugin *, + const Vamp::Plugin::FeatureSet &, + Transform::SummaryType summaryType = + Transform::NoSummary); + void finish(); + + int m_blockSize; + int m_defaultSampleRate; + int m_sampleRate; + int m_channels; + + void print(Transform transform) const; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/FeatureWriterFactory.cpp Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,49 @@ +/* -*- 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-2008 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 "FeatureWriterFactory.h" + +#include "DefaultFeatureWriter.h" +#include "rdf/RDFFeatureWriter.h" +#include "AudioDBFeatureWriter.h" +#include "transform/CSVFeatureWriter.h" + +set<string> +FeatureWriterFactory::getWriterTags() +{ + set<string> tags; + tags.insert("default"); + tags.insert("rdf"); + tags.insert("audiodb"); + tags.insert("csv"); + return tags; +} + +FeatureWriter * +FeatureWriterFactory::createWriter(string tag) +{ + if (tag == "default") { + return new DefaultFeatureWriter(); + } else if (tag == "rdf") { + return new RDFFeatureWriter(); + } else if (tag == "audiodb") { + return new AudioDBFeatureWriter(); + } else if (tag == "csv") { + return new CSVFeatureWriter(); + } + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/FeatureWriterFactory.h Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,36 @@ +/* -*- 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-2008 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 _FEATURE_WRITER_FACTORY_H_ +#define _FEATURE_WRITER_FACTORY_H_ + +#include <set> +#include <string> + +using std::set; +using std::string; + +class FeatureWriter; + +class FeatureWriterFactory +{ +public: + static set<string> getWriterTags(); + static FeatureWriter *createWriter(string tag); +}; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/main.cpp Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,735 @@ +/* -*- 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-2008 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 <vector> +#include <string> +#include <iostream> + +#include <QCoreApplication> +#include <QSettings> +#include <QStringList> +#include <QString> +#include <QFileInfo> +#include <QDir> + +using std::cout; +using std::cerr; +using std::endl; +using std::vector; +using std::string; + +#include "base/Exceptions.h" +#include "base/TempDirectory.h" + +#include "data/fileio/AudioFileReaderFactory.h" +#include "data/fileio/PlaylistFileReader.h" + +#include "transform/Transform.h" +#include "transform/TransformFactory.h" + +#include "FeatureExtractionManager.h" +#include "transform/FeatureWriter.h" +#include "FeatureWriterFactory.h" + +#include "rdf/RDFTransformFactory.h" + +#include <vamp-hostsdk/PluginSummarisingAdapter.h> + +#ifdef HAVE_FFTW3 +#include <fftw3.h> +#endif + +// Desired options: +// +// * output preference: +// - all data in one file +// - one file per input file +// - one file per input file per transform +// - (any use for: one file per transform?) +// +// * output location: +// - same directory as input file +// - current directory +// +// * output filename: +// - based on input (obvious choice for one file per input file modes) +// - specified on command line (obvious choice for all in one file mode) +// +// * output format: one or more of +// - RDF +// - AudioDB +// - Vamp Simple Host format +// - CSV +// +// * input handling: +// - run each transform on each input file separately +// - provide all input files to the same transform, one per channel +// +// * format-specific options: +// - RDF format: fancy/plain RDF +// - CSV format: separator, timestamp type +// note: do the output file/location also count as format-specific options? +// an output writer that wrote to a database would have different options... +// +// * debug level and progress output +// +// * other potential options: +// - ignore version mismatches in Transform specifications +// - sample rate: force a given rate; use file rate instead of rate in +// Transform spec +// +// * other potential instructions: +// - write out a skeleton Transform file for a specified plugin +// - write out skeleton RDF for a plugin library (i.e. do the job of +// RDF template_generator) +// - verify that RDF for a plugin library matches the plugin +// +// MAYBE: +// * transform(s) to run: +// - supply transform file names on command line +// - use all transforms found in a given directory? +// +// MAYBE: +// * input files to transform: +// - supply file names or URIs on command line +// - use all files in a given directory or tree + +static QString +wrap(QString s, int len, int pfx = 0) +{ + QString ws; + QStringList sl(s.split(' ')); + int i = 0, c = 0; + while (i < sl.size()) { + int wl = sl[i].length(); + if (c + wl < len) { + if (c > 0) { + ws += ' '; + ++c; + } + } else { + if (c > 0) { + ws += '\n'; + for (int j = 0; j < pfx; ++j) ws += ' '; + c = 0; + } + } + ws += sl[i]; + c += wl; + ++i; + } + return ws; +} + +void usage(QString myname) +{ + set<string> writers = FeatureWriterFactory::getWriterTags(); + + cerr << endl; + cerr << "Sonic Annotator" << endl; + cerr << "A utility for batch feature extraction from audio files." << endl; + cerr << "Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London." << endl; + cerr << "Copyright 2007-2008 Queen Mary, University of London." << endl; + cerr << endl; + cerr << "This program is free software. You may redistribute copies of it under the" << endl; + cerr << "terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>." << endl; + cerr << "This program is supplied with NO WARRANTY, to the extent permitted by law." << endl; + cerr << endl; + cerr << " Usage: " << myname.toStdString() + << " [-mr] -t trans.xml [...] -w <writer> [...] <audio> [...]" << endl; + cerr << " " << myname.toStdString() + << " [-mr] -T trans.txt [...] -w <writer> [...] <audio> [...]" << endl; + cerr << " " << myname.toStdString() + << " -s <transform>" << endl; + cerr << " " << myname.toStdString() + << " [-lh]" << endl; + cerr << endl; + cerr << "Where <audio> is an audio file or URL to use as input: either a local file" << endl; + cerr << "path, local \"file://\" URL, or remote \"http://\" or \"ftp://\" URL." << endl; + cerr << endl; + + QString extensions = AudioFileReaderFactory::getKnownExtensions(); + QStringList extlist = extensions.split(" ", QString::SkipEmptyParts); + if (!extlist.empty()) { + cerr << "The following audio file extensions are recognised:" << endl; + cerr << " "; + int c = 2; + for (int i = 0; i < extlist.size(); ++i) { + QString ext = extlist[i]; + if (ext.startsWith("*.")) ext = ext.right(ext.length()-2); + c += ext.length() + 2; + if (c >= 80) { + cerr << "\n "; + c -= 78; + } + cerr << ext.toStdString(); + if (i + 1 == extlist.size()) cerr << "."; + else cerr << ", "; + } + cerr << endl; + } + + cerr << "Playlist files in M3U format are also supported." << endl; + cerr << endl; + cerr << "Transformation options:" << endl; + cerr << endl; + cerr << " -t, --transform <T> Apply transform described in transform file <T> to" << endl; + cerr << " all input audio files. You may supply this option" << endl; + cerr << " multiple times. You must supply this option or -T at" << endl; + cerr << " least once for any work to be done. Transform format" << endl; + cerr << " may be SV transform XML or Vamp transform RDF. See" << endl; + cerr << " documentation for examples." << endl; + cerr << endl; + cerr << " -T, --transforms <T> Apply all transforms described in transform files" << endl; + cerr << " whose names are listed in text file <T>. You may supply" << endl; + cerr << " this option multiple times." << endl; + cerr << endl; + cerr << " -d, --default <I> Apply the default transform for transform id <I>. This" << endl; + cerr << " is equivalent to generating a skeleton transform for this" << endl; + cerr << " id (using the -s option, below) and then applying that," << endl; + cerr << " unmodified, with the -t option in the normal way. Note" << endl; + cerr << " that the results may vary as the implementation's default" << endl; + cerr << " processing parameters are not guaranteed. Do not use" << endl; + cerr << " this in production systems. You may supply this option" << endl; + cerr << " multiple times, and mix it with -t and -T." << endl; + cerr << endl; + cerr << " -w, --writer <W> Write output using writer type <W>." << endl; + cerr << " Supported writer types are: "; + for (set<string>::const_iterator i = writers.begin(); + i != writers.end(); ) { + cerr << *i; + if (++i != writers.end()) cerr << ", "; + else cerr << "."; + } + cerr << endl; + cerr << " You may supply this option multiple times. You must" << endl; + cerr << " supply this option at least once for any work to be done." << endl; + cerr << endl; + cerr << " -S, --summary <S> In addition to the result features, write summary feature" << endl; + cerr << " of summary type <S>." << endl; + cerr << " Supported summary types are: min, max, mean, median, mode," << endl; + cerr << " sum, variance, sd, count." << endl; + cerr << " You may supply this option multiple times." << endl; + cerr << endl; + cerr << " --summary-only Write only summary features; do not write the regular" << endl; + cerr << " result features." << endl; + cerr << endl; + cerr << " --segments <A>,<B>[,...]" << endl; + cerr << " Summarise in segments, with segment boundaries" << endl; + cerr << " at A, B, ... seconds." << endl; + cerr << endl; + +/*!!! This feature not implemented yet (sniff) + cerr << " -m, --multiplex If multiple input audio files are given, use mono" << endl; + cerr << " mixdowns of all files as the input channels for a single" << endl; + cerr << " invocation of each transform, instead of running the" << endl; + cerr << " transform against all files separately." << endl; + cerr << endl; +*/ + + cerr << " -r, --recursive If any of the <audio> arguments is found to be a local" << endl; + cerr << " directory, search the tree starting at that directory" << endl; + cerr << " for all supported audio files and take all of those as" << endl; + cerr << " input instead." << endl; + cerr << endl; + cerr << "Housekeeping options:" << endl; + cerr << endl; + cerr << " -l, --list List all known transform ids to standard output." << endl; + cerr << endl; + cerr << " -s, --skeleton <I> Generate a skeleton transform file for transform id <I>" << endl; + cerr << " and write it to standard output." << endl; + cerr << endl; + cerr << " -h, --help Show this help." << endl; + + cerr << endl; + cerr << "If no -w (or --writer) options are supplied, either the -l -s or -h option (or" << endl; + cerr << "long equivalent) must be given instead." << endl; + + for (set<string>::const_iterator i = writers.begin(); + i != writers.end(); ++i) { + FeatureWriter *w = FeatureWriterFactory::createWriter(*i); + if (!w) { + cerr << " (Internal error: failed to create writer of this type)" << endl; + continue; + } + FeatureWriter::ParameterList params = w->getSupportedParameters(); + delete w; + if (params.empty()) { + continue; + } + cerr << endl; + cerr << "Additional options for writer type \"" << *i << "\":" << endl; + cerr << endl; + for (FeatureWriter::ParameterList::const_iterator j = params.begin(); + j != params.end(); ++j) { + cerr << " --" << *i << "-" << j->name << " "; + int spaceage = 16 - int(i->length()) - int(j->name.length()); + if (j->hasArg) { cerr << "<X> "; spaceage -= 4; } + for (int k = 0; k < spaceage; ++k) cerr << " "; + QString s(j->description.c_str()); + s = wrap(s, 56, 22); + cerr << s.toStdString() << endl; + } + } + + cerr << endl; + exit(0); +} + +void +listTransforms() +{ + TransformList transforms = + TransformFactory::getInstance()->getAllTransformDescriptions(); + + for (TransformList::const_iterator iter = transforms.begin(); + iter != transforms.end(); ++iter) { + const TransformDescription &transform = *iter; + if (transform.type == TransformDescription::Analysis) { + cout << transform.identifier.toStdString() << endl; + } + } +} + +void +printSkeleton(QString id) +{ + Transform transform = + TransformFactory::getInstance()->getDefaultTransformFor(id); + cout << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> ." << endl + << "@prefix vamp: <http://purl.org/ontology/vamp/> ." << endl + << "@prefix : <#> ." << endl << endl; + QString rdf = RDFTransformFactory::writeTransformToRDF + (transform, ":transform"); + cout << rdf.toStdString(); +} + +void +findSourcesRecursive(QString dirname, QStringList &addTo, int &found) +{ + QDir dir(dirname); + + QString printable = dir.dirName().left(20); + cerr << "\rScanning \"" << printable.toStdString() << "\"..." + << QString(" ").left(20 - printable.length()).toStdString() + << " [" << found << " audio file(s)]"; + + QString extensions = AudioFileReaderFactory::getKnownExtensions(); + QStringList extlist = extensions.split(" ", QString::SkipEmptyParts); + + QStringList files = dir.entryList + (extlist, QDir::Files | QDir::Readable); + for (int i = 0; i < files.size(); ++i) { + addTo.push_back(dir.filePath(files[i])); + ++found; + } + + QStringList subdirs = dir.entryList + (QStringList(), QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); + for (int i = 0; i < subdirs.size(); ++i) { + findSourcesRecursive(dir.filePath(subdirs[i]), addTo, found); + } +} + + +int main(int argc, char **argv) +{ + QCoreApplication application(argc, argv); + + QCoreApplication::setOrganizationName("QMUL"); + QCoreApplication::setOrganizationDomain("qmul.ac.uk"); + QCoreApplication::setApplicationName("Sonic Annotator"); + + QStringList args = application.arguments(); + set<string> requestedWriterTags; + set<string> requestedTransformFiles; + set<string> requestedTransformListFiles; + set<string> requestedDefaultTransforms; + set<string> requestedSummaryTypes; +//!!! bool multiplex = false; + bool recursive = false; + bool list = false; + bool summaryOnly = false; + QString skeletonFor = ""; + QString myname = args[0]; + myname = QFileInfo(myname).baseName(); + QStringList otherArgs; + Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries boundaries; + + QString helpStr = myname + ": use -h or --help option for help"; + + for (int i = 1; i < args.size(); ++i) { + + QString arg = args[i]; + bool last = ((i + 1) == args.size()); + + if (arg == "-h" || arg == "--help" || arg == "-?") { + usage(myname); + } + + if (arg == "-w" || arg == "--writer") { + if (last || args[i+1].startsWith("-")) { + cerr << myname.toStdString() << ": argument expected for \"" + << arg.toStdString() << "\" option" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } else { + string tag = args[++i].toStdString(); + if (requestedWriterTags.find(tag) != requestedWriterTags.end()) { + cerr << myname.toStdString() << ": NOTE: duplicate specification of writer type \"" << tag << "\" ignored" << endl; + } else { + requestedWriterTags.insert(tag); + } + continue; + } + } else if (arg == "-t" || arg == "--transform") { + if (last || args[i+1].startsWith("-")) { + cerr << myname.toStdString() << ": argument expected for \"" + << arg.toStdString() << "\" option" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } else { + string transform = args[++i].toStdString(); + if (requestedTransformFiles.find(transform) != + requestedTransformFiles.end()) { + cerr << myname.toStdString() << ": NOTE: duplicate specification of transform file \"" << transform << "\" ignored" << endl; + } else { + requestedTransformFiles.insert(transform); + } + continue; + } + } else if (arg == "-T" || arg == "--transforms") { + if (last || args[i+1].startsWith("-")) { + cerr << myname.toStdString() << ": argument expected for \"" + << arg.toStdString() << "\" option" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } else { + string transform = args[++i].toStdString(); + if (requestedTransformListFiles.find(transform) != + requestedTransformListFiles.end()) { + cerr << myname.toStdString() << ": NOTE: duplicate specification of transform list file \"" << transform << "\" ignored" << endl; + } else { + requestedTransformListFiles.insert(transform); + } + continue; + } + } else if (arg == "-d" || arg == "--default") { + if (last || args[i+1].startsWith("-")) { + cerr << myname.toStdString() << ": argument expected for \"" + << arg.toStdString() << "\" option" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } else { + string deft = args[++i].toStdString(); + if (requestedDefaultTransforms.find(deft) != + requestedDefaultTransforms.end()) { + cerr << myname.toStdString() << ": NOTE: duplicate specification of default transform \"" << deft << "\" ignored" << endl; + } else { + requestedDefaultTransforms.insert(deft); + } + continue; + } + } else if (arg == "-S" || arg == "--summary") { + if (last || args[i+1].startsWith("-")) { + cerr << myname.toStdString() << ": argument expected for \"" + << arg.toStdString() << "\" option" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } else { + string summary = args[++i].toStdString(); + requestedSummaryTypes.insert(summary); + continue; + } + } else if (arg == "--summary-only") { + summaryOnly = true; + continue; + } else if (arg == "--segments") { + if (last) { + cerr << myname.toStdString() << ": argument expected for \"" + << arg.toStdString() << "\" option" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } else { + string segmentSpec = args[++i].toStdString(); + QStringList segmentStrs = QString(segmentSpec.c_str()).split(','); + for (int j = 0; j < segmentStrs.size(); ++j) { + bool good = false; + boundaries.insert(Vamp::RealTime::fromSeconds + (segmentStrs[j].toDouble(&good))); + if (!good) { + cerr << myname.toStdString() << ": segment boundaries must be numeric" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } + } + } +/*!!! + } else if (arg == "-m" || arg == "--multiplex") { + multiplex = true; + cerr << myname.toStdString() + << ": WARNING: Multiplex argument not yet implemented" << endl; //!!! + continue; +*/ + } else if (arg == "-r" || arg == "--recursive") { + recursive = true; + continue; + } else if (arg == "-l" || arg == "--list") { + list = true; + continue; + } else if (arg == "-s" || arg == "--skeleton") { + if (last || args[i+1].startsWith("-")) { + cerr << myname.toStdString() << ": usage: " + << myname.toStdString() << " " << arg.toStdString() + << " <transform>" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } else { + skeletonFor = args[++i]; + continue; + } + } else { + otherArgs.push_back(args[i]); + } + } + + if (list) { + if (!requestedWriterTags.empty() || skeletonFor != "") { + cerr << helpStr.toStdString() << endl; + exit(2); + } + listTransforms(); + exit(0); + } + if (skeletonFor != "") { + if (!requestedWriterTags.empty()) { + cerr << helpStr.toStdString() << endl; + exit(2); + } + printSkeleton(skeletonFor); + exit(0); + } + + if (requestedTransformFiles.empty() && + requestedTransformListFiles.empty() && + requestedDefaultTransforms.empty()) { + cerr << myname.toStdString() + << ": no transform(s) specified" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } + + if (requestedWriterTags.empty()) { + cerr << myname.toStdString() + << ": no writer(s) specified" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } + + if (!boundaries.empty()) { + if (requestedSummaryTypes.empty()) { + cerr << myname.toStdString() + << ": summary segment boundaries provided, but no summary type specified" + << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } + } + +#ifdef HAVE_FFTW3 + QSettings settings; + settings.beginGroup("FFTWisdom"); + QString wisdom = settings.value("wisdom").toString(); + if (wisdom != "") { + fftw_import_wisdom_from_string(wisdom.toLocal8Bit().data()); + } + settings.endGroup(); +#endif + + FeatureExtractionManager manager; + + if (!requestedSummaryTypes.empty()) { + if (!manager.setSummaryTypes(requestedSummaryTypes, + summaryOnly, + boundaries)) { + cerr << myname.toStdString() + << ": failed to set requested summary types" << endl; + exit(1); + } + } + + // the manager dictates the sample rate and number of channels + // to work at - files with too few channels are rejected, + // too many channels are handled as usual by the Vamp plugin + + //!!! Review this: although we probably do want to fix the channel + // count here, we don't necessarily want to fix the rate: it's + // specified in the Transform file. + + manager.setDefaultSampleRate(44100); + manager.setChannels(1); + + vector<FeatureWriter *> writers; + + for (set<string>::const_iterator i = requestedWriterTags.begin(); + i != requestedWriterTags.end(); ++i) { + + FeatureWriter *writer = FeatureWriterFactory::createWriter(*i); + + if (!writer) { + cerr << myname.toStdString() << ": unknown feature writer \"" + << *i << "\"" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } + + map<string, string> writerArgs; + FeatureWriter::ParameterList pl(writer->getSupportedParameters()); + + for (int k = 0; k < pl.size(); ++k) { + + string argbase = pl[k].name; + QString literal = QString("--%1-%2") + .arg(i->c_str()).arg(argbase.c_str()); + + for (int j = 0; j < otherArgs.size(); ) { + + if (otherArgs[j] != literal) { + ++j; + continue; + } + + otherArgs.removeAt(j); + + if (pl[k].hasArg) { + if (j < otherArgs.size()) { + writerArgs[argbase] = otherArgs[j].toStdString(); + otherArgs.removeAt(j); + } else { + cerr << myname.toStdString() << ": " + << "argument required for \"" + << literal.toStdString() << "\" option" + << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } + } else { + writerArgs[argbase] = ""; + } + } + } + + writer->setParameters(writerArgs); + + writers.push_back(writer); + } + + for (int i = 0; i < otherArgs.size(); ++i) { + if (otherArgs[i].startsWith("-")) { + cerr << myname.toStdString() << ": unknown option \"" + << otherArgs[i].toStdString() << "\"" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } + } + + if (otherArgs.empty()) { + cerr << myname.toStdString() << ": no input(s) specified" << endl; + cerr << helpStr.toStdString() << endl; + exit(2); + } + + for (set<string>::const_iterator i = requestedTransformListFiles.begin(); + i != requestedTransformListFiles.end(); ++i) { + PlaylistFileReader reader(i->c_str()); + if (reader.isOK()) { + vector<QString> files = reader.load(); + for (int j = 0; j < files.size(); ++j) { + requestedTransformFiles.insert(files[j].toStdString()); + } + } else { + cerr << myname.toStdString() << ": failed to read template list file \"" << *i << "\"" << endl; + exit(2); + } + } + + bool haveFeatureExtractor = false; + + for (set<string>::const_iterator i = requestedTransformFiles.begin(); + i != requestedTransformFiles.end(); ++i) { + if (manager.addFeatureExtractorFromFile(i->c_str(), writers)) { + haveFeatureExtractor = true; + } + } + + for (set<string>::const_iterator i = requestedDefaultTransforms.begin(); + i != requestedDefaultTransforms.end(); ++i) { + if (manager.addDefaultFeatureExtractor(i->c_str(), writers)) { + haveFeatureExtractor = true; + } + } + + if (!haveFeatureExtractor) { + cerr << myname.toStdString() << ": no feature extractors added" << endl; + exit(2); + } + + QStringList sources; + if (!recursive) { + sources = otherArgs; + } else { + for (QStringList::const_iterator i = otherArgs.begin(); + i != otherArgs.end(); ++i) { + if (QDir(*i).exists()) { + cerr << "Directory found and recursive flag set, scanning for audio files..." << endl; + int found = 0; + findSourcesRecursive(*i, sources, found); + cerr << "\rDone, found " << found << " supported audio file(s) " << endl; + } else { + sources.push_back(*i); + } + } + } + + for (QStringList::const_iterator i = sources.begin(); + i != sources.end(); ++i) { + std::cerr << "Extracting features for: \"" << i->toStdString() << "\"" << std::endl; + try { + manager.extractFeatures(*i); + } catch (FailedToOpenFile f) { + cerr << "ERROR: Failed to open output file for feature writer: " + << f.what() << endl; + break; + } + } + + for (int i = 0; i < writers.size(); ++i) delete writers[i]; + +#ifdef HAVE_FFTW3 + settings.beginGroup("FFTWisdom"); + char *cwisdom = fftw_export_wisdom_to_string(); + if (cwisdom) { + settings.setValue("wisdom", cwisdom); + fftw_free(cwisdom); + } + settings.endGroup(); +#endif + + TempDirectory::getInstance()->cleanup(); + + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/runner.pro Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,71 @@ + +TEMPLATE = app + +SV_UNIT_PACKAGES = vamp vamp-hostsdk samplerate mad id3tag oggz fishsound sndfile lrdf redland rasqal raptor + +#linux-g++:LIBS += -Wl,-Bstatic +#linux-g++:DEFINES += BUILD_STATIC + +load(../sonic-visualiser/sv.prf) + +LIBPATH += /usr/local/lib + +CONFIG += sv qt thread warn_on stl rtti exceptions console +QT += xml network +QT -= gui + +# Using the "console" CONFIG flag above should ensure this happens for +# normal Windows builds, but the console feature doesn't get picked up +# in my local cross-compile setup because qmake itself doesn't know to +# look for win32 features +win32-x-g++:QMAKE_LFLAGS += -Wl,-subsystem,console + +# If you have compiled your Vamp plugin SDK with FFTW (using its +# HAVE_FFTW3 flag), you can define the same flag here to ensure the +# program saves and restores FFTW wisdom in its configuration properly +# +#DEFINES += HAVE_FFTW3 + +TARGET = sonic-annotator + +DEPENDPATH += . ../sonic-visualiser i18n main +INCLUDEPATH += . ../sonic-visualiser main +LIBPATH = ../sonic-visualiser/audioio ../sonic-visualiser/data ../sonic-visualiser/plugin ../sonic-visualiser/rdf ../sonic-visualiser/transform ../sonic-visualiser/base ../sonic-visualiser/system $$LIBPATH + +QMAKE_CXXFLAGS_RELEASE += -fmessage-length=80 -fdiagnostics-show-location=every-line + +contains(DEFINES, BUILD_STATIC):LIBS -= -ljack + +#LIBS = -lsvaudioio -lsvdata -lsvtransform -lsvplugin -lsvrdf -lsvbase -lsvsystem $$LIBS +LIBS = -lsvdata -lsvtransform -lsvplugin -lsvrdf -lsvdata -lsvbase -lsvsystem $$LIBS + +PRE_TARGETDEPS += ../sonic-visualiser/audioio/libsvaudioio.a \ + ../sonic-visualiser/data/libsvdata.a \ + ../sonic-visualiser/transform/libsvtransform.a \ + ../sonic-visualiser/plugin/libsvplugin.a \ + ../sonic-visualiser/rdf/libsvrdf.a \ + ../sonic-visualiser/base/libsvbase.a \ + ../sonic-visualiser/system/libsvsystem.a + +OBJECTS_DIR = tmp_obj +MOC_DIR = tmp_moc + +# Input +HEADERS += \ + AudioDBFeatureWriter.h \ + FeatureWriterFactory.h \ + DefaultFeatureWriter.h \ + FeatureExtractionManager.h + +SOURCES += \ + main.cpp \ + DefaultFeatureWriter.cpp \ + FeatureExtractionManager.cpp \ + AudioDBFeatureWriter.cpp \ + FeatureWriterFactory.cpp + + + + +# Restore dynamic linkage, in case we went static earlier +linux-g++:LIBS += -Wl,-Bdynamic -lpthread -ldl -lz
--- a/test-queries/test-query Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ - -PREFIX vamp: <http://purl.org/ontology/vamp/> -PREFIX examples: <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins#> - -SELECT ?transform ?library ?library_id ?plugin -FROM <file:///work/runner/transforms/percussiononsets.n3> -FROM <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins> - -WHERE { - ?transform a vamp:Transform . - ?transform vamp:plugin ?plugin . - ?plugin a vamp:Plugin . - ?library a vamp:PluginLibrary . - ?library vamp:identifier ?library_id . - ?library vamp:available_plugin ?plugin . -} -
--- a/test-queries/test-query-dense-output Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ - -PREFIX vamp: <http://purl.org/ontology/vamp/> -PREFIX mo: <http://purl.org/ontology/mo/> -PREFIX af: <http://purl.org/ontology/af/> - -SELECT ?signal_source ?feature_signal_type ?value -FROM <file:///share/music/wav/12-You Look So Fine.n3> - -WHERE { - ?signal mo:available_as ?signal_source . - ?signal a mo:Signal . - ?signal af:signal_feature ?feature . - ?feature a ?feature_signal_type. - ?feature af:value ?value . -} -
--- a/test-queries/test-query-plugin-for-transform Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ - -PREFIX vamp: <http://purl.org/ontology/vamp/> - -SELECT ?transform ?plugin ?step_size ?block_size ?window_type ?program ?sample_rate ?start ?duration ?param_id ?param_value -FROM <file:///work/runner/transforms/percussiononsets.n3> - -WHERE { - ?transform a vamp:Transform . - ?transform vamp:plugin ?plugin . - OPTIONAL { ?transform vamp:step_size ?step_size } . - OPTIONAL { ?transform vamp:block_size ?block_size } . - OPTIONAL { ?transform vamp:window_type ?window_type } . - OPTIONAL { ?transform vamp:program ?program } . - OPTIONAL { ?transform vamp:sample_rate ?sample_rate } . - OPTIONAL { ?transform vamp:start ?start } . - OPTIONAL { ?transform vamp:duration ?duration } . - OPTIONAL { - ?transform vamp:parameter ?param . - ?param vamp:identifier ?param_id . - ?param vamp:value ?param_value - } -} -
--- a/test-queries/test-query-plugin-output-types Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ - -PREFIX vamp: <http://purl.org/ontology/vamp/> -PREFIX examples: <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins#> - -SELECT ?output_id ?output_type ?feature_type ?event_type ?unit -FROM <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins> - -WHERE { - ?plugin a vamp:Plugin . - ?plugin vamp:identifier "percussiononsets" . - ?plugin vamp:output_descriptor ?output . - ?output vamp:identifier ?output_id . - ?output a ?output_type . - OPTIONAL { ?output vamp:computes_feature_type ?feature_type } . - OPTIONAL { ?output vamp:computes_event_type ?event_type } . - OPTIONAL { ?output vamp:unit ?unit } . -} -
--- a/test-queries/test-query-pluginid-for-rdf Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ - -PREFIX vamp: <http://purl.org/ontology/vamp/> -PREFIX examples: <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins#> - -SELECT ?plugin ?library_id ?plugin_id -FROM <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins> - -WHERE { - ?plugin a vamp:Plugin . - ?plugin vamp:identifier ?plugin_id . - OPTIONAL { ?library a vamp:PluginLibrary . - ?library vamp:identifier ?library_id . - ?library vamp:available_plugin ?plugin . } -} -
--- a/test-queries/test-query-transform-params Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -PREFIX vamp: <http://purl.org/ontology/vamp/> - -SELECT ?transform ?param_id ?param_value - -FROM <file:///work/runner/transforms/percussiononsets.n3> - -WHERE { - ?transform vamp:parameter_binding ?binding . - ?binding vamp:parameter ?param . - ?param vamp:identifier ?param_id . - ?binding vamp:value ?param_value . - } -
--- a/test-queries/test-query-transforms Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ - -PREFIX vamp: <http://purl.org/ontology/vamp/> - -SELECT ?transform ?plugin ?output ?program - ?step_size ?block_size ?window_type - ?sample_rate ?start ?duration - -FROM <file:///work/runner/transforms/percussiononsets.n3> - -WHERE { - ?transform a vamp:Transform ; - vamp:plugin ?plugin . - OPTIONAL { ?transform vamp:output ?output } . - OPTIONAL { ?transform vamp:program ?program } . - OPTIONAL { ?transform vamp:step_size ?step_size } . - OPTIONAL { ?transform vamp:block_size ?block_size } . - OPTIONAL { ?transform vamp:window_type ?window_type } . - OPTIONAL { ?transform vamp:sample_rate ?sample_rate } . - OPTIONAL { ?transform vamp:start ?start } . - OPTIONAL { ?transform vamp:duration ?duration } -}
--- a/test-queries/test-roqet Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ - -PREFIX : <file:///work/runner/transforms/test.n3#> - -SELECT ?result ?mand - -FROM <file:///work/runner/transforms/test.n3> - -WHERE { - ?result :mand ?mand . - OPTIONAL { ?result :opt1 ?opt1 } . - OPTIONAL { ?result :opt2 ?opt2 } . -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/misc-queries/test-query Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,17 @@ + +PREFIX vamp: <http://purl.org/ontology/vamp/> +PREFIX examples: <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins#> + +SELECT ?transform ?library ?library_id ?plugin +FROM <file:///work/runner/transforms/percussiononsets.n3> +FROM <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins> + +WHERE { + ?transform a vamp:Transform . + ?transform vamp:plugin ?plugin . + ?plugin a vamp:Plugin . + ?library a vamp:PluginLibrary . + ?library vamp:identifier ?library_id . + ?library vamp:available_plugin ?plugin . +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/misc-queries/test-query-dense-output Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,16 @@ + +PREFIX vamp: <http://purl.org/ontology/vamp/> +PREFIX mo: <http://purl.org/ontology/mo/> +PREFIX af: <http://purl.org/ontology/af/> + +SELECT ?signal_source ?feature_signal_type ?value +FROM <file:///share/music/wav/12-You Look So Fine.n3> + +WHERE { + ?signal mo:available_as ?signal_source . + ?signal a mo:Signal . + ?signal af:signal_feature ?feature . + ?feature a ?feature_signal_type. + ?feature af:value ?value . +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/misc-queries/test-query-plugin-for-transform Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,23 @@ + +PREFIX vamp: <http://purl.org/ontology/vamp/> + +SELECT ?transform ?plugin ?step_size ?block_size ?window_type ?program ?sample_rate ?start ?duration ?param_id ?param_value +FROM <file:///work/runner/transforms/percussiononsets.n3> + +WHERE { + ?transform a vamp:Transform . + ?transform vamp:plugin ?plugin . + OPTIONAL { ?transform vamp:step_size ?step_size } . + OPTIONAL { ?transform vamp:block_size ?block_size } . + OPTIONAL { ?transform vamp:window_type ?window_type } . + OPTIONAL { ?transform vamp:program ?program } . + OPTIONAL { ?transform vamp:sample_rate ?sample_rate } . + OPTIONAL { ?transform vamp:start ?start } . + OPTIONAL { ?transform vamp:duration ?duration } . + OPTIONAL { + ?transform vamp:parameter ?param . + ?param vamp:identifier ?param_id . + ?param vamp:value ?param_value + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/misc-queries/test-query-plugin-output-types Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,18 @@ + +PREFIX vamp: <http://purl.org/ontology/vamp/> +PREFIX examples: <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins#> + +SELECT ?output_id ?output_type ?feature_type ?event_type ?unit +FROM <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins> + +WHERE { + ?plugin a vamp:Plugin . + ?plugin vamp:identifier "percussiononsets" . + ?plugin vamp:output_descriptor ?output . + ?output vamp:identifier ?output_id . + ?output a ?output_type . + OPTIONAL { ?output vamp:computes_feature_type ?feature_type } . + OPTIONAL { ?output vamp:computes_event_type ?event_type } . + OPTIONAL { ?output vamp:unit ?unit } . +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/misc-queries/test-query-pluginid-for-rdf Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,15 @@ + +PREFIX vamp: <http://purl.org/ontology/vamp/> +PREFIX examples: <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins#> + +SELECT ?plugin ?library_id ?plugin_id +FROM <http://vamp-plugins.org/rdf/plugins/vamp-example-plugins> + +WHERE { + ?plugin a vamp:Plugin . + ?plugin vamp:identifier ?plugin_id . + OPTIONAL { ?library a vamp:PluginLibrary . + ?library vamp:identifier ?library_id . + ?library vamp:available_plugin ?plugin . } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/misc-queries/test-query-transform-params Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,13 @@ +PREFIX vamp: <http://purl.org/ontology/vamp/> + +SELECT ?transform ?param_id ?param_value + +FROM <file:///work/runner/transforms/percussiononsets.n3> + +WHERE { + ?transform vamp:parameter_binding ?binding . + ?binding vamp:parameter ?param . + ?param vamp:identifier ?param_id . + ?binding vamp:value ?param_value . + } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/misc-queries/test-query-transforms Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,21 @@ + +PREFIX vamp: <http://purl.org/ontology/vamp/> + +SELECT ?transform ?plugin ?output ?program + ?step_size ?block_size ?window_type + ?sample_rate ?start ?duration + +FROM <file:///work/runner/transforms/percussiononsets.n3> + +WHERE { + ?transform a vamp:Transform ; + vamp:plugin ?plugin . + OPTIONAL { ?transform vamp:output ?output } . + OPTIONAL { ?transform vamp:program ?program } . + OPTIONAL { ?transform vamp:step_size ?step_size } . + OPTIONAL { ?transform vamp:block_size ?block_size } . + OPTIONAL { ?transform vamp:window_type ?window_type } . + OPTIONAL { ?transform vamp:sample_rate ?sample_rate } . + OPTIONAL { ?transform vamp:start ?start } . + OPTIONAL { ?transform vamp:duration ?duration } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/misc-queries/test-roqet Thu Dec 11 10:26:12 2008 +0000 @@ -0,0 +1,13 @@ + +PREFIX : <file:///work/runner/transforms/test.n3#> + +SELECT ?result ?mand + +FROM <file:///work/runner/transforms/test.n3> + +WHERE { + ?result :mand ?mand . + OPTIONAL { ?result :opt1 ?opt1 } . + OPTIONAL { ?result :opt2 ?opt2 } . +} +
--- a/vamp-example-plugins:percussiononsets.n3 Thu Dec 11 10:22:33 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. -@prefix xsd: <http://www.w3.org/2001/XMLSchema#>. -@prefix vamp: <http://www.vamp-plugins.org/ontology/> . -@prefix vampex: <http://www.vamp-plugins.org/examples/> . -@prefix owl: <http://www.w3.org/2002/07/owl#> . -@prefix dc: <http://purl.org/dc/elements/1.1/> . -@prefix af: <http://purl.org/ontology/af/> . -@prefix foaf: <http://xmlns.com/foaf/0.1/> . -@prefix cc: <http://web.resource.org/cc/> . -@prefix thisplug: <http://www.vamp-plugins.org/examples/percussiononsets#>. -@prefix : <> . - -<> a vamp:PluginDescription ; - foaf:maker <http://chrissutton.org/me> ; - foaf:primaryTopic vampex:percussiononsets . - -vampex:percussiononsets a vamp:Plugin ; - dc:title "Simple Percussion Onset Detector" ; - dc:description "Detect percussive note onsets by identifying broadband energy rises"; - foaf:maker <http://www.all-day-breakfast.com/cannam> ; #we'll pretend this is his URI - cc:license <http://creativecommons.org/licenses/BSD/> ; - vamp:identifier "percussiononsets" ; # The Vamp identifier for the plugin - vamp:vamp_API_version vamp:version_v1.1b ; # Made up - this plugin doesn't actually specify it - owl:versionInfo "2" ; - vamp:input_domain vamp:TimeDomain ; # Made up - this plugin doesn't actually specify it - - vamp:parameter_descriptor thisplug:pd1 ; - vamp:parameter_descriptor thisplug:pd2 ; - vamp:output_descriptor thisplug:od1 ; - vamp:output_descriptor thisplug:od2 . - - -thisplug:pd1 a vamp:ParameterDescriptor ; - vamp:identifier "threshold" ; - dc:title "Energy Rise threshold" ; - dc:description "Energy rise within a frequency bin necessary to count toward broadband total" ; - dc:format "dB" ; - vamp:minValue 0 ; #might be useful when interpreting plugin output - vamp:maxValue 20 ; - vamp:defaultValue 3 . - - -thisplug:pd2 a vamp:ParameterDescriptor ; - vamp:identifier "sensitivity" ; - dc:title "Sensitivity" ; - dc:description "Sensitivity of peak detector applied to broadband detection function" ; - dc:format "%" ; - vamp:minValue 0 ; #might be useful when interpreting plugin output - vamp:maxValue 100 ; - vamp:defaultValue 40 . - -thisplug:od1 a vamp:OutputDescriptor ; - vamp:identifier "onsets" ; - dc:title "Onsets" ; - dc:description "Percussive note onset locations" ; - dc:format "" ; - vamp:fixed_bin_count "true" ; - vamp:bin_count 0 ; - vamp:sample_type vamp:VariableSampleRate ; - vamp:computes_event_type af:Onset . # af:Onset is pending some thought - -thisplug:od2 a vamp:OutputDescriptor ; - vamp:identifier "detectionfunction" ; - dc:title "Detection Function" ; - dc:description "Broadband energy rise detection function"; - dc:format "" ; - vamp:fixed_bin_count "true" ; - vamp:bin_count 1 ; - vamp:sample_type vamp:OneSamplePerStep ; - vamp:computes_feature_type af:OnsetDetectionFunction .