cannam@92: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ cannam@92: cannam@92: /* cannam@92: Vamp cannam@92: cannam@92: An API for audio analysis and feature extraction plugins. cannam@92: cannam@92: Centre for Digital Music, Queen Mary, University of London. cannam@92: Copyright 2006-2007 Chris Cannam and QMUL. cannam@92: This file by Mark Levy, Copyright 2007 QMUL. cannam@92: cannam@92: Permission is hereby granted, free of charge, to any person cannam@92: obtaining a copy of this software and associated documentation cannam@92: files (the "Software"), to deal in the Software without cannam@92: restriction, including without limitation the rights to use, copy, cannam@92: modify, merge, publish, distribute, sublicense, and/or sell copies cannam@92: of the Software, and to permit persons to whom the Software is cannam@92: furnished to do so, subject to the following conditions: cannam@92: cannam@92: The above copyright notice and this permission notice shall be cannam@92: included in all copies or substantial portions of the Software. cannam@92: cannam@92: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, cannam@92: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF cannam@92: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND cannam@92: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR cannam@92: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF cannam@92: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION cannam@92: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cannam@92: cannam@92: Except as contained in this notice, the names of the Centre for cannam@92: Digital Music; Queen Mary, University of London; and Chris Cannam cannam@92: shall not be used in advertising or otherwise to promote the sale, cannam@92: use or other dealings in this Software without prior written cannam@92: authorization. cannam@92: */ cannam@92: cannam@92: #include cannam@92: #include cannam@92: cannam@92: #include "PluginBufferingAdapter.h" cannam@92: cannam@92: using std::vector; cannam@92: using std::map; cannam@92: cannam@92: namespace Vamp { cannam@92: cannam@92: namespace HostExt { cannam@92: cannam@92: class PluginBufferingAdapter::Impl cannam@92: { cannam@92: public: cannam@92: Impl(Plugin *plugin, float inputSampleRate); cannam@92: ~Impl(); cannam@92: cannam@92: bool initialise(size_t channels, size_t stepSize, size_t blockSize); cannam@92: cannam@92: OutputList getOutputDescriptors() const; cannam@92: cannam@92: FeatureSet process(const float *const *inputBuffers, RealTime timestamp); cannam@92: cannam@92: FeatureSet getRemainingFeatures(); cannam@92: cannam@92: protected: cannam@92: Plugin *m_plugin; cannam@92: size_t m_inputStepSize; cannam@92: size_t m_inputBlockSize; cannam@92: size_t m_stepSize; cannam@92: size_t m_blockSize; cannam@92: size_t m_channels; cannam@92: vector > m_queue; cannam@92: float **m_buffers; // in fact an array of pointers into the queue cannam@92: size_t m_inputPos; // start position in the queue of next input block cannam@92: float m_inputSampleRate; cannam@92: RealTime m_timestamp; cannam@92: OutputList m_outputs; cannam@92: cannam@92: void processBlock(FeatureSet& allFeatureSets, RealTime timestamp); cannam@92: }; cannam@92: cannam@92: PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) : cannam@92: PluginWrapper(plugin) cannam@92: { cannam@92: m_impl = new Impl(plugin, m_inputSampleRate); cannam@92: } cannam@92: cannam@92: PluginBufferingAdapter::~PluginBufferingAdapter() cannam@92: { cannam@92: delete m_impl; cannam@92: } cannam@92: cannam@92: bool cannam@92: PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize) cannam@92: { cannam@92: return m_impl->initialise(channels, stepSize, blockSize); cannam@92: } cannam@92: cannam@92: PluginBufferingAdapter::OutputList cannam@92: PluginBufferingAdapter::getOutputDescriptors() const cannam@92: { cannam@92: return m_impl->getOutputDescriptors(); cannam@92: } cannam@92: cannam@92: PluginBufferingAdapter::FeatureSet cannam@92: PluginBufferingAdapter::process(const float *const *inputBuffers, cannam@92: RealTime timestamp) cannam@92: { cannam@92: return m_impl->process(inputBuffers, timestamp); cannam@92: } cannam@92: cannam@92: PluginBufferingAdapter::FeatureSet cannam@92: PluginBufferingAdapter::getRemainingFeatures() cannam@92: { cannam@92: return m_impl->getRemainingFeatures(); cannam@92: } cannam@92: cannam@92: PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : cannam@92: m_plugin(plugin), cannam@92: m_inputStepSize(0), cannam@92: m_inputBlockSize(0), cannam@92: m_stepSize(0), cannam@92: m_blockSize(0), cannam@92: m_channels(0), cannam@92: m_buffers(0), cannam@92: m_inputPos(0), cannam@92: m_inputSampleRate(inputSampleRate), cannam@92: m_timestamp() cannam@92: { cannam@92: m_outputs = plugin->getOutputDescriptors(); cannam@92: } cannam@92: cannam@92: PluginBufferingAdapter::Impl::~Impl() cannam@92: { cannam@92: // the adapter will delete the plugin cannam@92: cannam@92: delete [] m_buffers; cannam@92: } cannam@92: cannam@92: size_t cannam@92: PluginBufferingAdapter::getPreferredStepSize() const cannam@92: { cannam@92: return getPreferredBlockSize(); cannam@92: } cannam@92: cannam@92: bool cannam@92: PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize) cannam@92: { cannam@92: if (stepSize != blockSize) { cannam@92: std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl; cannam@92: return false; cannam@92: } cannam@92: cannam@92: m_channels = channels; cannam@92: m_inputStepSize = stepSize; cannam@92: m_inputBlockSize = blockSize; cannam@92: cannam@92: // use the step and block sizes which the plugin prefers cannam@92: m_stepSize = m_plugin->getPreferredStepSize(); cannam@92: m_blockSize = m_plugin->getPreferredBlockSize(); cannam@92: cannam@92: // or sensible defaults if it has no preference cannam@92: if (m_blockSize == 0) { cannam@92: m_blockSize = 1024; cannam@92: } cannam@92: if (m_stepSize == 0) { cannam@92: if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) { cannam@92: m_stepSize = m_blockSize/2; cannam@92: } else { cannam@92: m_stepSize = m_blockSize; cannam@92: } cannam@92: } else if (m_stepSize > m_blockSize) { cannam@92: if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) { cannam@92: m_blockSize = m_stepSize * 2; cannam@92: } else { cannam@92: m_blockSize = m_stepSize; cannam@92: } cannam@92: } cannam@92: cannam@92: std::cerr << "PluginBufferingAdapter::initialise: stepSize " << m_inputStepSize << " -> " << m_stepSize cannam@92: << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl; cannam@92: cannam@92: // current implementation breaks if step is greater than block cannam@92: if (m_stepSize > m_blockSize) { cannam@92: std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl; cannam@92: return false; cannam@92: } cannam@92: cannam@92: m_queue.resize(m_channels); cannam@92: m_buffers = new float*[m_channels]; cannam@92: cannam@92: return m_plugin->initialise(m_channels, m_stepSize, m_blockSize); cannam@92: } cannam@92: cannam@92: PluginBufferingAdapter::OutputList cannam@92: PluginBufferingAdapter::Impl::getOutputDescriptors() const cannam@92: { cannam@92: OutputList outs = m_plugin->getOutputDescriptors(); cannam@92: for (size_t i = 0; i < outs.size(); ++i) { cannam@92: if (outs[i].sampleType == OutputDescriptor::OneSamplePerStep) { cannam@92: outs[i].sampleRate = 1.f / m_stepSize; cannam@92: } cannam@92: outs[i].sampleType = OutputDescriptor::VariableSampleRate; cannam@92: } cannam@92: return outs; cannam@92: } cannam@92: cannam@92: PluginBufferingAdapter::FeatureSet cannam@92: PluginBufferingAdapter::Impl::process(const float *const *inputBuffers, cannam@92: RealTime timestamp) cannam@92: { cannam@92: FeatureSet allFeatureSets; cannam@92: cannam@92: // queue the new input cannam@92: cannam@92: //std::cerr << "unread " << m_queue[0].size() - m_inputPos << " samples" << std::endl; cannam@92: //std::cerr << "queueing " << m_inputBlockSize - (m_queue[0].size() - m_inputPos) << " samples" << std::endl; cannam@92: cannam@92: for (size_t i = 0; i < m_channels; ++i) cannam@92: for (size_t j = m_queue[0].size() - m_inputPos; j < m_inputBlockSize; ++j) cannam@92: m_queue[i].push_back(inputBuffers[i][j]); cannam@92: cannam@92: m_inputPos += m_inputStepSize; cannam@92: cannam@92: // process as much as we can cannam@92: while (m_queue[0].size() >= m_blockSize) cannam@92: { cannam@92: processBlock(allFeatureSets, timestamp); cannam@92: m_inputPos -= m_stepSize; cannam@92: cannam@92: //std::cerr << m_queue[0].size() << " samples still left in queue" << std::endl; cannam@92: //std::cerr << "inputPos = " << m_inputPos << std::endl; cannam@92: } cannam@92: cannam@92: return allFeatureSets; cannam@92: } cannam@92: cannam@92: PluginBufferingAdapter::FeatureSet cannam@92: PluginBufferingAdapter::Impl::getRemainingFeatures() cannam@92: { cannam@92: FeatureSet allFeatureSets; cannam@92: cannam@92: // process remaining samples in queue cannam@92: while (m_queue[0].size() >= m_blockSize) cannam@92: { cannam@92: processBlock(allFeatureSets, m_timestamp); cannam@92: } cannam@92: cannam@92: // pad any last samples remaining and process cannam@92: if (m_queue[0].size() > 0) cannam@92: { cannam@92: for (size_t i = 0; i < m_channels; ++i) cannam@92: while (m_queue[i].size() < m_blockSize) cannam@92: m_queue[i].push_back(0.0); cannam@92: processBlock(allFeatureSets, m_timestamp); cannam@92: } cannam@92: cannam@92: // get remaining features cannam@92: FeatureSet featureSet = m_plugin->getRemainingFeatures(); cannam@92: for (map::iterator iter = featureSet.begin(); cannam@92: iter != featureSet.end(); ++iter) cannam@92: { cannam@92: FeatureList featureList = iter->second; cannam@92: for (size_t i = 0; i < featureList.size(); ++i) cannam@92: allFeatureSets[iter->first].push_back(featureList[i]); cannam@92: } cannam@92: cannam@92: return allFeatureSets; cannam@92: } cannam@92: cannam@92: void cannam@92: PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets, RealTime timestamp) cannam@92: { cannam@92: //std::cerr << m_queue[0].size() << " samples left in queue" << std::endl; cannam@92: cannam@92: // point the buffers to the head of the queue cannam@92: for (size_t i = 0; i < m_channels; ++i) cannam@92: m_buffers[i] = &m_queue[i][0]; cannam@92: cannam@92: FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp); cannam@92: cannam@92: for (map::iterator iter = featureSet.begin(); cannam@92: iter != featureSet.end(); ++iter) cannam@92: { cannam@92: cannam@92: FeatureList featureList = iter->second; cannam@92: int outputNo = iter->first; cannam@92: cannam@92: for (size_t i = 0; i < featureList.size(); ++i) cannam@92: { cannam@92: cannam@92: // make sure the timestamp is set cannam@92: switch (m_outputs[outputNo].sampleType) cannam@92: { cannam@92: case OutputDescriptor::OneSamplePerStep: cannam@92: // use our internal timestamp - OK???? cannam@92: featureList[i].timestamp = m_timestamp; cannam@92: break; cannam@92: case OutputDescriptor::FixedSampleRate: cannam@92: // use our internal timestamp cannam@92: featureList[i].timestamp = m_timestamp; cannam@92: break; cannam@92: case OutputDescriptor::VariableSampleRate: cannam@92: break; // plugin must set timestamp cannam@92: default: cannam@92: break; cannam@92: } cannam@92: cannam@92: allFeatureSets[outputNo].push_back(featureList[i]); cannam@92: } cannam@92: } cannam@92: cannam@92: // step forward cannam@92: for (size_t i = 0; i < m_channels; ++i) cannam@92: m_queue[i].erase(m_queue[i].begin(), m_queue[i].begin() + m_stepSize); cannam@92: cannam@92: // fake up the timestamp each time we step forward cannam@92: //std::cerr << m_timestamp; cannam@92: long frame = RealTime::realTime2Frame(m_timestamp, int(m_inputSampleRate + 0.5)); cannam@92: m_timestamp = RealTime::frame2RealTime(frame + m_stepSize, int(m_inputSampleRate + 0.5)); cannam@92: //std::cerr << "--->" << m_timestamp << std::endl; cannam@92: } cannam@92: cannam@92: } cannam@92: cannam@92: } cannam@92: cannam@92: