annotate vamp-sdk/hostext/PluginBufferingAdapter.cpp @ 92:c94c066a4897

* Add Mark L's PluginBufferingAdapter
author cannam
date Fri, 02 Nov 2007 14:54:04 +0000
parents
children ca40f3bc99f0
rev   line source
cannam@92 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@92 2
cannam@92 3 /*
cannam@92 4 Vamp
cannam@92 5
cannam@92 6 An API for audio analysis and feature extraction plugins.
cannam@92 7
cannam@92 8 Centre for Digital Music, Queen Mary, University of London.
cannam@92 9 Copyright 2006-2007 Chris Cannam and QMUL.
cannam@92 10 This file by Mark Levy, Copyright 2007 QMUL.
cannam@92 11
cannam@92 12 Permission is hereby granted, free of charge, to any person
cannam@92 13 obtaining a copy of this software and associated documentation
cannam@92 14 files (the "Software"), to deal in the Software without
cannam@92 15 restriction, including without limitation the rights to use, copy,
cannam@92 16 modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@92 17 of the Software, and to permit persons to whom the Software is
cannam@92 18 furnished to do so, subject to the following conditions:
cannam@92 19
cannam@92 20 The above copyright notice and this permission notice shall be
cannam@92 21 included in all copies or substantial portions of the Software.
cannam@92 22
cannam@92 23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@92 24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@92 25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@92 26 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@92 27 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@92 28 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@92 29 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@92 30
cannam@92 31 Except as contained in this notice, the names of the Centre for
cannam@92 32 Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@92 33 shall not be used in advertising or otherwise to promote the sale,
cannam@92 34 use or other dealings in this Software without prior written
cannam@92 35 authorization.
cannam@92 36 */
cannam@92 37
cannam@92 38 #include <vector>
cannam@92 39 #include <map>
cannam@92 40
cannam@92 41 #include "PluginBufferingAdapter.h"
cannam@92 42
cannam@92 43 using std::vector;
cannam@92 44 using std::map;
cannam@92 45
cannam@92 46 namespace Vamp {
cannam@92 47
cannam@92 48 namespace HostExt {
cannam@92 49
cannam@92 50 class PluginBufferingAdapter::Impl
cannam@92 51 {
cannam@92 52 public:
cannam@92 53 Impl(Plugin *plugin, float inputSampleRate);
cannam@92 54 ~Impl();
cannam@92 55
cannam@92 56 bool initialise(size_t channels, size_t stepSize, size_t blockSize);
cannam@92 57
cannam@92 58 OutputList getOutputDescriptors() const;
cannam@92 59
cannam@92 60 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
cannam@92 61
cannam@92 62 FeatureSet getRemainingFeatures();
cannam@92 63
cannam@92 64 protected:
cannam@92 65 Plugin *m_plugin;
cannam@92 66 size_t m_inputStepSize;
cannam@92 67 size_t m_inputBlockSize;
cannam@92 68 size_t m_stepSize;
cannam@92 69 size_t m_blockSize;
cannam@92 70 size_t m_channels;
cannam@92 71 vector<vector<float> > m_queue;
cannam@92 72 float **m_buffers; // in fact an array of pointers into the queue
cannam@92 73 size_t m_inputPos; // start position in the queue of next input block
cannam@92 74 float m_inputSampleRate;
cannam@92 75 RealTime m_timestamp;
cannam@92 76 OutputList m_outputs;
cannam@92 77
cannam@92 78 void processBlock(FeatureSet& allFeatureSets, RealTime timestamp);
cannam@92 79 };
cannam@92 80
cannam@92 81 PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) :
cannam@92 82 PluginWrapper(plugin)
cannam@92 83 {
cannam@92 84 m_impl = new Impl(plugin, m_inputSampleRate);
cannam@92 85 }
cannam@92 86
cannam@92 87 PluginBufferingAdapter::~PluginBufferingAdapter()
cannam@92 88 {
cannam@92 89 delete m_impl;
cannam@92 90 }
cannam@92 91
cannam@92 92 bool
cannam@92 93 PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@92 94 {
cannam@92 95 return m_impl->initialise(channels, stepSize, blockSize);
cannam@92 96 }
cannam@92 97
cannam@92 98 PluginBufferingAdapter::OutputList
cannam@92 99 PluginBufferingAdapter::getOutputDescriptors() const
cannam@92 100 {
cannam@92 101 return m_impl->getOutputDescriptors();
cannam@92 102 }
cannam@92 103
cannam@92 104 PluginBufferingAdapter::FeatureSet
cannam@92 105 PluginBufferingAdapter::process(const float *const *inputBuffers,
cannam@92 106 RealTime timestamp)
cannam@92 107 {
cannam@92 108 return m_impl->process(inputBuffers, timestamp);
cannam@92 109 }
cannam@92 110
cannam@92 111 PluginBufferingAdapter::FeatureSet
cannam@92 112 PluginBufferingAdapter::getRemainingFeatures()
cannam@92 113 {
cannam@92 114 return m_impl->getRemainingFeatures();
cannam@92 115 }
cannam@92 116
cannam@92 117 PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
cannam@92 118 m_plugin(plugin),
cannam@92 119 m_inputStepSize(0),
cannam@92 120 m_inputBlockSize(0),
cannam@92 121 m_stepSize(0),
cannam@92 122 m_blockSize(0),
cannam@92 123 m_channels(0),
cannam@92 124 m_buffers(0),
cannam@92 125 m_inputPos(0),
cannam@92 126 m_inputSampleRate(inputSampleRate),
cannam@92 127 m_timestamp()
cannam@92 128 {
cannam@92 129 m_outputs = plugin->getOutputDescriptors();
cannam@92 130 }
cannam@92 131
cannam@92 132 PluginBufferingAdapter::Impl::~Impl()
cannam@92 133 {
cannam@92 134 // the adapter will delete the plugin
cannam@92 135
cannam@92 136 delete [] m_buffers;
cannam@92 137 }
cannam@92 138
cannam@92 139 size_t
cannam@92 140 PluginBufferingAdapter::getPreferredStepSize() const
cannam@92 141 {
cannam@92 142 return getPreferredBlockSize();
cannam@92 143 }
cannam@92 144
cannam@92 145 bool
cannam@92 146 PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@92 147 {
cannam@92 148 if (stepSize != blockSize) {
cannam@92 149 std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl;
cannam@92 150 return false;
cannam@92 151 }
cannam@92 152
cannam@92 153 m_channels = channels;
cannam@92 154 m_inputStepSize = stepSize;
cannam@92 155 m_inputBlockSize = blockSize;
cannam@92 156
cannam@92 157 // use the step and block sizes which the plugin prefers
cannam@92 158 m_stepSize = m_plugin->getPreferredStepSize();
cannam@92 159 m_blockSize = m_plugin->getPreferredBlockSize();
cannam@92 160
cannam@92 161 // or sensible defaults if it has no preference
cannam@92 162 if (m_blockSize == 0) {
cannam@92 163 m_blockSize = 1024;
cannam@92 164 }
cannam@92 165 if (m_stepSize == 0) {
cannam@92 166 if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
cannam@92 167 m_stepSize = m_blockSize/2;
cannam@92 168 } else {
cannam@92 169 m_stepSize = m_blockSize;
cannam@92 170 }
cannam@92 171 } else if (m_stepSize > m_blockSize) {
cannam@92 172 if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
cannam@92 173 m_blockSize = m_stepSize * 2;
cannam@92 174 } else {
cannam@92 175 m_blockSize = m_stepSize;
cannam@92 176 }
cannam@92 177 }
cannam@92 178
cannam@92 179 std::cerr << "PluginBufferingAdapter::initialise: stepSize " << m_inputStepSize << " -> " << m_stepSize
cannam@92 180 << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;
cannam@92 181
cannam@92 182 // current implementation breaks if step is greater than block
cannam@92 183 if (m_stepSize > m_blockSize) {
cannam@92 184 std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl;
cannam@92 185 return false;
cannam@92 186 }
cannam@92 187
cannam@92 188 m_queue.resize(m_channels);
cannam@92 189 m_buffers = new float*[m_channels];
cannam@92 190
cannam@92 191 return m_plugin->initialise(m_channels, m_stepSize, m_blockSize);
cannam@92 192 }
cannam@92 193
cannam@92 194 PluginBufferingAdapter::OutputList
cannam@92 195 PluginBufferingAdapter::Impl::getOutputDescriptors() const
cannam@92 196 {
cannam@92 197 OutputList outs = m_plugin->getOutputDescriptors();
cannam@92 198 for (size_t i = 0; i < outs.size(); ++i) {
cannam@92 199 if (outs[i].sampleType == OutputDescriptor::OneSamplePerStep) {
cannam@92 200 outs[i].sampleRate = 1.f / m_stepSize;
cannam@92 201 }
cannam@92 202 outs[i].sampleType = OutputDescriptor::VariableSampleRate;
cannam@92 203 }
cannam@92 204 return outs;
cannam@92 205 }
cannam@92 206
cannam@92 207 PluginBufferingAdapter::FeatureSet
cannam@92 208 PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
cannam@92 209 RealTime timestamp)
cannam@92 210 {
cannam@92 211 FeatureSet allFeatureSets;
cannam@92 212
cannam@92 213 // queue the new input
cannam@92 214
cannam@92 215 //std::cerr << "unread " << m_queue[0].size() - m_inputPos << " samples" << std::endl;
cannam@92 216 //std::cerr << "queueing " << m_inputBlockSize - (m_queue[0].size() - m_inputPos) << " samples" << std::endl;
cannam@92 217
cannam@92 218 for (size_t i = 0; i < m_channels; ++i)
cannam@92 219 for (size_t j = m_queue[0].size() - m_inputPos; j < m_inputBlockSize; ++j)
cannam@92 220 m_queue[i].push_back(inputBuffers[i][j]);
cannam@92 221
cannam@92 222 m_inputPos += m_inputStepSize;
cannam@92 223
cannam@92 224 // process as much as we can
cannam@92 225 while (m_queue[0].size() >= m_blockSize)
cannam@92 226 {
cannam@92 227 processBlock(allFeatureSets, timestamp);
cannam@92 228 m_inputPos -= m_stepSize;
cannam@92 229
cannam@92 230 //std::cerr << m_queue[0].size() << " samples still left in queue" << std::endl;
cannam@92 231 //std::cerr << "inputPos = " << m_inputPos << std::endl;
cannam@92 232 }
cannam@92 233
cannam@92 234 return allFeatureSets;
cannam@92 235 }
cannam@92 236
cannam@92 237 PluginBufferingAdapter::FeatureSet
cannam@92 238 PluginBufferingAdapter::Impl::getRemainingFeatures()
cannam@92 239 {
cannam@92 240 FeatureSet allFeatureSets;
cannam@92 241
cannam@92 242 // process remaining samples in queue
cannam@92 243 while (m_queue[0].size() >= m_blockSize)
cannam@92 244 {
cannam@92 245 processBlock(allFeatureSets, m_timestamp);
cannam@92 246 }
cannam@92 247
cannam@92 248 // pad any last samples remaining and process
cannam@92 249 if (m_queue[0].size() > 0)
cannam@92 250 {
cannam@92 251 for (size_t i = 0; i < m_channels; ++i)
cannam@92 252 while (m_queue[i].size() < m_blockSize)
cannam@92 253 m_queue[i].push_back(0.0);
cannam@92 254 processBlock(allFeatureSets, m_timestamp);
cannam@92 255 }
cannam@92 256
cannam@92 257 // get remaining features
cannam@92 258 FeatureSet featureSet = m_plugin->getRemainingFeatures();
cannam@92 259 for (map<int, FeatureList>::iterator iter = featureSet.begin();
cannam@92 260 iter != featureSet.end(); ++iter)
cannam@92 261 {
cannam@92 262 FeatureList featureList = iter->second;
cannam@92 263 for (size_t i = 0; i < featureList.size(); ++i)
cannam@92 264 allFeatureSets[iter->first].push_back(featureList[i]);
cannam@92 265 }
cannam@92 266
cannam@92 267 return allFeatureSets;
cannam@92 268 }
cannam@92 269
cannam@92 270 void
cannam@92 271 PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets, RealTime timestamp)
cannam@92 272 {
cannam@92 273 //std::cerr << m_queue[0].size() << " samples left in queue" << std::endl;
cannam@92 274
cannam@92 275 // point the buffers to the head of the queue
cannam@92 276 for (size_t i = 0; i < m_channels; ++i)
cannam@92 277 m_buffers[i] = &m_queue[i][0];
cannam@92 278
cannam@92 279 FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp);
cannam@92 280
cannam@92 281 for (map<int, FeatureList>::iterator iter = featureSet.begin();
cannam@92 282 iter != featureSet.end(); ++iter)
cannam@92 283 {
cannam@92 284
cannam@92 285 FeatureList featureList = iter->second;
cannam@92 286 int outputNo = iter->first;
cannam@92 287
cannam@92 288 for (size_t i = 0; i < featureList.size(); ++i)
cannam@92 289 {
cannam@92 290
cannam@92 291 // make sure the timestamp is set
cannam@92 292 switch (m_outputs[outputNo].sampleType)
cannam@92 293 {
cannam@92 294 case OutputDescriptor::OneSamplePerStep:
cannam@92 295 // use our internal timestamp - OK????
cannam@92 296 featureList[i].timestamp = m_timestamp;
cannam@92 297 break;
cannam@92 298 case OutputDescriptor::FixedSampleRate:
cannam@92 299 // use our internal timestamp
cannam@92 300 featureList[i].timestamp = m_timestamp;
cannam@92 301 break;
cannam@92 302 case OutputDescriptor::VariableSampleRate:
cannam@92 303 break; // plugin must set timestamp
cannam@92 304 default:
cannam@92 305 break;
cannam@92 306 }
cannam@92 307
cannam@92 308 allFeatureSets[outputNo].push_back(featureList[i]);
cannam@92 309 }
cannam@92 310 }
cannam@92 311
cannam@92 312 // step forward
cannam@92 313 for (size_t i = 0; i < m_channels; ++i)
cannam@92 314 m_queue[i].erase(m_queue[i].begin(), m_queue[i].begin() + m_stepSize);
cannam@92 315
cannam@92 316 // fake up the timestamp each time we step forward
cannam@92 317 //std::cerr << m_timestamp;
cannam@92 318 long frame = RealTime::realTime2Frame(m_timestamp, int(m_inputSampleRate + 0.5));
cannam@92 319 m_timestamp = RealTime::frame2RealTime(frame + m_stepSize, int(m_inputSampleRate + 0.5));
cannam@92 320 //std::cerr << "--->" << m_timestamp << std::endl;
cannam@92 321 }
cannam@92 322
cannam@92 323 }
cannam@92 324
cannam@92 325 }
cannam@92 326
cannam@92 327