diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2009-03-26 14:10:25 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2009-03-26 14:10:25 +0000 |
commit | c2429bdcd6ed957ff1c0a4600abbe5b0988eeeb3 (patch) | |
tree | f45f36294aa40c60a5d59536f7a6f02bec4fef75 | |
parent | cb5cb2e7b03e03cbaa185b60da2f253a63d838fa (diff) |
rearrange and update VAMP tree to match VAMP 2.0
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@4898 d708f5d6-7413-0410-9779-e7cbd77b26cf
31 files changed, 4127 insertions, 234 deletions
diff --git a/libs/vamp-sdk/SConscript b/libs/vamp-sdk/SConscript index e2d602e157..907149765f 100644 --- a/libs/vamp-sdk/SConscript +++ b/libs/vamp-sdk/SConscript @@ -5,18 +5,17 @@ import os.path import glob vampsdk_files = Split (""" -vamp-sdk/PluginAdapter.cpp -vamp-sdk/RealTime.cpp +src/vamp-sdk/PluginAdapter.cpp +src/vamp-sdk/RealTime.cpp """) vamphostsdk_files = Split (""" -vamp-sdk/PluginHostAdapter.cpp -vamp-sdk/hostext/PluginBufferingAdapter.cpp -vamp-sdk/hostext/PluginChannelAdapter.cpp -vamp-sdk/hostext/PluginInputDomainAdapter.cpp -vamp-sdk/hostext/PluginLoader.cpp -vamp-sdk/hostext/PluginWrapper.cpp -vamp-sdk/RealTime.cpp +src/vamp-hostsdk/PluginHostAdapter.cpp +src/vamp-hostsdk/PluginBufferingAdapter.cpp +src/vamp-hostsdk/PluginChannelAdapter.cpp +src/vamp-hostsdk/PluginInputDomainAdapter.cpp +src/vamp-hostsdk/PluginLoader.cpp +src/vamp-hostsdk/PluginWrapper.cpp """) Import('env install_prefix libraries') @@ -43,4 +42,4 @@ env.Alias('tarball', env.Distribute (env['DISTTREE'], vamphostsdk_files + glob.glob('vamp/*.h') + glob.glob('vamp-sdk/*.h') + - glob.glob('vamp-sdk/hostext/*.h'))) + glob.glob('vamp-hostsdk/*.h'))) diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp b/libs/vamp-sdk/src/vamp-hostsdk/PluginBufferingAdapter.cpp index cbe179fb6b..0aa92d9efc 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp +++ b/libs/vamp-sdk/src/vamp-hostsdk/PluginBufferingAdapter.cpp @@ -7,7 +7,7 @@ Centre for Digital Music, Queen Mary, University of London. Copyright 2006-2007 Chris Cannam and QMUL. - This file by Mark Levy and Chris Cannam. + This file by Mark Levy and Chris Cannam, Copyright 2007-2008 QMUL. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -38,11 +38,13 @@ #include <vector> #include <map> -#include "PluginBufferingAdapter.h" +#include <vamp-hostsdk/PluginBufferingAdapter.h> using std::vector; using std::map; +_VAMP_SDK_HOSTSPACE_BEGIN(PluginBufferingAdapter.cpp) + namespace Vamp { namespace HostExt { @@ -52,11 +54,19 @@ class PluginBufferingAdapter::Impl public: Impl(Plugin *plugin, float inputSampleRate); ~Impl(); - + + void setPluginStepSize(size_t stepSize); + void setPluginBlockSize(size_t blockSize); + bool initialise(size_t channels, size_t stepSize, size_t blockSize); + void getActualStepAndBlockSizes(size_t &stepSize, size_t &blockSize); + OutputList getOutputDescriptors() const; + void setParameter(std::string, float); + void selectProgram(std::string); + void reset(); FeatureSet process(const float *const *inputBuffers, RealTime timestamp); @@ -219,19 +229,22 @@ protected: }; Plugin *m_plugin; - size_t m_inputStepSize; - size_t m_inputBlockSize; - size_t m_stepSize; - size_t m_blockSize; + size_t m_inputStepSize; // value passed to wrapper initialise() + size_t m_inputBlockSize; // value passed to wrapper initialise() + size_t m_setStepSize; // value passed to setPluginStepSize() + size_t m_setBlockSize; // value passed to setPluginBlockSize() + size_t m_stepSize; // value actually used to initialise plugin + size_t m_blockSize; // value actually used to initialise plugin size_t m_channels; vector<RingBuffer *> m_queue; float **m_buffers; float m_inputSampleRate; - RealTime m_timestamp; + long m_frame; bool m_unrun; - OutputList m_outputs; + mutable OutputList m_outputs; + mutable std::map<int, bool> m_rewriteOutputTimes; - void processBlock(FeatureSet& allFeatureSets, RealTime timestamp); + void processBlock(FeatureSet& allFeatureSets); }; PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) : @@ -244,6 +257,49 @@ PluginBufferingAdapter::~PluginBufferingAdapter() { delete m_impl; } + +size_t +PluginBufferingAdapter::getPreferredStepSize() const +{ + return getPreferredBlockSize(); +} + +size_t +PluginBufferingAdapter::getPreferredBlockSize() const +{ + return PluginWrapper::getPreferredBlockSize(); +} + +size_t +PluginBufferingAdapter::getPluginPreferredStepSize() const +{ + return PluginWrapper::getPreferredStepSize(); +} + +size_t +PluginBufferingAdapter::getPluginPreferredBlockSize() const +{ + return PluginWrapper::getPreferredBlockSize(); +} + +void +PluginBufferingAdapter::setPluginStepSize(size_t stepSize) +{ + m_impl->setPluginStepSize(stepSize); +} + +void +PluginBufferingAdapter::setPluginBlockSize(size_t blockSize) +{ + m_impl->setPluginBlockSize(blockSize); +} + +void +PluginBufferingAdapter::getActualStepAndBlockSizes(size_t &stepSize, + size_t &blockSize) +{ + m_impl->getActualStepAndBlockSizes(stepSize, blockSize); +} bool PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize) @@ -258,6 +314,18 @@ PluginBufferingAdapter::getOutputDescriptors() const } void +PluginBufferingAdapter::setParameter(std::string name, float value) +{ + m_impl->setParameter(name, value); +} + +void +PluginBufferingAdapter::selectProgram(std::string name) +{ + m_impl->selectProgram(name); +} + +void PluginBufferingAdapter::reset() { m_impl->reset(); @@ -280,16 +348,18 @@ PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : m_plugin(plugin), m_inputStepSize(0), m_inputBlockSize(0), + m_setStepSize(0), + m_setBlockSize(0), m_stepSize(0), m_blockSize(0), m_channels(0), m_queue(0), m_buffers(0), m_inputSampleRate(inputSampleRate), - m_timestamp(RealTime::zeroTime), + m_frame(0), m_unrun(true) { - m_outputs = plugin->getOutputDescriptors(); + (void)getOutputDescriptors(); // set up m_outputs and m_rewriteOutputTimes } PluginBufferingAdapter::Impl::~Impl() @@ -302,13 +372,35 @@ PluginBufferingAdapter::Impl::~Impl() } delete[] m_buffers; } - -size_t -PluginBufferingAdapter::getPreferredStepSize() const + +void +PluginBufferingAdapter::Impl::setPluginStepSize(size_t stepSize) { - return getPreferredBlockSize(); + if (m_inputStepSize != 0) { + std::cerr << "PluginBufferingAdapter::setPluginStepSize: ERROR: Cannot be called after initialise()" << std::endl; + return; + } + m_setStepSize = stepSize; } +void +PluginBufferingAdapter::Impl::setPluginBlockSize(size_t blockSize) +{ + if (m_inputBlockSize != 0) { + std::cerr << "PluginBufferingAdapter::setPluginBlockSize: ERROR: Cannot be called after initialise()" << std::endl; + return; + } + m_setBlockSize = blockSize; +} + +void +PluginBufferingAdapter::Impl::getActualStepAndBlockSizes(size_t &stepSize, + size_t &blockSize) +{ + stepSize = m_stepSize; + blockSize = m_blockSize; +} + bool PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize) { @@ -320,37 +412,64 @@ PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_ m_channels = channels; m_inputStepSize = stepSize; m_inputBlockSize = blockSize; + + // if the user has requested particular step or block sizes, use + // those; otherwise use the step and block sizes which the plugin + // prefers + + m_stepSize = 0; + m_blockSize = 0; + + if (m_setStepSize > 0) { + m_stepSize = m_setStepSize; + } + if (m_setBlockSize > 0) { + m_blockSize = m_setBlockSize; + } + + if (m_stepSize == 0 && m_blockSize == 0) { + m_stepSize = m_plugin->getPreferredStepSize(); + m_blockSize = m_plugin->getPreferredBlockSize(); + } - // use the step and block sizes which the plugin prefers - m_stepSize = m_plugin->getPreferredStepSize(); - m_blockSize = m_plugin->getPreferredBlockSize(); + bool freq = (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain); // or sensible defaults if it has no preference if (m_blockSize == 0) { - m_blockSize = 1024; - } - if (m_stepSize == 0) { - if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) { - m_stepSize = m_blockSize/2; - } else { - m_stepSize = m_blockSize; - } - } else if (m_stepSize > m_blockSize) { - if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) { + if (m_stepSize == 0) { + m_blockSize = 1024; + if (freq) { + m_stepSize = m_blockSize / 2; + } else { + m_stepSize = m_blockSize; + } + } else if (freq) { m_blockSize = m_stepSize * 2; } else { m_blockSize = m_stepSize; } + } else if (m_stepSize == 0) { // m_blockSize != 0 (that was handled above) + if (freq) { + m_stepSize = m_blockSize/2; + } else { + m_stepSize = m_blockSize; + } } - // std::cerr << "PluginBufferingAdapter::initialise: stepSize " << m_inputStepSize << " -> " << m_stepSize - // << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl; - // current implementation breaks if step is greater than block if (m_stepSize > m_blockSize) { - std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl; - return false; + size_t newBlockSize; + if (freq) { + newBlockSize = m_stepSize * 2; + } else { + newBlockSize = m_stepSize; + } + std::cerr << "PluginBufferingAdapter::initialise: WARNING: step size " << m_stepSize << " is greater than block size " << m_blockSize << ": cannot handle this in adapter; adjusting block size to " << newBlockSize << std::endl; + m_blockSize = newBlockSize; } + +// std::cerr << "PluginBufferingAdapter::initialise: NOTE: stepSize " << m_inputStepSize << " -> " << m_stepSize +// << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl; m_buffers = new float *[m_channels]; @@ -359,41 +478,108 @@ PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_ m_buffers[i] = new float[m_blockSize]; } - return m_plugin->initialise(m_channels, m_stepSize, m_blockSize); + bool success = m_plugin->initialise(m_channels, m_stepSize, m_blockSize); + +// std::cerr << "PluginBufferingAdapter::initialise: success = " << success << std::endl; + + if (success) { + // Re-query outputs; properties such as bin count may have + // changed on initialise + m_outputs.clear(); + (void)getOutputDescriptors(); + } + + return success; } PluginBufferingAdapter::OutputList PluginBufferingAdapter::Impl::getOutputDescriptors() const { - OutputList outs = m_plugin->getOutputDescriptors(); + if (m_outputs.empty()) { +// std::cerr << "PluginBufferingAdapter::getOutputDescriptors: querying anew" << std::endl; + + m_outputs = m_plugin->getOutputDescriptors(); + } + + PluginBufferingAdapter::OutputList outs = m_outputs; + for (size_t i = 0; i < outs.size(); ++i) { - if (outs[i].sampleType == OutputDescriptor::OneSamplePerStep) { - outs[i].sampleRate = 1.f / m_stepSize; + + switch (outs[i].sampleType) { + + case OutputDescriptor::OneSamplePerStep: + outs[i].sampleType = OutputDescriptor::FixedSampleRate; + outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize; + m_rewriteOutputTimes[i] = true; + break; + + case OutputDescriptor::FixedSampleRate: + if (outs[i].sampleRate == 0.f) { + outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize; + } + // We actually only need to rewrite output times for + // features that don't have timestamps already, but we + // can't tell from here whether our features will have + // timestamps or not + m_rewriteOutputTimes[i] = true; + break; + + case OutputDescriptor::VariableSampleRate: + m_rewriteOutputTimes[i] = false; + break; } - outs[i].sampleType = OutputDescriptor::VariableSampleRate; } + return outs; } void +PluginBufferingAdapter::Impl::setParameter(std::string name, float value) +{ + m_plugin->setParameter(name, value); + + // Re-query outputs; properties such as bin count may have changed + m_outputs.clear(); + (void)getOutputDescriptors(); +} + +void +PluginBufferingAdapter::Impl::selectProgram(std::string name) +{ + m_plugin->selectProgram(name); + + // Re-query outputs; properties such as bin count may have changed + m_outputs.clear(); + (void)getOutputDescriptors(); +} + +void PluginBufferingAdapter::Impl::reset() { - m_timestamp = RealTime::zeroTime; + m_frame = 0; m_unrun = true; for (size_t i = 0; i < m_queue.size(); ++i) { m_queue[i]->reset(); } + + m_plugin->reset(); } PluginBufferingAdapter::FeatureSet PluginBufferingAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp) { + if (m_inputStepSize == 0) { + std::cerr << "PluginBufferingAdapter::process: ERROR: Plugin has not been initialised" << std::endl; + return FeatureSet(); + } + FeatureSet allFeatureSets; if (m_unrun) { - m_timestamp = timestamp; + m_frame = RealTime::realTime2Frame(timestamp, + int(m_inputSampleRate + 0.5)); m_unrun = false; } @@ -414,7 +600,7 @@ PluginBufferingAdapter::Impl::process(const float *const *inputBuffers, // process as much as we can while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { - processBlock(allFeatureSets, timestamp); + processBlock(allFeatureSets); } return allFeatureSets; @@ -427,7 +613,7 @@ PluginBufferingAdapter::Impl::getRemainingFeatures() // process remaining samples in queue while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { - processBlock(allFeatureSets, m_timestamp); + processBlock(allFeatureSets); } // pad any last samples remaining and process @@ -435,7 +621,7 @@ PluginBufferingAdapter::Impl::getRemainingFeatures() for (size_t i = 0; i < m_channels; ++i) { m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace()); } - processBlock(allFeatureSets, m_timestamp); + processBlock(allFeatureSets); } // get remaining features @@ -454,44 +640,58 @@ PluginBufferingAdapter::Impl::getRemainingFeatures() } void -PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets, - RealTime timestamp) +PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets) { for (size_t i = 0; i < m_channels; ++i) { m_queue[i]->peek(m_buffers[i], m_blockSize); } - FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp); + long frame = m_frame; + RealTime timestamp = RealTime::frame2RealTime + (frame, int(m_inputSampleRate + 0.5)); + + FeatureSet featureSet = m_plugin->process(m_buffers, timestamp); - for (map<int, FeatureList>::iterator iter = featureSet.begin(); + for (FeatureSet::iterator iter = featureSet.begin(); iter != featureSet.end(); ++iter) { - - FeatureList featureList = iter->second; + int outputNo = iter->first; - - for (size_t i = 0; i < featureList.size(); ++i) { + + if (m_rewriteOutputTimes[outputNo]) { - // make sure the timestamp is set - switch (m_outputs[outputNo].sampleType) { + FeatureList featureList = iter->second; + + for (size_t i = 0; i < featureList.size(); ++i) { - case OutputDescriptor::OneSamplePerStep: - // use our internal timestamp - OK???? - featureList[i].timestamp = m_timestamp; - break; + switch (m_outputs[outputNo].sampleType) { - case OutputDescriptor::FixedSampleRate: - // use our internal timestamp - featureList[i].timestamp = m_timestamp; - break; + case OutputDescriptor::OneSamplePerStep: + // use our internal timestamp, always + featureList[i].timestamp = timestamp; + featureList[i].hasTimestamp = true; + break; - case OutputDescriptor::VariableSampleRate: - break; // plugin must set timestamp + case OutputDescriptor::FixedSampleRate: + // use our internal timestamp if feature lacks one + if (!featureList[i].hasTimestamp) { + featureList[i].timestamp = timestamp; + featureList[i].hasTimestamp = true; + } + break; - default: - break; - } + case OutputDescriptor::VariableSampleRate: + break; // plugin must set timestamp + + default: + break; + } - allFeatureSets[outputNo].push_back(featureList[i]); + allFeatureSets[outputNo].push_back(featureList[i]); + } + } else { + for (size_t i = 0; i < iter->second.size(); ++i) { + allFeatureSets[outputNo].push_back(iter->second[i]); + } } } @@ -501,16 +701,12 @@ PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets, m_queue[i]->skip(m_stepSize); } - // fake up the timestamp each time we step forward - - long frame = RealTime::realTime2Frame(m_timestamp, - int(m_inputSampleRate + 0.5)); - m_timestamp = RealTime::frame2RealTime(frame + m_stepSize, - int(m_inputSampleRate + 0.5)); + // increment internal frame counter each time we step forward + m_frame += m_stepSize; } } } - +_VAMP_SDK_HOSTSPACE_END(PluginBufferingAdapter.cpp) diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.cpp b/libs/vamp-sdk/src/vamp-hostsdk/PluginChannelAdapter.cpp index fe676bcafd..92a35bba85 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.cpp +++ b/libs/vamp-sdk/src/vamp-hostsdk/PluginChannelAdapter.cpp @@ -34,7 +34,9 @@ authorization. */ -#include "PluginChannelAdapter.h" +#include <vamp-hostsdk/PluginChannelAdapter.h> + +_VAMP_SDK_HOSTSPACE_BEGIN(PluginChannelAdapter.cpp) namespace Vamp { @@ -49,6 +51,7 @@ public: bool initialise(size_t channels, size_t stepSize, size_t blockSize); FeatureSet process(const float *const *inputBuffers, RealTime timestamp); + FeatureSet processInterleaved(const float *inputBuffers, RealTime timestamp); protected: Plugin *m_plugin; @@ -56,6 +59,7 @@ protected: size_t m_inputChannels; size_t m_pluginChannels; float **m_buffer; + float **m_deinterleave; const float **m_forwardPtrs; }; @@ -83,12 +87,20 @@ PluginChannelAdapter::process(const float *const *inputBuffers, return m_impl->process(inputBuffers, timestamp); } +PluginChannelAdapter::FeatureSet +PluginChannelAdapter::processInterleaved(const float *inputBuffers, + RealTime timestamp) +{ + return m_impl->processInterleaved(inputBuffers, timestamp); +} + PluginChannelAdapter::Impl::Impl(Plugin *plugin) : m_plugin(plugin), m_blockSize(0), m_inputChannels(0), m_pluginChannels(0), m_buffer(0), + m_deinterleave(0), m_forwardPtrs(0) { } @@ -109,6 +121,14 @@ PluginChannelAdapter::Impl::~Impl() m_buffer = 0; } + if (m_deinterleave) { + for (size_t i = 0; i < m_inputChannels; ++i) { + delete[] m_deinterleave[i]; + } + delete[] m_deinterleave; + m_deinterleave = 0; + } + if (m_forwardPtrs) { delete[] m_forwardPtrs; m_forwardPtrs = 0; @@ -143,7 +163,7 @@ PluginChannelAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t m_pluginChannels = minch; - // std::cerr << "PluginChannelAdapter::initialise: expanding " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl; +// std::cerr << "PluginChannelAdapter::initialise: expanding " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl; } else if (m_inputChannels > maxch) { @@ -155,18 +175,18 @@ PluginChannelAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t m_buffer = new float *[1]; m_buffer[0] = new float[blockSize]; - // std::cerr << "PluginChannelAdapter::initialise: mixing " << m_inputChannels << " to mono for plugin" << std::endl; +// std::cerr << "PluginChannelAdapter::initialise: mixing " << m_inputChannels << " to mono for plugin" << std::endl; } else { - // std::cerr << "PluginChannelAdapter::initialise: reducing " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl; +// std::cerr << "PluginChannelAdapter::initialise: reducing " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl; } m_pluginChannels = maxch; } else { - // std::cerr << "PluginChannelAdapter::initialise: accepting given number of channels (" << m_inputChannels << ")" << std::endl; +// std::cerr << "PluginChannelAdapter::initialise: accepting given number of channels (" << m_inputChannels << ")" << std::endl; m_pluginChannels = m_inputChannels; } @@ -174,6 +194,26 @@ PluginChannelAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t } PluginChannelAdapter::FeatureSet +PluginChannelAdapter::Impl::processInterleaved(const float *inputBuffers, + RealTime timestamp) +{ + if (!m_deinterleave) { + m_deinterleave = new float *[m_inputChannels]; + for (size_t i = 0; i < m_inputChannels; ++i) { + m_deinterleave[i] = new float[m_blockSize]; + } + } + + for (size_t i = 0; i < m_inputChannels; ++i) { + for (size_t j = 0; j < m_blockSize; ++j) { + m_deinterleave[i][j] = inputBuffers[j * m_inputChannels + i]; + } + } + + return process(m_deinterleave, timestamp); +} + +PluginChannelAdapter::FeatureSet PluginChannelAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp) { @@ -225,4 +265,6 @@ PluginChannelAdapter::Impl::process(const float *const *inputBuffers, } +_VAMP_SDK_HOSTSPACE_END(PluginChannelAdapter.cpp) + diff --git a/libs/vamp-sdk/src/vamp-hostsdk/PluginHostAdapter.cpp b/libs/vamp-sdk/src/vamp-hostsdk/PluginHostAdapter.cpp new file mode 100644 index 0000000000..5bf323b1a7 --- /dev/null +++ b/libs/vamp-sdk/src/vamp-hostsdk/PluginHostAdapter.cpp @@ -0,0 +1,456 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#include <vamp-hostsdk/PluginHostAdapter.h> +#include <cstdlib> + +#if ( VAMP_SDK_MAJOR_VERSION != 2 || VAMP_SDK_MINOR_VERSION != 0 ) +#error Incorrect Vamp SDK header included (not the expected 2.0 SDK) +#endif + +_VAMP_SDK_HOSTSPACE_BEGIN(PluginHostAdapter.cpp) + +namespace Vamp +{ + +PluginHostAdapter::PluginHostAdapter(const VampPluginDescriptor *descriptor, + float inputSampleRate) : + Plugin(inputSampleRate), + m_descriptor(descriptor) +{ +// std::cerr << "PluginHostAdapter::PluginHostAdapter (plugin = " << descriptor->name << ")" << std::endl; + m_handle = m_descriptor->instantiate(m_descriptor, inputSampleRate); + if (!m_handle) { +// std::cerr << "WARNING: PluginHostAdapter: Plugin instantiation failed for plugin " << m_descriptor->name << std::endl; + } +} + +PluginHostAdapter::~PluginHostAdapter() +{ +// std::cerr << "PluginHostAdapter::~PluginHostAdapter (plugin = " << m_descriptor->name << ")" << std::endl; + if (m_handle) m_descriptor->cleanup(m_handle); +} + +std::vector<std::string> +PluginHostAdapter::getPluginPath() +{ + std::vector<std::string> path; + std::string envPath; + + char *cpath = getenv("VAMP_PATH"); + if (cpath) envPath = cpath; + +#ifdef _WIN32 +#define PATH_SEPARATOR ';' +#define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins" +#else +#define PATH_SEPARATOR ':' +#ifdef __APPLE__ +#define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp" +#else +#define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp" +#endif +#endif + + if (envPath == "") { + envPath = DEFAULT_VAMP_PATH; + char *chome = getenv("HOME"); + if (chome) { + std::string home(chome); + std::string::size_type f; + while ((f = envPath.find("$HOME")) != std::string::npos && + f < envPath.length()) { + envPath.replace(f, 5, home); + } + } +#ifdef _WIN32 + char *cpfiles = getenv("ProgramFiles"); + if (!cpfiles) cpfiles = (char *)"C:\\Program Files"; + std::string pfiles(cpfiles); + std::string::size_type f; + while ((f = envPath.find("%ProgramFiles%")) != std::string::npos && + f < envPath.length()) { + envPath.replace(f, 14, pfiles); + } +#endif + } + + std::string::size_type index = 0, newindex = 0; + + while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) { + path.push_back(envPath.substr(index, newindex - index)); + index = newindex + 1; + } + + path.push_back(envPath.substr(index)); + + return path; +} + +bool +PluginHostAdapter::initialise(size_t channels, + size_t stepSize, + size_t blockSize) +{ + if (!m_handle) return false; + return m_descriptor->initialise(m_handle, channels, stepSize, blockSize) ? + true : false; +} + +void +PluginHostAdapter::reset() +{ + if (!m_handle) { +// std::cerr << "PluginHostAdapter::reset: no handle" << std::endl; + return; + } +// std::cerr << "PluginHostAdapter::reset(" << m_handle << ")" << std::endl; + m_descriptor->reset(m_handle); +} + +PluginHostAdapter::InputDomain +PluginHostAdapter::getInputDomain() const +{ + if (m_descriptor->inputDomain == vampFrequencyDomain) { + return FrequencyDomain; + } else { + return TimeDomain; + } +} + +unsigned int +PluginHostAdapter::getVampApiVersion() const +{ + return m_descriptor->vampApiVersion; +} + +std::string +PluginHostAdapter::getIdentifier() const +{ + return m_descriptor->identifier; +} + +std::string +PluginHostAdapter::getName() const +{ + return m_descriptor->name; +} + +std::string +PluginHostAdapter::getDescription() const +{ + return m_descriptor->description; +} + +std::string +PluginHostAdapter::getMaker() const +{ + return m_descriptor->maker; +} + +int +PluginHostAdapter::getPluginVersion() const +{ + return m_descriptor->pluginVersion; +} + +std::string +PluginHostAdapter::getCopyright() const +{ + return m_descriptor->copyright; +} + +PluginHostAdapter::ParameterList +PluginHostAdapter::getParameterDescriptors() const +{ + ParameterList list; + for (unsigned int i = 0; i < m_descriptor->parameterCount; ++i) { + const VampParameterDescriptor *spd = m_descriptor->parameters[i]; + ParameterDescriptor pd; + pd.identifier = spd->identifier; + pd.name = spd->name; + pd.description = spd->description; + pd.unit = spd->unit; + pd.minValue = spd->minValue; + pd.maxValue = spd->maxValue; + pd.defaultValue = spd->defaultValue; + pd.isQuantized = spd->isQuantized; + pd.quantizeStep = spd->quantizeStep; + if (pd.isQuantized && spd->valueNames) { + for (unsigned int j = 0; spd->valueNames[j]; ++j) { + pd.valueNames.push_back(spd->valueNames[j]); + } + } + list.push_back(pd); + } + return list; +} + +float +PluginHostAdapter::getParameter(std::string param) const +{ + if (!m_handle) return 0.0; + + for (unsigned int i = 0; i < m_descriptor->parameterCount; ++i) { + if (param == m_descriptor->parameters[i]->identifier) { + return m_descriptor->getParameter(m_handle, i); + } + } + + return 0.0; +} + +void +PluginHostAdapter::setParameter(std::string param, + float value) +{ + if (!m_handle) return; + + for (unsigned int i = 0; i < m_descriptor->parameterCount; ++i) { + if (param == m_descriptor->parameters[i]->identifier) { + m_descriptor->setParameter(m_handle, i, value); + return; + } + } +} + +PluginHostAdapter::ProgramList +PluginHostAdapter::getPrograms() const +{ + ProgramList list; + + for (unsigned int i = 0; i < m_descriptor->programCount; ++i) { + list.push_back(m_descriptor->programs[i]); + } + + return list; +} + +std::string +PluginHostAdapter::getCurrentProgram() const +{ + if (!m_handle) return ""; + + int pn = m_descriptor->getCurrentProgram(m_handle); + return m_descriptor->programs[pn]; +} + +void +PluginHostAdapter::selectProgram(std::string program) +{ + if (!m_handle) return; + + for (unsigned int i = 0; i < m_descriptor->programCount; ++i) { + if (program == m_descriptor->programs[i]) { + m_descriptor->selectProgram(m_handle, i); + return; + } + } +} + +size_t +PluginHostAdapter::getPreferredStepSize() const +{ + if (!m_handle) return 0; + return m_descriptor->getPreferredStepSize(m_handle); +} + +size_t +PluginHostAdapter::getPreferredBlockSize() const +{ + if (!m_handle) return 0; + return m_descriptor->getPreferredBlockSize(m_handle); +} + +size_t +PluginHostAdapter::getMinChannelCount() const +{ + if (!m_handle) return 0; + return m_descriptor->getMinChannelCount(m_handle); +} + +size_t +PluginHostAdapter::getMaxChannelCount() const +{ + if (!m_handle) return 0; + return m_descriptor->getMaxChannelCount(m_handle); +} + +PluginHostAdapter::OutputList +PluginHostAdapter::getOutputDescriptors() const +{ + OutputList list; + if (!m_handle) { +// std::cerr << "PluginHostAdapter::getOutputDescriptors: no handle " << std::endl; + return list; + } + + unsigned int count = m_descriptor->getOutputCount(m_handle); + + for (unsigned int i = 0; i < count; ++i) { + VampOutputDescriptor *sd = m_descriptor->getOutputDescriptor(m_handle, i); + OutputDescriptor d; + d.identifier = sd->identifier; + d.name = sd->name; + d.description = sd->description; + d.unit = sd->unit; + d.hasFixedBinCount = sd->hasFixedBinCount; + d.binCount = sd->binCount; + if (d.hasFixedBinCount && sd->binNames) { + for (unsigned int j = 0; j < sd->binCount; ++j) { + d.binNames.push_back(sd->binNames[j] ? sd->binNames[j] : ""); + } + } + d.hasKnownExtents = sd->hasKnownExtents; + d.minValue = sd->minValue; + d.maxValue = sd->maxValue; + d.isQuantized = sd->isQuantized; + d.quantizeStep = sd->quantizeStep; + + switch (sd->sampleType) { + case vampOneSamplePerStep: + d.sampleType = OutputDescriptor::OneSamplePerStep; break; + case vampFixedSampleRate: + d.sampleType = OutputDescriptor::FixedSampleRate; break; + case vampVariableSampleRate: + d.sampleType = OutputDescriptor::VariableSampleRate; break; + } + + d.sampleRate = sd->sampleRate; + + if (m_descriptor->vampApiVersion >= 2) { + d.hasDuration = sd->hasDuration; + } else { + d.hasDuration = false; + } + + list.push_back(d); + + m_descriptor->releaseOutputDescriptor(sd); + } + + return list; +} + +PluginHostAdapter::FeatureSet +PluginHostAdapter::process(const float *const *inputBuffers, + RealTime timestamp) +{ + FeatureSet fs; + if (!m_handle) return fs; + + int sec = timestamp.sec; + int nsec = timestamp.nsec; + + VampFeatureList *features = m_descriptor->process(m_handle, + inputBuffers, + sec, nsec); + + convertFeatures(features, fs); + m_descriptor->releaseFeatureSet(features); + return fs; +} + +PluginHostAdapter::FeatureSet +PluginHostAdapter::getRemainingFeatures() +{ + FeatureSet fs; + if (!m_handle) return fs; + + VampFeatureList *features = m_descriptor->getRemainingFeatures(m_handle); + + convertFeatures(features, fs); + m_descriptor->releaseFeatureSet(features); + return fs; +} + +void +PluginHostAdapter::convertFeatures(VampFeatureList *features, + FeatureSet &fs) +{ + if (!features) return; + + unsigned int outputs = m_descriptor->getOutputCount(m_handle); + + for (unsigned int i = 0; i < outputs; ++i) { + + VampFeatureList &list = features[i]; + + if (list.featureCount > 0) { + + Feature feature; + feature.values.reserve(list.features[0].v1.valueCount); + + for (unsigned int j = 0; j < list.featureCount; ++j) { + + feature.hasTimestamp = list.features[j].v1.hasTimestamp; + feature.timestamp = RealTime(list.features[j].v1.sec, + list.features[j].v1.nsec); + feature.hasDuration = false; + + if (m_descriptor->vampApiVersion >= 2) { + unsigned int j2 = j + list.featureCount; + feature.hasDuration = list.features[j2].v2.hasDuration; + feature.duration = RealTime(list.features[j2].v2.durationSec, + list.features[j2].v2.durationNsec); + } + + for (unsigned int k = 0; k < list.features[j].v1.valueCount; ++k) { + feature.values.push_back(list.features[j].v1.values[k]); + } + + if (list.features[j].v1.label) { + feature.label = list.features[j].v1.label; + } + + fs[i].push_back(feature); + + if (list.features[j].v1.valueCount > 0) { + feature.values.clear(); + } + + if (list.features[j].v1.label) { + feature.label = ""; + } + } + } + } +} + +} + +_VAMP_SDK_HOSTSPACE_END(PluginHostAdapter.cpp) + diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp b/libs/vamp-sdk/src/vamp-hostsdk/PluginInputDomainAdapter.cpp index 273925f96d..967d5cfcd9 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp +++ b/libs/vamp-sdk/src/vamp-hostsdk/PluginInputDomainAdapter.cpp @@ -37,10 +37,11 @@ authorization. */ -#include "PluginInputDomainAdapter.h" +#include <vamp-hostsdk/PluginInputDomainAdapter.h> #include <cmath> + /** * If you want to compile using FFTW instead of the built-in FFT * implementation for the PluginInputDomainAdapter, define HAVE_FFTW3 @@ -68,6 +69,9 @@ #include <fftw3.h> #endif + +_VAMP_SDK_HOSTSPACE_BEGIN(PluginInputDomainAdapter.cpp) + namespace Vamp { namespace HostExt { @@ -84,6 +88,8 @@ public: size_t getPreferredBlockSize() const; FeatureSet process(const float *const *inputBuffers, RealTime timestamp); + + RealTime getTimestampAdjustment() const; protected: Plugin *m_plugin; @@ -149,6 +155,13 @@ PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime tim return m_impl->process(inputBuffers, timestamp); } +RealTime +PluginInputDomainAdapter::getTimestampAdjustment() const +{ + return m_impl->getTimestampAdjustment(); +} + + PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : m_plugin(plugin), m_inputSampleRate(inputSampleRate), @@ -336,6 +349,17 @@ PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const return blockSize; } +RealTime +PluginInputDomainAdapter::Impl::getTimestampAdjustment() const +{ + if (m_plugin->getInputDomain() == TimeDomain) { + return RealTime::zeroTime; + } else { + return RealTime::frame2RealTime + (m_blockSize/2, int(m_inputSampleRate + 0.5)); + } +} + Plugin::FeatureSet PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp) @@ -388,8 +412,7 @@ PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers, // std::cerr << "PluginInputDomainAdapter: sampleRate " << m_inputSampleRate << ", blocksize " << m_blockSize << ", adjusting time from " << timestamp; - timestamp = timestamp + RealTime::frame2RealTime - (m_blockSize/2, int(m_inputSampleRate + 0.5)); + timestamp = timestamp + getTimestampAdjustment(); // std::cerr << " to " << timestamp << std::endl; @@ -555,3 +578,5 @@ PluginInputDomainAdapter::Impl::fft(unsigned int n, bool inverse, } +_VAMP_SDK_HOSTSPACE_END(PluginInputDomainAdapter.cpp) + diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp b/libs/vamp-sdk/src/vamp-hostsdk/PluginLoader.cpp index e9d75ee181..3bf23bb165 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.cpp +++ b/libs/vamp-sdk/src/vamp-hostsdk/PluginLoader.cpp @@ -34,16 +34,15 @@ authorization. */ -#include "vamp-sdk/PluginHostAdapter.h" -#include "PluginLoader.h" -#include "PluginInputDomainAdapter.h" -#include "PluginChannelAdapter.h" -#include "PluginBufferingAdapter.h" +#include <vamp-hostsdk/PluginHostAdapter.h> +#include <vamp-hostsdk/PluginLoader.h> +#include <vamp-hostsdk/PluginInputDomainAdapter.h> +#include <vamp-hostsdk/PluginChannelAdapter.h> +#include <vamp-hostsdk/PluginBufferingAdapter.h> -#include <string> -#include <cstring> #include <fstream> #include <cctype> // tolower + #include <cstring> #ifdef _WIN32 @@ -67,6 +66,8 @@ using namespace std; +_VAMP_SDK_HOSTSPACE_BEGIN(PluginLoader.cpp) + namespace Vamp { namespace HostExt { @@ -518,7 +519,7 @@ PluginLoader::Impl::loadLibrary(string path) << path << "\"" << endl; } #else - handle = dlopen(path.c_str(), RTLD_LAZY); + handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) { cerr << "Vamp::HostExt::PluginLoader: Unable to load library \"" << path << "\": " << dlerror() << endl; @@ -586,8 +587,6 @@ PluginLoader::Impl::listFiles(string dir, string extension) struct dirent *e = 0; while ((e = readdir(d))) { - if (!(e->d_type & DT_REG) && (e->d_type != DT_UNKNOWN)) continue; - if (!e->d_name) continue; size_t len = strlen(e->d_name); @@ -637,3 +636,6 @@ PluginLoader::Impl::PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter() } } + +_VAMP_SDK_HOSTSPACE_END(PluginLoader.cpp) + diff --git a/libs/vamp-sdk/src/vamp-hostsdk/PluginSummarisingAdapter.cpp b/libs/vamp-sdk/src/vamp-hostsdk/PluginSummarisingAdapter.cpp new file mode 100644 index 0000000000..592190fdf4 --- /dev/null +++ b/libs/vamp-sdk/src/vamp-hostsdk/PluginSummarisingAdapter.cpp @@ -0,0 +1,952 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006-2008 Chris Cannam and QMUL. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#include <vamp-hostsdk/PluginSummarisingAdapter.h> + +#include <map> +#include <algorithm> +#include <cmath> +#include <climits> + +//#define DEBUG_PLUGIN_SUMMARISING_ADAPTER 1 +//#define DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT 1 + +_VAMP_SDK_HOSTSPACE_BEGIN(PluginSummarisingAdapter.cpp) + +namespace Vamp { + +namespace HostExt { + +class PluginSummarisingAdapter::Impl +{ +public: + Impl(Plugin *plugin, float inputSampleRate); + ~Impl(); + + bool initialise(size_t channels, size_t stepSize, size_t blockSize); + + void reset(); + + FeatureSet process(const float *const *inputBuffers, RealTime timestamp); + FeatureSet getRemainingFeatures(); + + void setSummarySegmentBoundaries(const SegmentBoundaries &); + + FeatureList getSummaryForOutput(int output, + SummaryType type, + AveragingMethod avg); + + FeatureSet getSummaryForAllOutputs(SummaryType type, + AveragingMethod avg); + +protected: + Plugin *m_plugin; + float m_inputSampleRate; + size_t m_stepSize; + size_t m_blockSize; + + SegmentBoundaries m_boundaries; + + typedef std::vector<float> ValueList; + + struct Result { // smaller than Feature + RealTime time; + RealTime duration; + ValueList values; // bin number -> value + }; + + typedef std::vector<Result> ResultList; + + struct OutputAccumulator { + int bins; + ResultList results; + OutputAccumulator() : bins(0) { } + }; + + typedef std::map<int, OutputAccumulator> OutputAccumulatorMap; + OutputAccumulatorMap m_accumulators; // output number -> accumulator + + typedef std::map<RealTime, OutputAccumulator> SegmentAccumulatorMap; + typedef std::map<int, SegmentAccumulatorMap> OutputSegmentAccumulatorMap; + OutputSegmentAccumulatorMap m_segmentedAccumulators; // output -> segmented + + typedef std::map<int, RealTime> OutputTimestampMap; + OutputTimestampMap m_prevTimestamps; // output number -> timestamp + OutputTimestampMap m_prevDurations; // output number -> durations + + struct OutputBinSummary { + + int count; + + // extents + double minimum; + double maximum; + double sum; + + // sample-average results + double median; + double mode; + double variance; + + // continuous-time average results + double median_c; + double mode_c; + double mean_c; + double variance_c; + }; + + typedef std::map<int, OutputBinSummary> OutputSummary; + typedef std::map<RealTime, OutputSummary> SummarySegmentMap; + typedef std::map<int, SummarySegmentMap> OutputSummarySegmentMap; + + OutputSummarySegmentMap m_summaries; + + bool m_reduced; + RealTime m_endTime; + + void accumulate(const FeatureSet &fs, RealTime, bool final); + void accumulate(int output, const Feature &f, RealTime, bool final); + void accumulateFinalDurations(); + void findSegmentBounds(RealTime t, RealTime &start, RealTime &end); + void segment(); + void reduce(); + + std::string getSummaryLabel(SummaryType type, AveragingMethod avg); +}; + +static RealTime INVALID_DURATION(INT_MIN, INT_MIN); + +PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) : + PluginWrapper(plugin) +{ + m_impl = new Impl(plugin, m_inputSampleRate); +} + +PluginSummarisingAdapter::~PluginSummarisingAdapter() +{ + delete m_impl; +} + +bool +PluginSummarisingAdapter::initialise(size_t channels, + size_t stepSize, size_t blockSize) +{ + return + PluginWrapper::initialise(channels, stepSize, blockSize) && + m_impl->initialise(channels, stepSize, blockSize); +} + +void +PluginSummarisingAdapter::reset() +{ + m_impl->reset(); +} + +Plugin::FeatureSet +PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp) +{ + return m_impl->process(inputBuffers, timestamp); +} + +Plugin::FeatureSet +PluginSummarisingAdapter::getRemainingFeatures() +{ + return m_impl->getRemainingFeatures(); +} + +void +PluginSummarisingAdapter::setSummarySegmentBoundaries(const SegmentBoundaries &b) +{ + m_impl->setSummarySegmentBoundaries(b); +} + +Plugin::FeatureList +PluginSummarisingAdapter::getSummaryForOutput(int output, + SummaryType type, + AveragingMethod avg) +{ + return m_impl->getSummaryForOutput(output, type, avg); +} + +Plugin::FeatureSet +PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type, + AveragingMethod avg) +{ + return m_impl->getSummaryForAllOutputs(type, avg); +} + +PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : + m_plugin(plugin), + m_inputSampleRate(inputSampleRate), + m_reduced(false) +{ +} + +PluginSummarisingAdapter::Impl::~Impl() +{ +} + +bool +PluginSummarisingAdapter::Impl::initialise(size_t channels, + size_t stepSize, size_t blockSize) +{ + m_stepSize = stepSize; + m_blockSize = blockSize; + return true; +} + +void +PluginSummarisingAdapter::Impl::reset() +{ + m_accumulators.clear(); + m_segmentedAccumulators.clear(); + m_prevTimestamps.clear(); + m_prevDurations.clear(); + m_summaries.clear(); + m_reduced = false; + m_endTime = RealTime(); + m_plugin->reset(); +} + +Plugin::FeatureSet +PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers, + RealTime timestamp) +{ + if (m_reduced) { + std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl; + } + FeatureSet fs = m_plugin->process(inputBuffers, timestamp); + accumulate(fs, timestamp, false); + m_endTime = timestamp + + RealTime::frame2RealTime(m_stepSize, int(m_inputSampleRate + 0.5)); + return fs; +} + +Plugin::FeatureSet +PluginSummarisingAdapter::Impl::getRemainingFeatures() +{ + if (m_reduced) { + std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl; + } + FeatureSet fs = m_plugin->getRemainingFeatures(); + accumulate(fs, m_endTime, true); + return fs; +} + +void +PluginSummarisingAdapter::Impl::setSummarySegmentBoundaries(const SegmentBoundaries &b) +{ + m_boundaries = b; +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "PluginSummarisingAdapter::setSummarySegmentBoundaries: boundaries are:" << std::endl; + for (SegmentBoundaries::const_iterator i = m_boundaries.begin(); + i != m_boundaries.end(); ++i) { + std::cerr << *i << " "; + } + std::cerr << std::endl; +#endif +} + +Plugin::FeatureList +PluginSummarisingAdapter::Impl::getSummaryForOutput(int output, + SummaryType type, + AveragingMethod avg) +{ + if (!m_reduced) { + accumulateFinalDurations(); + segment(); + reduce(); + m_reduced = true; + } + + bool continuous = (avg == ContinuousTimeAverage); + + FeatureList fl; + for (SummarySegmentMap::const_iterator i = m_summaries[output].begin(); + i != m_summaries[output].end(); ++i) { + + Feature f; + + f.hasTimestamp = true; + f.timestamp = i->first; + + f.hasDuration = true; + SummarySegmentMap::const_iterator ii = i; + if (++ii == m_summaries[output].end()) { + f.duration = m_endTime - f.timestamp; + } else { + f.duration = ii->first - f.timestamp; + } + + f.label = getSummaryLabel(type, avg); + + for (OutputSummary::const_iterator j = i->second.begin(); + j != i->second.end(); ++j) { + + // these will be ordered by bin number, and no bin numbers + // will be missing except at the end (because of the way + // the accumulators were initially filled in accumulate()) + + const OutputBinSummary &summary = j->second; + double result = 0.f; + + switch (type) { + + case Minimum: + result = summary.minimum; + break; + + case Maximum: + result = summary.maximum; + break; + + case Mean: + if (continuous) { + result = summary.mean_c; + } else if (summary.count) { + result = summary.sum / summary.count; + } + break; + + case Median: + if (continuous) result = summary.median_c; + else result = summary.median; + break; + + case Mode: + if (continuous) result = summary.mode_c; + else result = summary.mode; + break; + + case Sum: + result = summary.sum; + break; + + case Variance: + if (continuous) result = summary.variance_c; + else result = summary.variance; + break; + + case StandardDeviation: + if (continuous) result = sqrtf(summary.variance_c); + else result = sqrtf(summary.variance); + break; + + case Count: + result = summary.count; + break; + + case UnknownSummaryType: + break; + + default: + break; + } + + f.values.push_back(result); + } + + fl.push_back(f); + } + return fl; +} + +Plugin::FeatureSet +PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type, + AveragingMethod avg) +{ + if (!m_reduced) { + accumulateFinalDurations(); + segment(); + reduce(); + m_reduced = true; + } + + FeatureSet fs; + for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin(); + i != m_summaries.end(); ++i) { + fs[i->first] = getSummaryForOutput(i->first, type, avg); + } + return fs; +} + +void +PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs, + RealTime timestamp, + bool final) +{ + for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) { + for (FeatureList::const_iterator j = i->second.begin(); + j != i->second.end(); ++j) { + if (j->hasTimestamp) { + accumulate(i->first, *j, j->timestamp, final); + } else { + //!!! is this correct? + accumulate(i->first, *j, timestamp, final); + } + } + } +} + +std::string +PluginSummarisingAdapter::Impl::getSummaryLabel(SummaryType type, + AveragingMethod avg) +{ + std::string label; + std::string avglabel; + + if (avg == SampleAverage) avglabel = ", sample average"; + else avglabel = ", continuous-time average"; + + switch (type) { + case Minimum: label = "(minimum value)"; break; + case Maximum: label = "(maximum value)"; break; + case Mean: label = "(mean value" + avglabel + ")"; break; + case Median: label = "(median value" + avglabel + ")"; break; + case Mode: label = "(modal value" + avglabel + ")"; break; + case Sum: label = "(sum)"; break; + case Variance: label = "(variance" + avglabel + ")"; break; + case StandardDeviation: label = "(standard deviation" + avglabel + ")"; break; + case Count: label = "(count)"; break; + case UnknownSummaryType: label = "(unknown summary)"; break; + } + + return label; +} + +void +PluginSummarisingAdapter::Impl::accumulate(int output, + const Feature &f, + RealTime timestamp, + bool final) +{ + // What should happen if a feature's duration spans a segment + // boundary? I think we probably want to chop it, and pretend + // that it appears in both. A very long feature (e.g. key, if the + // whole audio is in a single key) might span many or all + // segments, and we want that to be reflected in the results + // (e.g. it is the modal key in all of those segments, not just + // the first). This is actually quite complicated to do. + + // If features spanning a boundary should be chopped, then we need + // to have per-segment accumulators (and the feature value goes + // into both -- with a separate phase to split the accumulator up + // into segments). + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << std::endl; +#endif + + // At each process step, accumulate() is called once for each + // feature on each output within that process's returned feature + // list, and with the timestamp passed in being that of the start + // of the process block. + + // At the end (in getRemainingFeatures), accumulate() is called + // once for each feature on each output within the feature list + // returned by getRemainingFeatures, and with the timestamp being + // the same as the last process block and final set to true. + + // (What if getRemainingFeatures doesn't return any features? We + // still need to ensure that the final duration is written. Need + // a separate function to close the durations.) + + // At each call, we pull out the value for the feature and stuff + // it into the accumulator's appropriate values array; and we + // calculate the duration for the _previous_ feature, or pull it + // from the prevDurations array if the previous feature had a + // duration in its structure, and stuff that into the + // accumulator's appropriate durations array. + + if (m_prevDurations.find(output) != m_prevDurations.end()) { + + // Not the first time accumulate has been called for this + // output -- there has been a previous feature + + RealTime prevDuration; + + // Note that m_prevDurations[output] only contains the + // duration field that was contained in the previous feature. + // If it didn't have an explicit duration, + // m_prevDurations[output] should be INVALID_DURATION and we + // will have to calculate the duration from the previous and + // current timestamps. + + if (m_prevDurations[output] != INVALID_DURATION) { + prevDuration = m_prevDurations[output]; +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "Previous duration from previous feature: " << prevDuration << std::endl; +#endif + } else { + prevDuration = timestamp - m_prevTimestamps[output]; +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "Previous duration from diff: " << timestamp << " - " + << m_prevTimestamps[output] << std::endl; +#endif + } + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "output " << output << ": "; + std::cerr << "Pushing previous duration as " << prevDuration << std::endl; +#endif + + m_accumulators[output].results + [m_accumulators[output].results.size() - 1] + .duration = prevDuration; + } + + if (f.hasDuration) m_prevDurations[output] = f.duration; + else m_prevDurations[output] = INVALID_DURATION; + + m_prevTimestamps[output] = timestamp; + + if (f.hasDuration) { + RealTime et = timestamp; + et = et + f.duration; + if (et > m_endTime) m_endTime = et; + } + + Result result; + result.time = timestamp; + result.duration = INVALID_DURATION; + + if (int(f.values.size()) > m_accumulators[output].bins) { + m_accumulators[output].bins = f.values.size(); + } + + for (int i = 0; i < int(f.values.size()); ++i) { + result.values.push_back(f.values[i]); + } + + m_accumulators[output].results.push_back(result); +} + +void +PluginSummarisingAdapter::Impl::accumulateFinalDurations() +{ + for (OutputTimestampMap::iterator i = m_prevTimestamps.begin(); + i != m_prevTimestamps.end(); ++i) { + + int output = i->first; + + int acount = m_accumulators[output].results.size(); + + if (acount == 0) continue; + + RealTime prevTimestamp = i->second; + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "output " << output << ": "; +#endif + + if (m_prevDurations.find(output) != m_prevDurations.end() && + m_prevDurations[output] != INVALID_DURATION) { + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl; +#endif + + m_accumulators[output].results[acount - 1].duration = + m_prevDurations[output]; + + } else { + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "Pushing final duration from diff as " << m_endTime << " - " << m_prevTimestamps[output] << std::endl; +#endif + + m_accumulators[output].results[acount - 1].duration = + m_endTime - m_prevTimestamps[output]; + } + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "so duration for result no " << acount-1 << " is " + << m_accumulators[output].results[acount-1].duration + << std::endl; +#endif + } +} + +void +PluginSummarisingAdapter::Impl::findSegmentBounds(RealTime t, + RealTime &start, + RealTime &end) +{ +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT + std::cerr << "findSegmentBounds: t = " << t << std::endl; +#endif + + SegmentBoundaries::const_iterator i = std::upper_bound + (m_boundaries.begin(), m_boundaries.end(), t); + + start = RealTime::zeroTime; + end = m_endTime; + + if (i != m_boundaries.end()) { + end = *i; + } + + if (i != m_boundaries.begin()) { + start = *--i; + } + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT + std::cerr << "findSegmentBounds: " << t << " is in segment " << start << " -> " << end << std::endl; +#endif +} + +void +PluginSummarisingAdapter::Impl::segment() +{ + SegmentBoundaries::iterator boundaryitr = m_boundaries.begin(); + RealTime segmentStart = RealTime::zeroTime; + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT + std::cerr << "segment: starting" << std::endl; +#endif + + for (OutputAccumulatorMap::iterator i = m_accumulators.begin(); + i != m_accumulators.end(); ++i) { + + int output = i->first; + OutputAccumulator &source = i->second; + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT + std::cerr << "segment: total results for output " << output << " = " + << source.results.size() << std::endl; +#endif + + // This is basically nonsense if the results have no values + // (i.e. their times and counts are the only things of + // interest)... but perhaps it's the user's problem if they + // ask for segmentation (or any summary at all) in that case + + for (int n = 0; n < int(source.results.size()); ++n) { + + // This result spans source.results[n].time to + // source.results[n].time + source.results[n].duration. + // We need to dispose it into segments appropriately + + RealTime resultStart = source.results[n].time; + RealTime resultEnd = resultStart + source.results[n].duration; + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT + std::cerr << "output: " << output << ", result start = " << resultStart << ", end = " << resultEnd << std::endl; +#endif + + RealTime segmentStart = RealTime::zeroTime; + RealTime segmentEnd = resultEnd - RealTime(1, 0); + + RealTime prevSegmentStart = segmentStart - RealTime(1, 0); + + while (segmentEnd < resultEnd) { + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT + std::cerr << "segment end " << segmentEnd << " < result end " + << resultEnd << " (with result start " << resultStart << ")" << std::endl; +#endif + + findSegmentBounds(resultStart, segmentStart, segmentEnd); + + if (segmentStart == prevSegmentStart) { + // This can happen when we reach the end of the + // input, if a feature's end time overruns the + // input audio end time + break; + } + prevSegmentStart = segmentStart; + + RealTime chunkStart = resultStart; + if (chunkStart < segmentStart) chunkStart = segmentStart; + + RealTime chunkEnd = resultEnd; + if (chunkEnd > segmentEnd) chunkEnd = segmentEnd; + + m_segmentedAccumulators[output][segmentStart].bins = source.bins; + + Result chunk; + chunk.time = chunkStart; + chunk.duration = chunkEnd - chunkStart; + chunk.values = source.results[n].values; + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT + std::cerr << "chunk for segment " << segmentStart << ": from " << chunk.time << ", duration " << chunk.duration << std::endl; +#endif + + m_segmentedAccumulators[output][segmentStart].results + .push_back(chunk); + + resultStart = chunkEnd; + } + } + } +} + +struct ValueDurationFloatPair +{ + float value; + float duration; + + ValueDurationFloatPair() : value(0), duration(0) { } + ValueDurationFloatPair(float v, float d) : value(v), duration(d) { } + ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) { + value = p.value; + duration = p.duration; + return *this; + } + bool operator<(const ValueDurationFloatPair &p) const { + return value < p.value; + } +}; + +static double toSec(const RealTime &r) +{ + return r.sec + double(r.nsec) / 1000000000.0; +} + +void +PluginSummarisingAdapter::Impl::reduce() +{ + for (OutputSegmentAccumulatorMap::iterator i = + m_segmentedAccumulators.begin(); + i != m_segmentedAccumulators.end(); ++i) { + + int output = i->first; + SegmentAccumulatorMap &segments = i->second; + + for (SegmentAccumulatorMap::iterator j = segments.begin(); + j != segments.end(); ++j) { + + RealTime segmentStart = j->first; + OutputAccumulator &accumulator = j->second; + + int sz = accumulator.results.size(); + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "reduce: segment starting at " << segmentStart + << " on output " << output << " has " << sz << " result(s)" << std::endl; +#endif + + double totalDuration = 0.0; + //!!! is this right? + if (sz > 0) { +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "last time = " << accumulator.results[sz-1].time + << ", duration = " << accumulator.results[sz-1].duration + << " (step = " << m_stepSize << ", block = " << m_blockSize << ")" + << std::endl; +#endif + totalDuration = toSec((accumulator.results[sz-1].time + + accumulator.results[sz-1].duration) - + segmentStart); + } + + for (int bin = 0; bin < accumulator.bins; ++bin) { + + // work on all values over time for a single bin + + OutputBinSummary summary; + + summary.count = sz; + + summary.minimum = 0.f; + summary.maximum = 0.f; + + summary.median = 0.f; + summary.mode = 0.f; + summary.sum = 0.f; + summary.variance = 0.f; + + summary.median_c = 0.f; + summary.mode_c = 0.f; + summary.mean_c = 0.f; + summary.variance_c = 0.f; + + if (sz == 0) continue; + + std::vector<ValueDurationFloatPair> valvec; + + for (int k = 0; k < sz; ++k) { + while (int(accumulator.results[k].values.size()) < + accumulator.bins) { + accumulator.results[k].values.push_back(0.f); + } + } + + for (int k = 0; k < sz; ++k) { + float value = accumulator.results[k].values[bin]; + valvec.push_back(ValueDurationFloatPair + (value, + toSec(accumulator.results[k].duration))); + } + + std::sort(valvec.begin(), valvec.end()); + + summary.minimum = valvec[0].value; + summary.maximum = valvec[sz-1].value; + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "total duration = " << totalDuration << std::endl; +#endif + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER +/* + std::cerr << "value vector for medians:" << std::endl; + for (int k = 0; k < sz; ++k) { + std::cerr << "(" << valvec[k].value << "," << valvec[k].duration << ") "; + } + std::cerr << std::endl; +*/ +#endif + + if (sz % 2 == 1) { + summary.median = valvec[sz/2].value; + } else { + summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2; + } + + double duracc = 0.0; + summary.median_c = valvec[sz-1].value; + + for (int k = 0; k < sz; ++k) { + duracc += valvec[k].duration; + if (duracc > totalDuration/2) { + summary.median_c = valvec[k].value; + break; + } + } + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "median_c = " << summary.median_c << std::endl; + std::cerr << "median = " << summary.median << std::endl; +#endif + + std::map<float, int> distribution; + + for (int k = 0; k < sz; ++k) { + summary.sum += accumulator.results[k].values[bin]; + distribution[accumulator.results[k].values[bin]] += 1; + } + + int md = 0; + + for (std::map<float, int>::iterator di = distribution.begin(); + di != distribution.end(); ++di) { + if (di->second > md) { + md = di->second; + summary.mode = di->first; + } + } + + distribution.clear(); + + std::map<float, double> distribution_c; + + for (int k = 0; k < sz; ++k) { + distribution_c[accumulator.results[k].values[bin]] + += toSec(accumulator.results[k].duration); + } + + double mrd = 0.0; + + for (std::map<float, double>::iterator di = distribution_c.begin(); + di != distribution_c.end(); ++di) { + if (di->second > mrd) { + mrd = di->second; + summary.mode_c = di->first; + } + } + + distribution_c.clear(); + + if (totalDuration > 0.0) { + + double sum_c = 0.0; + + for (int k = 0; k < sz; ++k) { + double value = accumulator.results[k].values[bin] + * toSec(accumulator.results[k].duration); + sum_c += value; + } + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = " + << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl; +#endif + + summary.mean_c = sum_c / totalDuration; + + for (int k = 0; k < sz; ++k) { + double value = accumulator.results[k].values[bin]; +// * toSec(accumulator.results[k].duration); + summary.variance_c += + (value - summary.mean_c) * (value - summary.mean_c) + * toSec(accumulator.results[k].duration); + } + +// summary.variance_c /= summary.count; + summary.variance_c /= totalDuration; + } + + double mean = summary.sum / summary.count; + +#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER + std::cerr << "mean = " << summary.sum << " / " << summary.count << " = " + << summary.sum / summary.count << std::endl; +#endif + + for (int k = 0; k < sz; ++k) { + float value = accumulator.results[k].values[bin]; + summary.variance += (value - mean) * (value - mean); + } + summary.variance /= summary.count; + + m_summaries[output][segmentStart][bin] = summary; + } + } + } + + m_segmentedAccumulators.clear(); + m_accumulators.clear(); +} + + +} + +} + +_VAMP_SDK_HOSTSPACE_END(PluginSummarisingAdapter.cpp) + diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginWrapper.cpp b/libs/vamp-sdk/src/vamp-hostsdk/PluginWrapper.cpp index dcbdf5b73a..02bdc280f1 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginWrapper.cpp +++ b/libs/vamp-sdk/src/vamp-hostsdk/PluginWrapper.cpp @@ -34,7 +34,9 @@ authorization. */ -#include "PluginWrapper.h" +#include <vamp-hostsdk/PluginWrapper.h> + +_VAMP_SDK_HOSTSPACE_BEGIN(PluginWrapper.cpp) namespace Vamp { @@ -199,3 +201,4 @@ PluginWrapper::getRemainingFeatures() } +_VAMP_SDK_HOSTSPACE_END(PluginWrapper.cpp) diff --git a/libs/vamp-sdk/src/vamp-hostsdk/RealTime.cpp b/libs/vamp-sdk/src/vamp-hostsdk/RealTime.cpp new file mode 100644 index 0000000000..537ccbde5d --- /dev/null +++ b/libs/vamp-sdk/src/vamp-hostsdk/RealTime.cpp @@ -0,0 +1,39 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#include <vamp-hostsdk/RealTime.h> +#include "../vamp-sdk/RealTime.cpp" + diff --git a/libs/vamp-sdk/src/vamp-sdk/PluginAdapter.cpp b/libs/vamp-sdk/src/vamp-sdk/PluginAdapter.cpp new file mode 100644 index 0000000000..fc195d775b --- /dev/null +++ b/libs/vamp-sdk/src/vamp-sdk/PluginAdapter.cpp @@ -0,0 +1,914 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#include <vamp-sdk/PluginAdapter.h> + +#include <cstring> +#include <cstdlib> + +#if ( VAMP_SDK_MAJOR_VERSION != 2 || VAMP_SDK_MINOR_VERSION != 0 ) +#error Incorrect Vamp SDK header included (not the expected 2.0 SDK) +#endif + + +//#define DEBUG_PLUGIN_ADAPTER 1 + +_VAMP_SDK_PLUGSPACE_BEGIN(PluginAdapter.cpp) + +namespace Vamp { + +class PluginAdapterBase::Impl +{ +public: + Impl(PluginAdapterBase *); + ~Impl(); + + const VampPluginDescriptor *getDescriptor(); + +protected: + PluginAdapterBase *m_base; + + static VampPluginHandle vampInstantiate(const VampPluginDescriptor *desc, + float inputSampleRate); + + static void vampCleanup(VampPluginHandle handle); + + static int vampInitialise(VampPluginHandle handle, unsigned int channels, + unsigned int stepSize, unsigned int blockSize); + + static void vampReset(VampPluginHandle handle); + + static float vampGetParameter(VampPluginHandle handle, int param); + static void vampSetParameter(VampPluginHandle handle, int param, float value); + + static unsigned int vampGetCurrentProgram(VampPluginHandle handle); + static void vampSelectProgram(VampPluginHandle handle, unsigned int program); + + static unsigned int vampGetPreferredStepSize(VampPluginHandle handle); + static unsigned int vampGetPreferredBlockSize(VampPluginHandle handle); + static unsigned int vampGetMinChannelCount(VampPluginHandle handle); + static unsigned int vampGetMaxChannelCount(VampPluginHandle handle); + + static unsigned int vampGetOutputCount(VampPluginHandle handle); + + static VampOutputDescriptor *vampGetOutputDescriptor(VampPluginHandle handle, + unsigned int i); + + static void vampReleaseOutputDescriptor(VampOutputDescriptor *desc); + + static VampFeatureList *vampProcess(VampPluginHandle handle, + const float *const *inputBuffers, + int sec, + int nsec); + + static VampFeatureList *vampGetRemainingFeatures(VampPluginHandle handle); + + static void vampReleaseFeatureSet(VampFeatureList *fs); + + void checkOutputMap(Plugin *plugin); + void markOutputsChanged(Plugin *plugin); + + void cleanup(Plugin *plugin); + unsigned int getOutputCount(Plugin *plugin); + VampOutputDescriptor *getOutputDescriptor(Plugin *plugin, + unsigned int i); + VampFeatureList *process(Plugin *plugin, + const float *const *inputBuffers, + int sec, int nsec); + VampFeatureList *getRemainingFeatures(Plugin *plugin); + VampFeatureList *convertFeatures(Plugin *plugin, + const Plugin::FeatureSet &features); + + // maps both plugins and descriptors to adapters + typedef std::map<const void *, Impl *> AdapterMap; + static AdapterMap *m_adapterMap; + static Impl *lookupAdapter(VampPluginHandle); + + bool m_populated; + VampPluginDescriptor m_descriptor; + Plugin::ParameterList m_parameters; + Plugin::ProgramList m_programs; + + typedef std::map<Plugin *, Plugin::OutputList *> OutputMap; + OutputMap m_pluginOutputs; + + std::map<Plugin *, VampFeatureList *> m_fs; + std::map<Plugin *, std::vector<size_t> > m_fsizes; + std::map<Plugin *, std::vector<std::vector<size_t> > > m_fvsizes; + void resizeFS(Plugin *plugin, int n); + void resizeFL(Plugin *plugin, int n, size_t sz); + void resizeFV(Plugin *plugin, int n, int j, size_t sz); +}; + +PluginAdapterBase::PluginAdapterBase() +{ + m_impl = new Impl(this); +} + +PluginAdapterBase::~PluginAdapterBase() +{ + delete m_impl; +} + +const VampPluginDescriptor * +PluginAdapterBase::getDescriptor() +{ + return m_impl->getDescriptor(); +} + +PluginAdapterBase::Impl::Impl(PluginAdapterBase *base) : + m_base(base), + m_populated(false) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl[" << this << "]::Impl" << std::endl; +#endif +} + +const VampPluginDescriptor * +PluginAdapterBase::Impl::getDescriptor() +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl[" << this << "]::getDescriptor" << std::endl; +#endif + + if (m_populated) return &m_descriptor; + + Plugin *plugin = m_base->createPlugin(48000); + + if (plugin->getVampApiVersion() != VAMP_API_VERSION) { + std::cerr << "Vamp::PluginAdapterBase::Impl::getDescriptor: ERROR: " + << "API version " << plugin->getVampApiVersion() + << " for\nplugin \"" << plugin->getIdentifier() << "\" " + << "differs from version " + << VAMP_API_VERSION << " for adapter.\n" + << "This plugin is probably linked against a different version of the Vamp SDK\n" + << "from the version it was compiled with. It will need to be re-linked correctly\n" + << "before it can be used." << std::endl; + delete plugin; + return 0; + } + + m_parameters = plugin->getParameterDescriptors(); + m_programs = plugin->getPrograms(); + + m_descriptor.vampApiVersion = plugin->getVampApiVersion(); + m_descriptor.identifier = strdup(plugin->getIdentifier().c_str()); + m_descriptor.name = strdup(plugin->getName().c_str()); + m_descriptor.description = strdup(plugin->getDescription().c_str()); + m_descriptor.maker = strdup(plugin->getMaker().c_str()); + m_descriptor.pluginVersion = plugin->getPluginVersion(); + m_descriptor.copyright = strdup(plugin->getCopyright().c_str()); + + m_descriptor.parameterCount = m_parameters.size(); + m_descriptor.parameters = (const VampParameterDescriptor **) + malloc(m_parameters.size() * sizeof(VampParameterDescriptor)); + + unsigned int i; + + for (i = 0; i < m_parameters.size(); ++i) { + VampParameterDescriptor *desc = (VampParameterDescriptor *) + malloc(sizeof(VampParameterDescriptor)); + desc->identifier = strdup(m_parameters[i].identifier.c_str()); + desc->name = strdup(m_parameters[i].name.c_str()); + desc->description = strdup(m_parameters[i].description.c_str()); + desc->unit = strdup(m_parameters[i].unit.c_str()); + desc->minValue = m_parameters[i].minValue; + desc->maxValue = m_parameters[i].maxValue; + desc->defaultValue = m_parameters[i].defaultValue; + desc->isQuantized = m_parameters[i].isQuantized; + desc->quantizeStep = m_parameters[i].quantizeStep; + desc->valueNames = 0; + if (desc->isQuantized && !m_parameters[i].valueNames.empty()) { + desc->valueNames = (const char **) + malloc((m_parameters[i].valueNames.size()+1) * sizeof(char *)); + for (unsigned int j = 0; j < m_parameters[i].valueNames.size(); ++j) { + desc->valueNames[j] = strdup(m_parameters[i].valueNames[j].c_str()); + } + desc->valueNames[m_parameters[i].valueNames.size()] = 0; + } + m_descriptor.parameters[i] = desc; + } + + m_descriptor.programCount = m_programs.size(); + m_descriptor.programs = (const char **) + malloc(m_programs.size() * sizeof(const char *)); + + for (i = 0; i < m_programs.size(); ++i) { + m_descriptor.programs[i] = strdup(m_programs[i].c_str()); + } + + if (plugin->getInputDomain() == Plugin::FrequencyDomain) { + m_descriptor.inputDomain = vampFrequencyDomain; + } else { + m_descriptor.inputDomain = vampTimeDomain; + } + + m_descriptor.instantiate = vampInstantiate; + m_descriptor.cleanup = vampCleanup; + m_descriptor.initialise = vampInitialise; + m_descriptor.reset = vampReset; + m_descriptor.getParameter = vampGetParameter; + m_descriptor.setParameter = vampSetParameter; + m_descriptor.getCurrentProgram = vampGetCurrentProgram; + m_descriptor.selectProgram = vampSelectProgram; + m_descriptor.getPreferredStepSize = vampGetPreferredStepSize; + m_descriptor.getPreferredBlockSize = vampGetPreferredBlockSize; + m_descriptor.getMinChannelCount = vampGetMinChannelCount; + m_descriptor.getMaxChannelCount = vampGetMaxChannelCount; + m_descriptor.getOutputCount = vampGetOutputCount; + m_descriptor.getOutputDescriptor = vampGetOutputDescriptor; + m_descriptor.releaseOutputDescriptor = vampReleaseOutputDescriptor; + m_descriptor.process = vampProcess; + m_descriptor.getRemainingFeatures = vampGetRemainingFeatures; + m_descriptor.releaseFeatureSet = vampReleaseFeatureSet; + + if (!m_adapterMap) { + m_adapterMap = new AdapterMap; + } + (*m_adapterMap)[&m_descriptor] = this; + + delete plugin; + + m_populated = true; + return &m_descriptor; +} + +PluginAdapterBase::Impl::~Impl() +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl[" << this << "]::~Impl" << std::endl; +#endif + + if (!m_populated) return; + + free((void *)m_descriptor.identifier); + free((void *)m_descriptor.name); + free((void *)m_descriptor.description); + free((void *)m_descriptor.maker); + free((void *)m_descriptor.copyright); + + for (unsigned int i = 0; i < m_descriptor.parameterCount; ++i) { + const VampParameterDescriptor *desc = m_descriptor.parameters[i]; + free((void *)desc->identifier); + free((void *)desc->name); + free((void *)desc->description); + free((void *)desc->unit); + if (desc->valueNames) { + for (unsigned int j = 0; desc->valueNames[j]; ++j) { + free((void *)desc->valueNames[j]); + } + free((void *)desc->valueNames); + } + } + free((void *)m_descriptor.parameters); + + for (unsigned int i = 0; i < m_descriptor.programCount; ++i) { + free((void *)m_descriptor.programs[i]); + } + free((void *)m_descriptor.programs); + + if (m_adapterMap) { + + m_adapterMap->erase(&m_descriptor); + + if (m_adapterMap->empty()) { + delete m_adapterMap; + m_adapterMap = 0; + } + } +} + +PluginAdapterBase::Impl * +PluginAdapterBase::Impl::lookupAdapter(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::lookupAdapter(" << handle << ")" << std::endl; +#endif + + if (!m_adapterMap) return 0; + AdapterMap::const_iterator i = m_adapterMap->find(handle); + if (i == m_adapterMap->end()) return 0; + return i->second; +} + +VampPluginHandle +PluginAdapterBase::Impl::vampInstantiate(const VampPluginDescriptor *desc, + float inputSampleRate) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampInstantiate(" << desc << ")" << std::endl; +#endif + + if (!m_adapterMap) { + m_adapterMap = new AdapterMap(); + } + + if (m_adapterMap->find(desc) == m_adapterMap->end()) { + std::cerr << "WARNING: PluginAdapterBase::Impl::vampInstantiate: Descriptor " << desc << " not in adapter map" << std::endl; + return 0; + } + + Impl *adapter = (*m_adapterMap)[desc]; + if (desc != &adapter->m_descriptor) return 0; + + Plugin *plugin = adapter->m_base->createPlugin(inputSampleRate); + if (plugin) { + (*m_adapterMap)[plugin] = adapter; + } + +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampInstantiate(" << desc << "): returning handle " << plugin << std::endl; +#endif + + return plugin; +} + +void +PluginAdapterBase::Impl::vampCleanup(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampCleanup(" << handle << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + if (!adapter) { + delete ((Plugin *)handle); + return; + } + adapter->cleanup(((Plugin *)handle)); +} + +int +PluginAdapterBase::Impl::vampInitialise(VampPluginHandle handle, + unsigned int channels, + unsigned int stepSize, + unsigned int blockSize) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampInitialise(" << handle << ", " << channels << ", " << stepSize << ", " << blockSize << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + if (!adapter) return 0; + bool result = ((Plugin *)handle)->initialise(channels, stepSize, blockSize); + adapter->markOutputsChanged((Plugin *)handle); + return result ? 1 : 0; +} + +void +PluginAdapterBase::Impl::vampReset(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampReset(" << handle << ")" << std::endl; +#endif + + ((Plugin *)handle)->reset(); +} + +float +PluginAdapterBase::Impl::vampGetParameter(VampPluginHandle handle, + int param) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampGetParameter(" << handle << ", " << param << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + if (!adapter) return 0.0; + Plugin::ParameterList &list = adapter->m_parameters; + return ((Plugin *)handle)->getParameter(list[param].identifier); +} + +void +PluginAdapterBase::Impl::vampSetParameter(VampPluginHandle handle, + int param, float value) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampSetParameter(" << handle << ", " << param << ", " << value << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + if (!adapter) return; + Plugin::ParameterList &list = adapter->m_parameters; + ((Plugin *)handle)->setParameter(list[param].identifier, value); + adapter->markOutputsChanged((Plugin *)handle); +} + +unsigned int +PluginAdapterBase::Impl::vampGetCurrentProgram(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampGetCurrentProgram(" << handle << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + if (!adapter) return 0; + Plugin::ProgramList &list = adapter->m_programs; + std::string program = ((Plugin *)handle)->getCurrentProgram(); + for (unsigned int i = 0; i < list.size(); ++i) { + if (list[i] == program) return i; + } + return 0; +} + +void +PluginAdapterBase::Impl::vampSelectProgram(VampPluginHandle handle, + unsigned int program) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampSelectProgram(" << handle << ", " << program << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + if (!adapter) return; + + Plugin::ProgramList &list = adapter->m_programs; + ((Plugin *)handle)->selectProgram(list[program]); + + adapter->markOutputsChanged((Plugin *)handle); +} + +unsigned int +PluginAdapterBase::Impl::vampGetPreferredStepSize(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampGetPreferredStepSize(" << handle << ")" << std::endl; +#endif + + return ((Plugin *)handle)->getPreferredStepSize(); +} + +unsigned int +PluginAdapterBase::Impl::vampGetPreferredBlockSize(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampGetPreferredBlockSize(" << handle << ")" << std::endl; +#endif + + return ((Plugin *)handle)->getPreferredBlockSize(); +} + +unsigned int +PluginAdapterBase::Impl::vampGetMinChannelCount(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampGetMinChannelCount(" << handle << ")" << std::endl; +#endif + + return ((Plugin *)handle)->getMinChannelCount(); +} + +unsigned int +PluginAdapterBase::Impl::vampGetMaxChannelCount(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampGetMaxChannelCount(" << handle << ")" << std::endl; +#endif + + return ((Plugin *)handle)->getMaxChannelCount(); +} + +unsigned int +PluginAdapterBase::Impl::vampGetOutputCount(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampGetOutputCount(" << handle << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + +// std::cerr << "vampGetOutputCount: handle " << handle << " -> adapter "<< adapter << std::endl; + + if (!adapter) return 0; + return adapter->getOutputCount((Plugin *)handle); +} + +VampOutputDescriptor * +PluginAdapterBase::Impl::vampGetOutputDescriptor(VampPluginHandle handle, + unsigned int i) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampGetOutputDescriptor(" << handle << ", " << i << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + +// std::cerr << "vampGetOutputDescriptor: handle " << handle << " -> adapter "<< adapter << std::endl; + + if (!adapter) return 0; + return adapter->getOutputDescriptor((Plugin *)handle, i); +} + +void +PluginAdapterBase::Impl::vampReleaseOutputDescriptor(VampOutputDescriptor *desc) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampReleaseOutputDescriptor(" << desc << ")" << std::endl; +#endif + + if (desc->identifier) free((void *)desc->identifier); + if (desc->name) free((void *)desc->name); + if (desc->description) free((void *)desc->description); + if (desc->unit) free((void *)desc->unit); + if (desc->hasFixedBinCount && desc->binNames) { + for (unsigned int i = 0; i < desc->binCount; ++i) { + if (desc->binNames[i]) { + free((void *)desc->binNames[i]); + } + } + } + if (desc->binNames) free((void *)desc->binNames); + free((void *)desc); +} + +VampFeatureList * +PluginAdapterBase::Impl::vampProcess(VampPluginHandle handle, + const float *const *inputBuffers, + int sec, + int nsec) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampProcess(" << handle << ", " << sec << ", " << nsec << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + if (!adapter) return 0; + return adapter->process((Plugin *)handle, inputBuffers, sec, nsec); +} + +VampFeatureList * +PluginAdapterBase::Impl::vampGetRemainingFeatures(VampPluginHandle handle) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampGetRemainingFeatures(" << handle << ")" << std::endl; +#endif + + Impl *adapter = lookupAdapter(handle); + if (!adapter) return 0; + return adapter->getRemainingFeatures((Plugin *)handle); +} + +void +PluginAdapterBase::Impl::vampReleaseFeatureSet(VampFeatureList *fs) +{ +#ifdef DEBUG_PLUGIN_ADAPTER + std::cerr << "PluginAdapterBase::Impl::vampReleaseFeatureSet" << std::endl; +#endif +} + +void +PluginAdapterBase::Impl::cleanup(Plugin *plugin) +{ + if (m_fs.find(plugin) != m_fs.end()) { + size_t outputCount = 0; + if (m_pluginOutputs[plugin]) { + outputCount = m_pluginOutputs[plugin]->size(); + } + VampFeatureList *list = m_fs[plugin]; + for (unsigned int i = 0; i < outputCount; ++i) { + for (unsigned int j = 0; j < m_fsizes[plugin][i]; ++j) { + if (list[i].features[j].v1.label) { + free(list[i].features[j].v1.label); + } + if (list[i].features[j].v1.values) { + free(list[i].features[j].v1.values); + } + } + if (list[i].features) free(list[i].features); + } + m_fs.erase(plugin); + m_fsizes.erase(plugin); + m_fvsizes.erase(plugin); + } + + if (m_pluginOutputs.find(plugin) != m_pluginOutputs.end()) { + delete m_pluginOutputs[plugin]; + m_pluginOutputs.erase(plugin); + } + + if (m_adapterMap) { + m_adapterMap->erase(plugin); + + if (m_adapterMap->empty()) { + delete m_adapterMap; + m_adapterMap = 0; + } + } + + delete ((Plugin *)plugin); +} + +void +PluginAdapterBase::Impl::checkOutputMap(Plugin *plugin) +{ + OutputMap::iterator i = m_pluginOutputs.find(plugin); + + if (i == m_pluginOutputs.end() || !i->second) { + + m_pluginOutputs[plugin] = new Plugin::OutputList + (plugin->getOutputDescriptors()); + +// std::cerr << "PluginAdapterBase::Impl::checkOutputMap: Have " << m_pluginOutputs[plugin]->size() << " outputs for plugin " << plugin->getIdentifier() << std::endl; + } +} + +void +PluginAdapterBase::Impl::markOutputsChanged(Plugin *plugin) +{ + OutputMap::iterator i = m_pluginOutputs.find(plugin); + +// std::cerr << "PluginAdapterBase::Impl::markOutputsChanged" << std::endl; + + if (i != m_pluginOutputs.end()) { + + Plugin::OutputList *list = i->second; + m_pluginOutputs.erase(i); + delete list; + } +} + +unsigned int +PluginAdapterBase::Impl::getOutputCount(Plugin *plugin) +{ + checkOutputMap(plugin); + + return m_pluginOutputs[plugin]->size(); +} + +VampOutputDescriptor * +PluginAdapterBase::Impl::getOutputDescriptor(Plugin *plugin, + unsigned int i) +{ + checkOutputMap(plugin); + + Plugin::OutputDescriptor &od = + (*m_pluginOutputs[plugin])[i]; + + VampOutputDescriptor *desc = (VampOutputDescriptor *) + malloc(sizeof(VampOutputDescriptor)); + + desc->identifier = strdup(od.identifier.c_str()); + desc->name = strdup(od.name.c_str()); + desc->description = strdup(od.description.c_str()); + desc->unit = strdup(od.unit.c_str()); + desc->hasFixedBinCount = od.hasFixedBinCount; + desc->binCount = od.binCount; + + if (od.hasFixedBinCount && od.binCount > 0 + // We would like to do "&& !od.binNames.empty()" here -- but we + // can't, because it will crash older versions of the host adapter + // which try to copy the names across whenever the bin count is + // non-zero, regardless of whether they exist or not + ) { + desc->binNames = (const char **) + malloc(od.binCount * sizeof(const char *)); + + for (unsigned int i = 0; i < od.binCount; ++i) { + if (i < od.binNames.size()) { + desc->binNames[i] = strdup(od.binNames[i].c_str()); + } else { + desc->binNames[i] = 0; + } + } + } else { + desc->binNames = 0; + } + + desc->hasKnownExtents = od.hasKnownExtents; + desc->minValue = od.minValue; + desc->maxValue = od.maxValue; + desc->isQuantized = od.isQuantized; + desc->quantizeStep = od.quantizeStep; + + switch (od.sampleType) { + case Plugin::OutputDescriptor::OneSamplePerStep: + desc->sampleType = vampOneSamplePerStep; break; + case Plugin::OutputDescriptor::FixedSampleRate: + desc->sampleType = vampFixedSampleRate; break; + case Plugin::OutputDescriptor::VariableSampleRate: + desc->sampleType = vampVariableSampleRate; break; + } + + desc->sampleRate = od.sampleRate; + desc->hasDuration = od.hasDuration; + + return desc; +} + +VampFeatureList * +PluginAdapterBase::Impl::process(Plugin *plugin, + const float *const *inputBuffers, + int sec, int nsec) +{ +// std::cerr << "PluginAdapterBase::Impl::process" << std::endl; + RealTime rt(sec, nsec); + checkOutputMap(plugin); + return convertFeatures(plugin, plugin->process(inputBuffers, rt)); +} + +VampFeatureList * +PluginAdapterBase::Impl::getRemainingFeatures(Plugin *plugin) +{ +// std::cerr << "PluginAdapterBase::Impl::getRemainingFeatures" << std::endl; + checkOutputMap(plugin); + return convertFeatures(plugin, plugin->getRemainingFeatures()); +} + +VampFeatureList * +PluginAdapterBase::Impl::convertFeatures(Plugin *plugin, + const Plugin::FeatureSet &features) +{ + int lastN = -1; + + int outputCount = 0; + if (m_pluginOutputs[plugin]) outputCount = m_pluginOutputs[plugin]->size(); + + resizeFS(plugin, outputCount); + VampFeatureList *fs = m_fs[plugin]; + +// std::cerr << "PluginAdapter(v2)::convertFeatures: NOTE: sizeof(Feature) == " << sizeof(Plugin::Feature) << ", sizeof(VampFeature) == " << sizeof(VampFeature) << ", sizeof(VampFeatureList) == " << sizeof(VampFeatureList) << std::endl; + + for (Plugin::FeatureSet::const_iterator fi = features.begin(); + fi != features.end(); ++fi) { + + int n = fi->first; + +// std::cerr << "PluginAdapterBase::Impl::convertFeatures: n = " << n << std::endl; + + if (n >= int(outputCount)) { + std::cerr << "WARNING: PluginAdapterBase::Impl::convertFeatures: Too many outputs from plugin (" << n+1 << ", only should be " << outputCount << ")" << std::endl; + continue; + } + + if (n > lastN + 1) { + for (int i = lastN + 1; i < n; ++i) { + fs[i].featureCount = 0; + } + } + + const Plugin::FeatureList &fl = fi->second; + + size_t sz = fl.size(); + if (sz > m_fsizes[plugin][n]) resizeFL(plugin, n, sz); + fs[n].featureCount = sz; + + for (size_t j = 0; j < sz; ++j) { + +// std::cerr << "PluginAdapterBase::Impl::convertFeatures: j = " << j << std::endl; + + VampFeature *feature = &fs[n].features[j].v1; + + feature->hasTimestamp = fl[j].hasTimestamp; + feature->sec = fl[j].timestamp.sec; + feature->nsec = fl[j].timestamp.nsec; + feature->valueCount = fl[j].values.size(); + + VampFeatureV2 *v2 = &fs[n].features[j + sz].v2; + + v2->hasDuration = fl[j].hasDuration; + v2->durationSec = fl[j].duration.sec; + v2->durationNsec = fl[j].duration.nsec; + + if (feature->label) free(feature->label); + + if (fl[j].label.empty()) { + feature->label = 0; + } else { + feature->label = strdup(fl[j].label.c_str()); + } + + if (feature->valueCount > m_fvsizes[plugin][n][j]) { + resizeFV(plugin, n, j, feature->valueCount); + } + + for (unsigned int k = 0; k < feature->valueCount; ++k) { +// std::cerr << "PluginAdapterBase::Impl::convertFeatures: k = " << k << std::endl; + feature->values[k] = fl[j].values[k]; + } + } + + lastN = n; + } + + if (lastN == -1) return 0; + + if (int(outputCount) > lastN + 1) { + for (int i = lastN + 1; i < int(outputCount); ++i) { + fs[i].featureCount = 0; + } + } + +// std::cerr << "PluginAdapter(v2)::convertFeatures: NOTE: have " << outputCount << " outputs" << std::endl; +// for (int i = 0; i < outputCount; ++i) { +// std::cerr << "PluginAdapter(v2)::convertFeatures: NOTE: output " << i << " has " << fs[i].featureCount << " features" << std::endl; +// } + + + return fs; +} + +void +PluginAdapterBase::Impl::resizeFS(Plugin *plugin, int n) +{ +// std::cerr << "PluginAdapterBase::Impl::resizeFS(" << plugin << ", " << n << ")" << std::endl; + + int i = m_fsizes[plugin].size(); + if (i >= n) return; + +// std::cerr << "resizing from " << i << std::endl; + + m_fs[plugin] = (VampFeatureList *)realloc + (m_fs[plugin], n * sizeof(VampFeatureList)); + + while (i < n) { + m_fs[plugin][i].featureCount = 0; + m_fs[plugin][i].features = 0; + m_fsizes[plugin].push_back(0); + m_fvsizes[plugin].push_back(std::vector<size_t>()); + i++; + } +} + +void +PluginAdapterBase::Impl::resizeFL(Plugin *plugin, int n, size_t sz) +{ +// std::cerr << "PluginAdapterBase::Impl::resizeFL(" << plugin << ", " << n << ", " +// << sz << ")" << std::endl; + + size_t i = m_fsizes[plugin][n]; + if (i >= sz) return; + +// std::cerr << "resizing from " << i << std::endl; + + m_fs[plugin][n].features = (VampFeatureUnion *)realloc + (m_fs[plugin][n].features, 2 * sz * sizeof(VampFeatureUnion)); + + while (m_fsizes[plugin][n] < sz) { + m_fs[plugin][n].features[m_fsizes[plugin][n]].v1.hasTimestamp = 0; + m_fs[plugin][n].features[m_fsizes[plugin][n]].v1.valueCount = 0; + m_fs[plugin][n].features[m_fsizes[plugin][n]].v1.values = 0; + m_fs[plugin][n].features[m_fsizes[plugin][n]].v1.label = 0; + m_fs[plugin][n].features[m_fsizes[plugin][n] + sz].v2.hasDuration = 0; + m_fvsizes[plugin][n].push_back(0); + m_fsizes[plugin][n]++; + } +} + +void +PluginAdapterBase::Impl::resizeFV(Plugin *plugin, int n, int j, size_t sz) +{ +// std::cerr << "PluginAdapterBase::Impl::resizeFV(" << plugin << ", " << n << ", " +// << j << ", " << sz << ")" << std::endl; + + size_t i = m_fvsizes[plugin][n][j]; + if (i >= sz) return; + +// std::cerr << "resizing from " << i << std::endl; + + m_fs[plugin][n].features[j].v1.values = (float *)realloc + (m_fs[plugin][n].features[j].v1.values, sz * sizeof(float)); + + m_fvsizes[plugin][n][j] = sz; +} + +PluginAdapterBase::Impl::AdapterMap * +PluginAdapterBase::Impl::m_adapterMap = 0; + +} + +_VAMP_SDK_PLUGSPACE_END(PluginAdapter.cpp) + diff --git a/libs/vamp-sdk/src/vamp-sdk/RealTime.cpp b/libs/vamp-sdk/src/vamp-sdk/RealTime.cpp new file mode 100644 index 0000000000..d3c4364785 --- /dev/null +++ b/libs/vamp-sdk/src/vamp-sdk/RealTime.cpp @@ -0,0 +1,252 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +/* + This is a modified version of a source file from the + Rosegarden MIDI and audio sequencer and notation editor. + This file copyright 2000-2006 Chris Cannam. + Relicensed by the author as detailed above. +*/ + +#include <iostream> + +#if (__GNUC__ < 3) +#include <strstream> +#define stringstream strstream +#else +#include <sstream> +#endif + +using std::cerr; +using std::endl; + +#ifndef _WIN32 +#include <sys/time.h> +#endif + +#include <vamp-sdk/RealTime.h> + +_VAMP_SDK_PLUGSPACE_BEGIN(RealTime.cpp) + +namespace Vamp { + +// A RealTime consists of two ints that must be at least 32 bits each. +// A signed 32-bit int can store values exceeding +/- 2 billion. This +// means we can safely use our lower int for nanoseconds, as there are +// 1 billion nanoseconds in a second and we need to handle double that +// because of the implementations of addition etc that we use. +// +// The maximum valid RealTime on a 32-bit system is somewhere around +// 68 years: 999999999 nanoseconds longer than the classic Unix epoch. + +#define ONE_BILLION 1000000000 + +RealTime::RealTime(int s, int n) : + sec(s), nsec(n) +{ + if (sec == 0) { + while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; } + while (nsec >= ONE_BILLION) { nsec -= ONE_BILLION; ++sec; } + } else if (sec < 0) { + while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; } + while (nsec > 0) { nsec -= ONE_BILLION; ++sec; } + } else { + while (nsec >= ONE_BILLION) { nsec -= ONE_BILLION; ++sec; } + while (nsec < 0) { nsec += ONE_BILLION; --sec; } + } +} + +RealTime +RealTime::fromSeconds(double sec) +{ + return RealTime(int(sec), int((sec - int(sec)) * ONE_BILLION + 0.5)); +} + +RealTime +RealTime::fromMilliseconds(int msec) +{ + return RealTime(msec / 1000, (msec % 1000) * 1000000); +} + +#ifndef _WIN32 +RealTime +RealTime::fromTimeval(const struct timeval &tv) +{ + return RealTime(tv.tv_sec, tv.tv_usec * 1000); +} +#endif + +std::ostream &operator<<(std::ostream &out, const RealTime &rt) +{ + if (rt < RealTime::zeroTime) { + out << "-"; + } else { + out << " "; + } + + int s = (rt.sec < 0 ? -rt.sec : rt.sec); + int n = (rt.nsec < 0 ? -rt.nsec : rt.nsec); + + out << s << "."; + + int nn(n); + if (nn == 0) out << "00000000"; + else while (nn < (ONE_BILLION / 10)) { + out << "0"; + nn *= 10; + } + + out << n << "R"; + return out; +} + +std::string +RealTime::toString() const +{ + std::stringstream out; + out << *this; + +#if (__GNUC__ < 3) + out << std::ends; +#endif + + std::string s = out.str(); + + // remove trailing R + return s.substr(0, s.length() - 1); +} + +std::string +RealTime::toText(bool fixedDp) const +{ + if (*this < RealTime::zeroTime) return "-" + (-*this).toText(); + + std::stringstream out; + + if (sec >= 3600) { + out << (sec / 3600) << ":"; + } + + if (sec >= 60) { + out << (sec % 3600) / 60 << ":"; + } + + if (sec >= 10) { + out << ((sec % 60) / 10); + } + + out << (sec % 10); + + int ms = msec(); + + if (ms != 0) { + out << "."; + out << (ms / 100); + ms = ms % 100; + if (ms != 0) { + out << (ms / 10); + ms = ms % 10; + } else if (fixedDp) { + out << "0"; + } + if (ms != 0) { + out << ms; + } else if (fixedDp) { + out << "0"; + } + } else if (fixedDp) { + out << ".000"; + } + +#if (__GNUC__ < 3) + out << std::ends; +#endif + + std::string s = out.str(); + + return s; +} + + +RealTime +RealTime::operator/(int d) const +{ + int secdiv = sec / d; + int secrem = sec % d; + + double nsecdiv = (double(nsec) + ONE_BILLION * double(secrem)) / d; + + return RealTime(secdiv, int(nsecdiv + 0.5)); +} + +double +RealTime::operator/(const RealTime &r) const +{ + double lTotal = double(sec) * ONE_BILLION + double(nsec); + double rTotal = double(r.sec) * ONE_BILLION + double(r.nsec); + + if (rTotal == 0) return 0.0; + else return lTotal/rTotal; +} + +long +RealTime::realTime2Frame(const RealTime &time, unsigned int sampleRate) +{ + if (time < zeroTime) return -realTime2Frame(-time, sampleRate); + double s = time.sec + double(time.nsec + 1) / 1000000000.0; + return long(s * sampleRate); +} + +RealTime +RealTime::frame2RealTime(long frame, unsigned int sampleRate) +{ + if (frame < 0) return -frame2RealTime(-frame, sampleRate); + + RealTime rt; + rt.sec = frame / long(sampleRate); + frame -= rt.sec * long(sampleRate); + rt.nsec = (int)(((double(frame) * 1000000.0) / sampleRate) * 1000.0); + return rt; +} + +const RealTime RealTime::zeroTime(0,0); + +} + +_VAMP_SDK_PLUGSPACE_END(RealTime.cpp) + + + diff --git a/libs/vamp-sdk/vamp-hostsdk/Plugin.h b/libs/vamp-sdk/vamp-hostsdk/Plugin.h new file mode 100644 index 0000000000..72a3b640d5 --- /dev/null +++ b/libs/vamp-sdk/vamp-hostsdk/Plugin.h @@ -0,0 +1,47 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_HOSTSDK_PLUGIN_H_ +#define _VAMP_HOSTSDK_PLUGIN_H_ + +// Do not include vamp-sdk/Plugin.h directly from host code. Always +// use this header instead. + +#include "hostguard.h" + +#include <vamp-sdk/Plugin.h> + +#endif diff --git a/libs/vamp-sdk/vamp-hostsdk/PluginBase.h b/libs/vamp-sdk/vamp-hostsdk/PluginBase.h new file mode 100644 index 0000000000..f12c25a715 --- /dev/null +++ b/libs/vamp-sdk/vamp-hostsdk/PluginBase.h @@ -0,0 +1,47 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_HOSTSDK_PLUGIN_BASE_H_ +#define _VAMP_HOSTSDK_PLUGIN_BASE_H_ + +// Do not include vamp-sdk/PluginBase.h directly from host code. +// Always use this header instead. + +#include "hostguard.h" + +#include <vamp-sdk/PluginBase.h> + +#endif diff --git a/libs/vamp-sdk/vamp-hostsdk/PluginBufferingAdapter.h b/libs/vamp-sdk/vamp-hostsdk/PluginBufferingAdapter.h new file mode 100644 index 0000000000..21c2b36d74 --- /dev/null +++ b/libs/vamp-sdk/vamp-hostsdk/PluginBufferingAdapter.h @@ -0,0 +1,194 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006-2007 Chris Cannam and QMUL. + This file by Mark Levy and Chris Cannam, Copyright 2007-2008 QMUL. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_PLUGIN_BUFFERING_ADAPTER_H_ +#define _VAMP_PLUGIN_BUFFERING_ADAPTER_H_ + +#include "hostguard.h" +#include "PluginWrapper.h" + +_VAMP_SDK_HOSTSPACE_BEGIN(PluginBufferingAdapter.h) + +namespace Vamp { + +namespace HostExt { + +/** + * \class PluginBufferingAdapter PluginBufferingAdapter.h <vamp-hostsdk/PluginBufferingAdapter.h> + * + * PluginBufferingAdapter is a Vamp plugin adapter that allows plugins + * to be used by a host supplying an audio stream in non-overlapping + * buffers of arbitrary size. + * + * A host using PluginBufferingAdapter may ignore the preferred step + * and block size reported by the plugin, and still expect the plugin + * to run. The value of blockSize and stepSize passed to initialise + * should be the size of the buffer which the host will supply; the + * stepSize should be equal to the blockSize. + * + * If the internal step size used for the plugin differs from that + * supplied by the host, the adapter will modify the sample type and + * rate specifications for the plugin outputs appropriately, and set + * timestamps on the output features for outputs that formerly used a + * different sample rate specification. This is necessary in order to + * obtain correct time stamping. + * + * In other respects, the PluginBufferingAdapter behaves identically + * to the plugin that it wraps. The wrapped plugin will be deleted + * when the wrapper is deleted. + */ + +class PluginBufferingAdapter : public PluginWrapper +{ +public: + /** + * Construct a PluginBufferingAdapter wrapping the given plugin. + * The adapter takes ownership of the plugin, which will be + * deleted when the adapter is deleted. + */ + PluginBufferingAdapter(Plugin *plugin); + virtual ~PluginBufferingAdapter(); + + /** + * Return the preferred step size for this adapter. + * + * Because of the way this adapter works, its preferred step size + * will always be the same as its preferred block size. This may + * or may not be the same as the preferred step size of the + * underlying plugin, which may be obtained by calling + * getPluginPreferredStepSize(). + */ + size_t getPreferredStepSize() const; + + /** + * Return the preferred block size for this adapter. + * + * This may or may not be the same as the preferred block size of + * the underlying plugin, which may be obtained by calling + * getPluginPreferredBlockSize(). + * + * Note that this adapter may be initialised with any block size, + * not just its supposedly preferred one. + */ + size_t getPreferredBlockSize() const; + + /** + * Initialise the adapter (and therefore the plugin) for the given + * number of channels. Initialise the adapter for the given step + * and block size, which must be equal. + * + * The step and block size used for the underlying plugin will + * depend on its preferences, or any values previously passed to + * setPluginStepSize and setPluginBlockSize. + */ + bool initialise(size_t channels, size_t stepSize, size_t blockSize); + + /** + * Return the preferred step size of the plugin wrapped by this + * adapter. + * + * This is included mainly for informational purposes. This value + * is not likely to be a valid step size for the adapter itself, + * and it is not usually of any use in interpreting the results + * (because the adapter re-writes OneSamplePerStep outputs to + * FixedSampleRate so that the hop size no longer needs to be + * known beforehand in order to interpret them). + */ + size_t getPluginPreferredStepSize() const; + + /** + * Return the preferred block size of the plugin wrapped by this + * adapter. + * + * This is included mainly for informational purposes. + */ + size_t getPluginPreferredBlockSize() const; + + /** + * Set the step size that will be used for the underlying plugin + * when initialise() is called. If this is not set, the plugin's + * own preferred step size will be used. You will not usually + * need to call this function. If you do call it, it must be + * before the first call to initialise(). + */ + void setPluginStepSize(size_t stepSize); + + /** + * Set the block size that will be used for the underlying plugin + * when initialise() is called. If this is not set, the plugin's + * own preferred block size will be used. You will not usually + * need to call this function. If you do call it, it must be + * before the first call to initialise(). + */ + void setPluginBlockSize(size_t blockSize); + + /** + * Return the step and block sizes that were actually used when + * initialising the underlying plugin. + * + * This is included mainly for informational purposes. You will + * not usually need to call this function. If this is called + * before initialise(), it will return 0 for both values. If it + * is called after a failed call to initialise(), it will return + * the values that were used in the failed call to the plugin's + * initialise() function. + */ + void getActualStepAndBlockSizes(size_t &stepSize, size_t &blockSize); + + void setParameter(std::string, float); + void selectProgram(std::string); + + OutputList getOutputDescriptors() const; + + void reset(); + + FeatureSet process(const float *const *inputBuffers, RealTime timestamp); + + FeatureSet getRemainingFeatures(); + +protected: + class Impl; + Impl *m_impl; +}; + +} + +} + +_VAMP_SDK_HOSTSPACE_END(PluginBufferingAdapter.h) + +#endif diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.h b/libs/vamp-sdk/vamp-hostsdk/PluginChannelAdapter.h index a2b4d89aee..28ee82ae7d 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.h +++ b/libs/vamp-sdk/vamp-hostsdk/PluginChannelAdapter.h @@ -37,14 +37,17 @@ #ifndef _VAMP_PLUGIN_CHANNEL_ADAPTER_H_ #define _VAMP_PLUGIN_CHANNEL_ADAPTER_H_ +#include "hostguard.h" #include "PluginWrapper.h" +_VAMP_SDK_HOSTSPACE_BEGIN(PluginChannelAdapter.h) + namespace Vamp { namespace HostExt { /** - * \class PluginChannelAdapter PluginChannelAdapter.h <vamp-sdk/hostext/PluginChannelAdapter.h> + * \class PluginChannelAdapter PluginChannelAdapter.h <vamp-hostsdk/PluginChannelAdapter.h> * * PluginChannelAdapter is a Vamp plugin adapter that implements a * policy for management of plugins that expect a different number of @@ -109,13 +112,29 @@ namespace HostExt { class PluginChannelAdapter : public PluginWrapper { public: - PluginChannelAdapter(Plugin *plugin); // I take ownership of plugin + /** + * Construct a PluginChannelAdapter wrapping the given plugin. + * The adapter takes ownership of the plugin, which will be + * deleted when the adapter is deleted. + */ + PluginChannelAdapter(Plugin *plugin); virtual ~PluginChannelAdapter(); bool initialise(size_t channels, size_t stepSize, size_t blockSize); FeatureSet process(const float *const *inputBuffers, RealTime timestamp); + /** + * Call process(), providing interleaved audio data with the + * number of channels passed to initialise(). The adapter will + * de-interleave into temporary buffers as appropriate before + * calling process(). + * + * \note This function was introduced in version 1.4 of the Vamp + * plugin SDK. + */ + FeatureSet processInterleaved(const float *inputBuffer, RealTime timestamp); + protected: class Impl; Impl *m_impl; @@ -125,4 +144,6 @@ protected: } +_VAMP_SDK_HOSTSPACE_END(PluginChannelAdapter.h) + #endif diff --git a/libs/vamp-sdk/vamp-hostsdk/PluginHostAdapter.h b/libs/vamp-sdk/vamp-hostsdk/PluginHostAdapter.h new file mode 100644 index 0000000000..2ca1d69949 --- /dev/null +++ b/libs/vamp-sdk/vamp-hostsdk/PluginHostAdapter.h @@ -0,0 +1,123 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_PLUGIN_HOST_ADAPTER_H_ +#define _VAMP_PLUGIN_HOST_ADAPTER_H_ + +#include "hostguard.h" +#include "Plugin.h" + +#include <vamp/vamp.h> + +#include <vector> + +_VAMP_SDK_HOSTSPACE_BEGIN(PluginHostAdapter.h) + +namespace Vamp { + +/** + * \class PluginHostAdapter PluginHostAdapter.h <vamp-hostsdk/PluginHostAdapter.h> + * + * PluginHostAdapter is a wrapper class that a Vamp host can use to + * make the C-language VampPluginDescriptor object appear as a C++ + * Vamp::Plugin object. + * + * The Vamp API is defined in vamp/vamp.h as a C API. The C++ objects + * used for convenience by plugins and hosts actually communicate + * using the C low-level API, but the details of this communication + * are handled seamlessly by the Vamp SDK implementation provided the + * plugin and host use the proper C++ wrapper objects. + * + * See also PluginAdapter, the plugin-side wrapper that makes a C++ + * plugin object available using the C query API. + */ + +class PluginHostAdapter : public Plugin +{ +public: + PluginHostAdapter(const VampPluginDescriptor *descriptor, + float inputSampleRate); + virtual ~PluginHostAdapter(); + + static std::vector<std::string> getPluginPath(); + + bool initialise(size_t channels, size_t stepSize, size_t blockSize); + void reset(); + + InputDomain getInputDomain() const; + + unsigned int getVampApiVersion() const; + std::string getIdentifier() const; + std::string getName() const; + std::string getDescription() const; + std::string getMaker() const; + int getPluginVersion() const; + std::string getCopyright() const; + + ParameterList getParameterDescriptors() const; + float getParameter(std::string) const; + void setParameter(std::string, float); + + ProgramList getPrograms() const; + std::string getCurrentProgram() const; + void selectProgram(std::string); + + size_t getPreferredStepSize() const; + size_t getPreferredBlockSize() const; + + size_t getMinChannelCount() const; + size_t getMaxChannelCount() const; + + OutputList getOutputDescriptors() const; + + FeatureSet process(const float *const *inputBuffers, RealTime timestamp); + + FeatureSet getRemainingFeatures(); + +protected: + void convertFeatures(VampFeatureList *, FeatureSet &); + + const VampPluginDescriptor *m_descriptor; + VampPluginHandle m_handle; +}; + +} + +_VAMP_SDK_HOSTSPACE_END(PluginHostAdapter.h) + +#endif + + diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.h b/libs/vamp-sdk/vamp-hostsdk/PluginInputDomainAdapter.h index 73da053149..7f7c11c3be 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.h +++ b/libs/vamp-sdk/vamp-hostsdk/PluginInputDomainAdapter.h @@ -37,14 +37,17 @@ #ifndef _VAMP_PLUGIN_INPUT_DOMAIN_ADAPTER_H_ #define _VAMP_PLUGIN_INPUT_DOMAIN_ADAPTER_H_ +#include "hostguard.h" #include "PluginWrapper.h" +_VAMP_SDK_HOSTSPACE_BEGIN(PluginInputDomainAdapter.h) + namespace Vamp { namespace HostExt { /** - * \class PluginInputDomainAdapter PluginInputDomainAdapter.h <vamp-sdk/hostext/PluginInputDomainAdapter.h> + * \class PluginInputDomainAdapter PluginInputDomainAdapter.h <vamp-hostsdk/PluginInputDomainAdapter.h> * * PluginInputDomainAdapter is a Vamp plugin adapter that converts * time-domain input into frequency-domain input for plugins that need @@ -79,7 +82,12 @@ namespace HostExt { class PluginInputDomainAdapter : public PluginWrapper { public: - PluginInputDomainAdapter(Plugin *plugin); // I take ownership of plugin + /** + * Construct a PluginInputDomainAdapter wrapping the given plugin. + * The adapter takes ownership of the plugin, which will be + * deleted when the adapter is deleted. + */ + PluginInputDomainAdapter(Plugin *plugin); virtual ~PluginInputDomainAdapter(); bool initialise(size_t channels, size_t stepSize, size_t blockSize); @@ -91,6 +99,29 @@ public: FeatureSet process(const float *const *inputBuffers, RealTime timestamp); + /** + * Return the amount by which the timestamps supplied to process() + * are being incremented when they are passed to the plugin's own + * process() implementation. + * + * The Vamp API mandates that the timestamp passed to the plugin + * for time-domain input should be the time of the first sample in + * the block, but the timestamp passed for frequency-domain input + * should be the timestamp of the centre of the block. + * + * The PluginInputDomainAdapter adjusts its timestamps properly so + * that the plugin receives correct times, but in some + * circumstances (such as for establishing the correct timing of + * implicitly-timed features, i.e. features without their own + * timestamps) the host may need to be aware that this adjustment + * is taking place. + * + * If the plugin requires time-domain input, this function will + * return zero. The result of calling this function before + * initialise() has been called is undefined. + */ + RealTime getTimestampAdjustment() const; + protected: class Impl; Impl *m_impl; @@ -100,4 +131,6 @@ protected: } +_VAMP_SDK_HOSTSPACE_END(PluginInputDomainAdapter.h) + #endif diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.h b/libs/vamp-sdk/vamp-hostsdk/PluginLoader.h index f48143c11e..4d1daffd25 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginLoader.h +++ b/libs/vamp-sdk/vamp-hostsdk/PluginLoader.h @@ -41,8 +41,11 @@ #include <string> #include <map> +#include "hostguard.h" #include "PluginWrapper.h" +_VAMP_SDK_HOSTSPACE_BEGIN(PluginLoader.h) + namespace Vamp { class Plugin; @@ -50,7 +53,7 @@ class Plugin; namespace HostExt { /** - * \class PluginLoader PluginLoader.h <vamp-sdk/hostext/PluginLoader.h> + * \class PluginLoader PluginLoader.h <vamp-hostsdk/PluginLoader.h> * * Vamp::HostExt::PluginLoader is a convenience class for discovering * and loading Vamp plugins using the typical plugin-path, library @@ -234,5 +237,7 @@ protected: } +_VAMP_SDK_HOSTSPACE_END(PluginLoader.h) + #endif diff --git a/libs/vamp-sdk/vamp-hostsdk/PluginSummarisingAdapter.h b/libs/vamp-sdk/vamp-hostsdk/PluginSummarisingAdapter.h new file mode 100644 index 0000000000..e9c14f5d2b --- /dev/null +++ b/libs/vamp-sdk/vamp-hostsdk/PluginSummarisingAdapter.h @@ -0,0 +1,197 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006-2008 Chris Cannam and QMUL. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_PLUGIN_SUMMARISING_ADAPTER_H_ +#define _VAMP_PLUGIN_SUMMARISING_ADAPTER_H_ + +#include "hostguard.h" +#include "PluginWrapper.h" + +#include <set> + +_VAMP_SDK_HOSTSPACE_BEGIN(PluginSummarisingAdapter.h) + +namespace Vamp { + +namespace HostExt { + +/** + * \class PluginSummarisingAdapter PluginSummarisingAdapter.h <vamp-hostsdk/PluginSummarisingAdapter.h> + * + * PluginSummarisingAdapter is a Vamp plugin adapter that provides + * summarisation methods such as mean and median averages of output + * features, for use in any context where an available plugin produces + * individual values but the result that is actually needed is some + * sort of aggregate. + * + * To make use of PluginSummarisingAdapter, the host should configure, + * initialise and run the plugin through the adapter interface just as + * normal. Then, after the process and getRemainingFeatures methods + * have been properly called and processing is complete, the host may + * call getSummaryForOutput or getSummaryForAllOutputs to obtain + * summarised features: averages, maximum values, etc, depending on + * the SummaryType passed to the function. + * + * By default PluginSummarisingAdapter calculates a single summary of + * each output's feature across the whole duration of processed audio. + * A host needing summaries of sub-segments of the whole audio may + * call setSummarySegmentBoundaries before retrieving the summaries, + * providing a list of times such that one summary will be provided + * for each segment between two consecutive times. + * + * PluginSummarisingAdapter is straightforward rather than fast. It + * calculates all of the summary types for all outputs always, and + * then returns only the ones that are requested. It is designed on + * the basis that, for most features, summarising and storing + * summarised results is far cheaper than calculating the results in + * the first place. If this is not true for your particular feature, + * PluginSummarisingAdapter may not be the best approach for you. + * + * \note This class was introduced in version 2.0 of the Vamp plugin SDK. + */ + +class PluginSummarisingAdapter : public PluginWrapper +{ +public: + /** + * Construct a PluginSummarisingAdapter wrapping the given plugin. + * The adapter takes ownership of the plugin, which will be + * deleted when the adapter is deleted. + */ + PluginSummarisingAdapter(Plugin *plugin); + virtual ~PluginSummarisingAdapter(); + + bool initialise(size_t channels, size_t stepSize, size_t blockSize); + + void reset(); + + FeatureSet process(const float *const *inputBuffers, RealTime timestamp); + FeatureSet getRemainingFeatures(); + + typedef std::set<RealTime> SegmentBoundaries; + + /** + * Specify a series of segment boundaries, such that one summary + * will be returned for each of the contiguous intra-boundary + * segments. This function must be called before + * getSummaryForOutput or getSummaryForAllOutputs. + * + * Note that you cannot retrieve results with multiple different + * segmentations by repeatedly calling this function followed by + * one of the getSummary functions. The summaries are all + * calculated at the first call to any getSummary function, and + * once the summaries have been calculated, they remain + * calculated. + */ + void setSummarySegmentBoundaries(const SegmentBoundaries &); + + enum SummaryType { + Minimum = 0, + Maximum = 1, + Mean = 2, + Median = 3, + Mode = 4, + Sum = 5, + Variance = 6, + StandardDeviation = 7, + Count = 8, + + UnknownSummaryType = 999 + }; + + /** + * AveragingMethod indicates how the adapter should handle + * average-based summaries of features whose results are not + * equally spaced in time. + * + * If SampleAverage is specified, summary types based on averages + * will be calculated by treating each result individually without + * regard to its time: for example, the mean will be the sum of + * all values divided by the number of values. + * + * If ContinuousTimeAverage is specified, each feature will be + * considered to have a duration, either as specified in the + * feature's duration field, or until the following feature: thus, + * for example, the mean will be the sum of the products of values + * and durations, divided by the total duration. + * + * Although SampleAverage is useful for many types of feature, + * ContinuousTimeAverage is essential for some situations, for + * example finding the result that spans the largest proportion of + * the input given a feature that emits a new result only when the + * value changes (the modal value integrated over time). + */ + enum AveragingMethod { + SampleAverage = 0, + ContinuousTimeAverage = 1, + }; + + /** + * Return summaries of the features that were returned on the + * given output, using the given SummaryType and AveragingMethod. + * + * The plugin must have been fully run (process() and + * getRemainingFeatures() calls all made as appropriate) before + * this function is called. + */ + FeatureList getSummaryForOutput(int output, + SummaryType type, + AveragingMethod method = SampleAverage); + + /** + * Return summaries of the features that were returned on all of + * the plugin's outputs, using the given SummaryType and + * AveragingMethod. + * + * The plugin must have been fully run (process() and + * getRemainingFeatures() calls all made as appropriate) before + * this function is called. + */ + FeatureSet getSummaryForAllOutputs(SummaryType type, + AveragingMethod method = SampleAverage); + +protected: + class Impl; + Impl *m_impl; +}; + +} + +} + +_VAMP_SDK_HOSTSPACE_END(PluginSummarisingAdapter.h) + +#endif diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginWrapper.h b/libs/vamp-sdk/vamp-hostsdk/PluginWrapper.h index d5ec1da0e8..357050d2be 100644 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginWrapper.h +++ b/libs/vamp-sdk/vamp-hostsdk/PluginWrapper.h @@ -37,14 +37,17 @@ #ifndef _VAMP_PLUGIN_WRAPPER_H_ #define _VAMP_PLUGIN_WRAPPER_H_ -#include <vamp-sdk/Plugin.h> +#include "hostguard.h" +#include <vamp-hostsdk/Plugin.h> + +_VAMP_SDK_HOSTSPACE_BEGIN(PluginWrapper.h) namespace Vamp { namespace HostExt { /** - * \class PluginWrapper PluginWrapper.h <vamp-sdk/hostext/PluginWrapper.h> + * \class PluginWrapper PluginWrapper.h <vamp-hostsdk/PluginWrapper.h> * * PluginWrapper is a simple base class for adapter plugins. It takes * a pointer to a "to be wrapped" Vamp plugin on construction, and @@ -94,6 +97,30 @@ public: FeatureSet getRemainingFeatures(); + /** + * Return a pointer to the plugin wrapper of type WrapperType + * surrounding this wrapper's plugin, if present. + * + * This is useful in situations where a plugin is wrapped by + * multiple different wrappers (one inside another) and the host + * wants to call some wrapper-specific function on one of the + * layers without having to care about the order in which they are + * wrapped. For example, the plugin returned by + * PluginLoader::loadPlugin may have more than one wrapper; if the + * host wanted to query or fine-tune some property of one of them, + * it would be hard to do so without knowing the order of the + * wrappers. This function therefore gives direct access to the + * wrapper of a particular type. + */ + template <typename WrapperType> + WrapperType *getWrapper() { + WrapperType *w = dynamic_cast<WrapperType *>(this); + if (w) return w; + PluginWrapper *pw = dynamic_cast<PluginWrapper *>(m_plugin); + if (pw) return pw->getWrapper<WrapperType>(); + return 0; + } + protected: PluginWrapper(Plugin *plugin); // I take ownership of plugin Plugin *m_plugin; @@ -103,4 +130,6 @@ protected: } +_VAMP_SDK_HOSTSPACE_END(PluginWrapper.h) + #endif diff --git a/libs/vamp-sdk/vamp-hostsdk/RealTime.h b/libs/vamp-sdk/vamp-hostsdk/RealTime.h new file mode 100644 index 0000000000..d789b6a1ac --- /dev/null +++ b/libs/vamp-sdk/vamp-hostsdk/RealTime.h @@ -0,0 +1,46 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_HOSTSDK_REALTIME_H_ +#define _VAMP_HOSTSDK_REALTIME_H_ + +// Do not include vamp-sdk/RealTime.h directly from host code. Always +// use this header instead. + +#include "hostguard.h" +#include <vamp-sdk/RealTime.h> + +#endif diff --git a/libs/vamp-sdk/vamp-hostsdk/hostguard.h b/libs/vamp-sdk/vamp-hostsdk/hostguard.h new file mode 100644 index 0000000000..3698d84cd7 --- /dev/null +++ b/libs/vamp-sdk/vamp-hostsdk/hostguard.h @@ -0,0 +1,69 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_HOSTSDK_HOSTGUARD_H_ +#define _VAMP_HOSTSDK_HOSTGUARD_H_ + +#ifdef _VAMP_IN_PLUGINSDK +#error You have included headers from both vamp-sdk and vamp-hostsdk in the same source file. Please include only vamp-sdk headers in plugin code, and only vamp-hostsdk headers in host code. +#else + +#define _VAMP_IN_HOSTSDK + +#ifdef _VAMP_NO_HOST_NAMESPACE +#define _VAMP_SDK_HOSTSPACE_BEGIN(h) +#define _VAMP_SDK_HOSTSPACE_END(h) +#define _VAMP_SDK_PLUGSPACE_BEGIN(h) +#define _VAMP_SDK_PLUGSPACE_END(h) +#else +#define _VAMP_SDK_HOSTSPACE_BEGIN(h) \ + namespace _VampHost { + +#define _VAMP_SDK_HOSTSPACE_END(h) \ + } \ + using namespace _VampHost; +#define _VAMP_SDK_PLUGSPACE_BEGIN(h) \ + namespace _VampHost { + +#define _VAMP_SDK_PLUGSPACE_END(h) \ + } \ + using namespace _VampHost; +#endif + +#endif + +#endif + diff --git a/libs/vamp-sdk/vamp-hostsdk/vamp-hostsdk.h b/libs/vamp-sdk/vamp-hostsdk/vamp-hostsdk.h new file mode 100644 index 0000000000..f32583d549 --- /dev/null +++ b/libs/vamp-sdk/vamp-hostsdk/vamp-hostsdk.h @@ -0,0 +1,53 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_HOSTSDK_SINGLE_INCLUDE_H_ +#define _VAMP_HOSTSDK_SINGLE_INCLUDE_H_ + +#include "PluginBase.h" +#include "PluginBufferingAdapter.h" +#include "PluginChannelAdapter.h" +#include "Plugin.h" +#include "PluginHostAdapter.h" +#include "PluginInputDomainAdapter.h" +#include "PluginLoader.h" +#include "PluginSummarisingAdapter.h" +#include "PluginWrapper.h" +#include "RealTime.h" + +#endif + + diff --git a/libs/vamp-sdk/vamp-sdk/Plugin.h b/libs/vamp-sdk/vamp-sdk/Plugin.h index 159bf4fac6..4709b19b67 100644 --- a/libs/vamp-sdk/vamp-sdk/Plugin.h +++ b/libs/vamp-sdk/vamp-sdk/Plugin.h @@ -34,16 +34,19 @@ authorization. */ -#ifndef _VAMP_PLUGIN_H_ -#define _VAMP_PLUGIN_H_ - -#include "PluginBase.h" -#include "RealTime.h" +#ifndef _VAMP_SDK_PLUGIN_H_ +#define _VAMP_SDK_PLUGIN_H_ #include <string> #include <vector> #include <map> +#include "PluginBase.h" +#include "RealTime.h" + +#include "plugguard.h" +_VAMP_SDK_PLUGSPACE_BEGIN(Plugin.h) + namespace Vamp { /** @@ -200,7 +203,7 @@ public: /** * The name of the output, in computer-usable form. Should be * reasonably short and without whitespace or punctuation, using - * the characters [a-zA-Z0-9_] only. + * the characters [a-zA-Z0-9_-] only. * Example: "zero_crossing_count" */ std::string identifier; @@ -304,6 +307,16 @@ public: * this to zero if that behaviour is not desired. */ float sampleRate; + + /** + * True if the returned results for this output are known to + * have a duration field. + */ + bool hasDuration; + + OutputDescriptor() : // defaults for mandatory non-class-type members + hasFixedBinCount(false), hasKnownExtents(false), isQuantized(false), + sampleType(OneSamplePerStep), hasDuration(false) { } }; typedef std::vector<OutputDescriptor> OutputList; @@ -319,17 +332,34 @@ public: { /** * True if an output feature has its own timestamp. This is - * mandatory if the output has VariableSampleRate, and is - * likely to be disregarded otherwise. + * mandatory if the output has VariableSampleRate, optional if + * the output has FixedSampleRate, and unused if the output + * has OneSamplePerStep. */ bool hasTimestamp; /** * Timestamp of the output feature. This is mandatory if the - * output has VariableSampleRate, and is likely to be - * disregarded otherwise. Undefined if hasTimestamp is false. + * output has VariableSampleRate or if the output has + * FixedSampleRate and hasTimestamp is true, and unused + * otherwise. */ RealTime timestamp; + + /** + * True if an output feature has a specified duration. This + * is optional if the output has VariableSampleRate or + * FixedSampleRate, and and unused if the output has + * OneSamplePerStep. + */ + bool hasDuration; + + /** + * Duration of the output feature. This is mandatory if the + * output has VariableSampleRate or FixedSampleRate and + * hasDuration is true, and unused otherwise. + */ + RealTime duration; /** * Results for a single sample of this feature. If the output @@ -342,9 +372,13 @@ public: * Label for the sample of this feature. */ std::string label; + + Feature() : // defaults for mandatory non-class-type members + hasTimestamp(false), hasDuration(false) { } }; typedef std::vector<Feature> FeatureList; + typedef std::map<int, FeatureList> FeatureSet; // key is output no /** @@ -353,9 +387,9 @@ public: * If the plugin's inputDomain is TimeDomain, inputBuffers will * point to one array of floats per input channel, and each of * these arrays will contain blockSize consecutive audio samples - * (the host will zero-pad as necessary). The timestamp will be - * the real time in seconds of the start of the supplied block of - * samples. + * (the host will zero-pad as necessary). The timestamp in this + * case will be the real time in seconds of the start of the + * supplied block of samples. * * If the plugin's inputDomain is FrequencyDomain, inputBuffers * will point to one array of floats per input channel, and each @@ -399,6 +433,8 @@ protected: } +_VAMP_SDK_PLUGSPACE_END(Plugin.h) + #endif diff --git a/libs/vamp-sdk/vamp-sdk/PluginAdapter.h b/libs/vamp-sdk/vamp-sdk/PluginAdapter.h index bfc97508eb..dafd532a3b 100644 --- a/libs/vamp-sdk/vamp-sdk/PluginAdapter.h +++ b/libs/vamp-sdk/vamp-sdk/PluginAdapter.h @@ -37,11 +37,13 @@ #ifndef _VAMP_PLUGIN_ADAPTER_H_ #define _VAMP_PLUGIN_ADAPTER_H_ +#include <map> #include <vamp/vamp.h> #include "Plugin.h" -#include <map> +#include "plugguard.h" +_VAMP_SDK_PLUGSPACE_BEGIN(PluginAdapter.h) namespace Vamp { @@ -113,5 +115,7 @@ protected: } +_VAMP_SDK_PLUGSPACE_END(PluginAdapter.h) + #endif diff --git a/libs/vamp-sdk/vamp-sdk/PluginBase.h b/libs/vamp-sdk/vamp-sdk/PluginBase.h index 38d2b49904..147ffdc153 100644 --- a/libs/vamp-sdk/vamp-sdk/PluginBase.h +++ b/libs/vamp-sdk/vamp-sdk/PluginBase.h @@ -34,13 +34,18 @@ authorization. */ -#ifndef _VAMP_PLUGIN_BASE_H_ -#define _VAMP_PLUGIN_BASE_H_ +#ifndef _VAMP_SDK_PLUGIN_BASE_H_ +#define _VAMP_SDK_PLUGIN_BASE_H_ #include <string> #include <vector> -#define VAMP_SDK_VERSION "1.1" +#define VAMP_SDK_VERSION "2.0" +#define VAMP_SDK_MAJOR_VERSION 2 +#define VAMP_SDK_MINOR_VERSION 0 + +#include "plugguard.h" +_VAMP_SDK_PLUGSPACE_BEGIN(PluginBase.h) namespace Vamp { @@ -64,12 +69,12 @@ public: /** * Get the Vamp API compatibility level of the plugin. */ - virtual unsigned int getVampApiVersion() const { return 1; } + virtual unsigned int getVampApiVersion() const { return 2; } /** * Get the computer-usable name of the plugin. This should be * reasonably short and contain no whitespace or punctuation - * characters. It may only contain the characters [a-zA-Z0-9_]. + * characters. It may only contain the characters [a-zA-Z0-9_-]. * This is the authoritative way for a program to identify a * plugin within a given library. * @@ -127,7 +132,7 @@ public: /** * The name of the parameter, in computer-usable form. Should * be reasonably short, and may only contain the characters - * [a-zA-Z0-9_]. + * [a-zA-Z0-9_-]. */ std::string identifier; @@ -190,6 +195,9 @@ public: * encoded in the names. */ std::vector<std::string> valueNames; + + ParameterDescriptor() : // the defaults are invalid: you must set them + minValue(0), maxValue(0), defaultValue(0), isQuantized(false) { } }; typedef std::vector<ParameterDescriptor> ParameterList; @@ -249,4 +257,6 @@ public: } +_VAMP_SDK_PLUGSPACE_END(PluginBase.h) + #endif diff --git a/libs/vamp-sdk/vamp-sdk/RealTime.h b/libs/vamp-sdk/vamp-sdk/RealTime.h index 6b88ed53f0..297c4ad524 100644 --- a/libs/vamp-sdk/vamp-sdk/RealTime.h +++ b/libs/vamp-sdk/vamp-sdk/RealTime.h @@ -51,6 +51,9 @@ struct timeval; #endif +#include "plugguard.h" +_VAMP_SDK_PLUGSPACE_BEGIN(RealTime.h) + namespace Vamp { /** @@ -158,5 +161,7 @@ struct RealTime std::ostream &operator<<(std::ostream &out, const RealTime &rt); } + +_VAMP_SDK_PLUGSPACE_END(RealTime.h) #endif diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h deleted file mode 100644 index 24ca6f8db8..0000000000 --- a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Vamp - - An API for audio analysis and feature extraction plugins. - - Centre for Digital Music, Queen Mary, University of London. - Copyright 2006-2007 Chris Cannam and QMUL. - This file by Mark Levy, Copyright 2007 QMUL. - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, copy, - modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR - ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the names of the Centre for - Digital Music; Queen Mary, University of London; and Chris Cannam - shall not be used in advertising or otherwise to promote the sale, - use or other dealings in this Software without prior written - authorization. -*/ - -#ifndef _VAMP_PLUGIN_BUFFERING_ADAPTER_H_ -#define _VAMP_PLUGIN_BUFFERING_ADAPTER_H_ - -#include "PluginWrapper.h" - -namespace Vamp { - -namespace HostExt { - -/** - * \class PluginBufferingAdapter PluginBufferingAdapter.h <vamp-sdk/hostext/PluginBufferingAdapter.h> - * - * PluginBufferingAdapter is a Vamp plugin adapter that allows plugins - * to be used by a host supplying an audio stream in non-overlapping - * buffers of arbitrary size. - * - * A host using PluginBufferingAdapter may ignore the preferred step - * and block size reported by the plugin, and still expect the plugin - * to run. The value of blockSize and stepSize passed to initialise - * should be the size of the buffer which the host will supply; the - * stepSize should be equal to the blockSize. - * - * If the internal step size used for the plugin differs from that - * supplied by the host, the adapter will modify the sample rate - * specifications for the plugin outputs (setting them all to - * VariableSampleRate) and set timestamps on the output features for - * outputs that formerly used a different sample rate specification. - * This is necessary in order to obtain correct time stamping. - * - * In other respects, the PluginBufferingAdapter behaves identically - * to the plugin that it wraps. The wrapped plugin will be deleted - * when the wrapper is deleted. - */ - -class PluginBufferingAdapter : public PluginWrapper -{ -public: - PluginBufferingAdapter(Plugin *plugin); // I take ownership of plugin - virtual ~PluginBufferingAdapter(); - - bool initialise(size_t channels, size_t stepSize, size_t blockSize); - - size_t getPreferredStepSize() const; - - OutputList getOutputDescriptors() const; - - void reset(); - - FeatureSet process(const float *const *inputBuffers, RealTime timestamp); - - FeatureSet getRemainingFeatures(); - -protected: - class Impl; - Impl *m_impl; -}; - -} - -} - -#endif diff --git a/libs/vamp-sdk/vamp-sdk/plugguard.h b/libs/vamp-sdk/vamp-sdk/plugguard.h new file mode 100644 index 0000000000..311dd488e0 --- /dev/null +++ b/libs/vamp-sdk/vamp-sdk/plugguard.h @@ -0,0 +1,98 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_SDK_PLUGGUARD_H_ +#define _VAMP_SDK_PLUGGUARD_H_ + +/** + * Normal usage should be: + * + * - Plugins include vamp-sdk/Plugin.h or vamp-sdk/PluginBase.h. + * These files include this header, which specifies an appropriate + * namespace for the plugin classes to avoid any risk of conflict + * with non-plugin class implementations in the host on load. + * + * - Hosts include vamp-hostsdk/Plugin.h, vamp-hostsdk/PluginBase.h, + * vamp-hostsdk/PluginHostAdapter, vamp-hostsdk/PluginLoader.h etc. + * These files include vamp-hostsdk/hostguard.h, which makes a note + * that we are in a host. A file such as vamp-hostsdk/Plugin.h + * then simply includes vamp-sdk/Plugin.h, and this guard header + * takes notice of the fact that it has been included from a host + * and leaves the plugin namespace unset. + * + * Problems will occur when a host includes files directly from the + * vamp-sdk directory. There are two reasons this might happen: + * mistake, perhaps owing to ignorance of the fact that this isn't + * allowed (particularly since it was the normal mechanism in v1 of + * the SDK); and a wish to incorporate plugin code directly into the + * host rather than having to load it. + * + * What if the host does include a vamp-sdk header by mistake? We can + * catch it if it's included before something from vamp-hostsdk. If + * it's included after something from vamp-hostsdk, it will work OK + * anyway. The remaining problem case is where nothing from + * vamp-hostsdk is included in the same file. We can't catch that. + */ + +#ifndef _VAMP_IN_HOSTSDK + +#define _VAMP_IN_PLUGINSDK 1 + +#ifdef _VAMP_NO_PLUGIN_NAMESPACE +#define _VAMP_SDK_PLUGSPACE_BEGIN(h) +#define _VAMP_SDK_PLUGSPACE_END(h) +#else +#ifdef _VAMP_PLUGIN_IN_HOST_NAMESPACE +#define _VAMP_SDK_PLUGSPACE_BEGIN(h) \ + namespace _VampHost { + +#define _VAMP_SDK_PLUGSPACE_END(h) \ + } \ + using namespace _VampHost; +#else +#define _VAMP_SDK_PLUGSPACE_BEGIN(h) \ + namespace _VampPlugin { + +#define _VAMP_SDK_PLUGSPACE_END(h) \ + } \ + using namespace _VampPlugin; +#endif +#endif + +#endif + +#endif + diff --git a/libs/vamp-sdk/vamp-sdk/vamp-sdk.h b/libs/vamp-sdk/vamp-sdk/vamp-sdk.h new file mode 100644 index 0000000000..3ac2f8bc05 --- /dev/null +++ b/libs/vamp-sdk/vamp-sdk/vamp-sdk.h @@ -0,0 +1,46 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _VAMP_SDK_SINGLE_INCLUDE_H_ +#define _VAMP_SDK_SINGLE_INCLUDE_H_ + +#include "PluginBase.h" +#include "Plugin.h" +#include "RealTime.h" + +#endif + + diff --git a/libs/vamp-sdk/vamp/vamp.h b/libs/vamp-sdk/vamp/vamp.h index 4f0145ab59..08a83ee6ac 100644 --- a/libs/vamp-sdk/vamp/vamp.h +++ b/libs/vamp-sdk/vamp/vamp.h @@ -50,7 +50,7 @@ extern "C" { * See also the vampApiVersion field in the plugin descriptor, and the * hostApiVersion argument to the vampGetPluginDescriptor function. */ -#define VAMP_API_VERSION 1 +#define VAMP_API_VERSION 2 /** * C language API for Vamp plugins. @@ -160,6 +160,15 @@ typedef struct _VampOutputDescriptor "Resolution" of result, if sampleType is vampVariableSampleRate. */ float sampleRate; + /** 1 if the returned results for this output are known to have a + duration field. + + This field is new in Vamp API version 2; it must not be tested + for plugins that report an older API version in their plugin + descriptor. + */ + int hasDuration; + } VampOutputDescriptor; typedef struct _VampFeature @@ -184,13 +193,46 @@ typedef struct _VampFeature } VampFeature; +typedef struct _VampFeatureV2 +{ + /** 1 if the feature has a duration. */ + int hasDuration; + + /** Seconds component of duratiion. */ + int durationSec; + + /** Nanoseconds component of duration. */ + int durationNsec; + +} VampFeatureV2; + +typedef union _VampFeatureUnion +{ + // sizeof(featureV1) >= sizeof(featureV2) for backward compatibility + VampFeature v1; + VampFeatureV2 v2; + +} VampFeatureUnion; + typedef struct _VampFeatureList { /** Number of features in this feature list. */ unsigned int featureCount; - /** Features in this feature list. May be NULL if featureCount is zero. */ - VampFeature *features; + /** Features in this feature list. May be NULL if featureCount is + zero. + + If present, this array must contain featureCount feature + structures for a Vamp API version 1 plugin, or 2*featureCount + feature unions for a Vamp API version 2 plugin. + + The features returned by an API version 2 plugin must consist + of the same feature structures as in API version 1 for the + first featureCount array elements, followed by featureCount + unions that contain VampFeatureV2 structures (or NULL pointers + if no V2 feature structures are present). + */ + VampFeatureUnion *features; } VampFeatureList; @@ -289,7 +331,7 @@ typedef struct _VampPluginDescriptor handle, or releaseOutputDescriptor for this descriptor. Host must call releaseOutputDescriptor after use. */ VampOutputDescriptor *(*getOutputDescriptor)(VampPluginHandle, - unsigned int); + unsigned int); /** Destroy a descriptor for a feature output. */ void (*releaseOutputDescriptor)(VampOutputDescriptor *); @@ -312,6 +354,7 @@ typedef struct _VampPluginDescriptor } VampPluginDescriptor; + /** Get the descriptor for a given plugin index in this library. Return NULL if the index is outside the range of valid indices for this plugin library. @@ -324,10 +367,16 @@ typedef struct _VampPluginDescriptor field for its actual compatibility level, the host should be able to do the right thing with it: use it if possible, discard it otherwise. + + This is the only symbol that a Vamp plugin actually needs to + export from its shared object; all others can be hidden. See the + accompanying documentation for notes on how to achieve this with + certain compilers. */ const VampPluginDescriptor *vampGetPluginDescriptor (unsigned int hostApiVersion, unsigned int index); + /** Function pointer type for vampGetPluginDescriptor. */ typedef const VampPluginDescriptor *(*VampGetPluginDescriptorFunction) (unsigned int, unsigned int); |