# HG changeset patch # User Chris Cannam # Date 1228991172 0 # Node ID 92911f967a161dec4cf04c52d463945e464f472e # Parent 581b1b150a4d7d953ff1bb4210d69160bb7d2c2c * some reorganisation diff -r 581b1b150a4d -r 92911f967a16 AudioDBFeatureWriter.cpp --- 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 - -#include - -#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::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 ¶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 - // /. - // with timestamps in a corresponding /..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; -} - - diff -r 581b1b150a4d -r 92911f967a16 AudioDBFeatureWriter.h --- 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 -#include - -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 ¶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 dbfiles; - - bool openDBFile(QString trackid, const string& identifier); - bool replaceDBFile(QString trackid, const string& identifier); -}; - -#endif diff -r 581b1b150a4d -r 92911f967a16 DefaultFeatureWriter.cpp --- 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 -#include - -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 - - /* - - - output.name - feature.timestamp - output.binName[0]:feature.value[0]... - - - - */ - - for (int i = 0; i < featureList.size(); ++i) - { - if (summaryType == "") { - cout << "" << endl; - } else { - cout << "" << endl; - } - cout << "\t" << output.name << "" << endl; - if (featureList[i].hasTimestamp) { - cout << "\t" << featureList[i].timestamp << "" << endl; - } - if (featureList[i].hasDuration) { - cout << "\t" << featureList[i].duration << "" << endl; - } - if (featureList[i].values.size() > 0) - { - cout << "\t"; - 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 << "" << endl; - } - if (featureList[i].label.length() > 0) - cout << "\t" << endl; - if (summaryType == "") { - cout << "" << endl; - } else { - cout << "" << endl; - } - } -} diff -r 581b1b150a4d -r 92911f967a16 DefaultFeatureWriter.h --- 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 diff -r 581b1b150a4d -r 92911f967a16 FeatureExtractionManager.cpp --- 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 -#include -#include -#include -#include - -#include - -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 -#include -#include - -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 &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 &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(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 &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 &writers) -{ - RDFTransformFactory factory - (QUrl::fromLocalFile(QFileInfo(transformXmlFile).absoluteFilePath()) - .toString()); - ProgressPrinter printer("Parsing transforms RDF file"); - std::vector 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 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 &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(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 &writers = ti->second; - - Transform::SummaryType summaryType = transform.getSummaryType(); - PluginSummarisingAdapter::SummaryType pType = - (PluginSummarisingAdapter::SummaryType)summaryType; - - if (transform.getSummaryType() == Transform::NoSummary) { - continue; - } - - PluginSummarisingAdapter *adapter = - dynamic_cast(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 &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 &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; -} diff -r 581b1b150a4d -r 92911f967a16 FeatureExtractionManager.h --- 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 -#include -#include - -#include -#include -#include - -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 &summaryTypes, - bool summariesOnly, - const Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries &boundaries); - - bool addFeatureExtractor(Transform transform, - const vector &writers); - - bool addFeatureExtractorFromFile(QString transformXmlFile, - const vector &writers); - - bool addDefaultFeatureExtractor(TransformId transformId, - const vector &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 > TransformWriterMap; - typedef map 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 TransformPluginMap; - TransformPluginMap m_transformPluginMap; - - // Cache the plugin output descriptors, mapping from plugin to a - // map from output ID to output descriptor. - typedef map OutputMap; - typedef map PluginOutputMap; - PluginOutputMap m_pluginOutputs; - - // Map from plugin output identifier to plugin output index - typedef map OutputIndexMap; - OutputIndexMap m_pluginOutputIndices; - - typedef set 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 diff -r 581b1b150a4d -r 92911f967a16 FeatureWriterFactory.cpp --- 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 -FeatureWriterFactory::getWriterTags() -{ - set 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; -} diff -r 581b1b150a4d -r 92911f967a16 FeatureWriterFactory.h --- 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 -#include - -using std::set; -using std::string; - -class FeatureWriter; - -class FeatureWriterFactory -{ -public: - static set getWriterTags(); - static FeatureWriter *createWriter(string tag); -}; - - -#endif diff -r 581b1b150a4d -r 92911f967a16 deploy_mac.sh --- 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 - diff -r 581b1b150a4d -r 92911f967a16 main.cpp --- 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 -#include -#include - -#include -#include -#include -#include -#include -#include - -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 - -#ifdef HAVE_FFTW3 -#include -#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 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 ." << 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 [...]