1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
6 An API for audio analysis and feature extraction plugins.
8 Centre for Digital Music, Queen Mary, University of London.
9 Copyright 2006-2007 Chris Cannam and QMUL.
10 This file by Mark Levy and Chris Cannam, Copyright 2007-2008 QMUL.
12 Permission is hereby granted, free of charge, to any person
13 obtaining a copy of this software and associated documentation
14 files (the "Software"), to deal in the Software without
15 restriction, including without limitation the rights to use, copy,
16 modify, merge, publish, distribute, sublicense, and/or sell copies
17 of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
20 The above copyright notice and this permission notice shall be
21 included in all copies or substantial portions of the Software.
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
27 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
28 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 Except as contained in this notice, the names of the Centre for
32 Digital Music; Queen Mary, University of London; and Chris Cannam
33 shall not be used in advertising or otherwise to promote the sale,
34 use or other dealings in this Software without prior written
41 #include <vamp-hostsdk/PluginBufferingAdapter.h>
46 _VAMP_SDK_HOSTSPACE_BEGIN(PluginBufferingAdapter.cpp)
52 class PluginBufferingAdapter::Impl
55 Impl(Plugin *plugin, float inputSampleRate);
58 void setPluginStepSize(size_t stepSize);
59 void setPluginBlockSize(size_t blockSize);
61 bool initialise(size_t channels, size_t stepSize, size_t blockSize);
63 void getActualStepAndBlockSizes(size_t &stepSize, size_t &blockSize);
65 OutputList getOutputDescriptors() const;
67 void setParameter(std::string, float);
68 void selectProgram(std::string);
72 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
74 FeatureSet getRemainingFeatures();
81 m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { }
82 virtual ~RingBuffer() { delete[] m_buffer; }
84 int getSize() const { return m_size-1; }
85 void reset() { m_writer = 0; m_reader = 0; }
87 int getReadSpace() const {
88 int writer = m_writer, reader = m_reader, space;
89 if (writer > reader) space = writer - reader;
90 else if (writer < reader) space = (writer + m_size) - reader;
95 int getWriteSpace() const {
96 int writer = m_writer;
97 int reader = m_reader;
98 int space = (reader + m_size - writer - 1);
99 if (space >= m_size) space -= m_size;
103 int peek(float *destination, int n) const {
105 int available = getReadSpace();
108 for (int i = available; i < n; ++i) {
109 destination[i] = 0.f;
113 if (n == 0) return n;
115 int reader = m_reader;
116 int here = m_size - reader;
117 const float *const bufbase = m_buffer + reader;
120 for (int i = 0; i < n; ++i) {
121 destination[i] = bufbase[i];
124 for (int i = 0; i < here; ++i) {
125 destination[i] = bufbase[i];
127 float *const destbase = destination + here;
128 const int nh = n - here;
129 for (int i = 0; i < nh; ++i) {
130 destbase[i] = m_buffer[i];
139 int available = getReadSpace();
143 if (n == 0) return n;
145 int reader = m_reader;
147 while (reader >= m_size) reader -= m_size;
152 int write(const float *source, int n) {
154 int available = getWriteSpace();
158 if (n == 0) return n;
160 int writer = m_writer;
161 int here = m_size - writer;
162 float *const bufbase = m_buffer + writer;
165 for (int i = 0; i < n; ++i) {
166 bufbase[i] = source[i];
169 for (int i = 0; i < here; ++i) {
170 bufbase[i] = source[i];
172 const int nh = n - here;
173 const float *const srcbase = source + here;
174 float *const buf = m_buffer;
175 for (int i = 0; i < nh; ++i) {
181 while (writer >= m_size) writer -= m_size;
189 int available = getWriteSpace();
193 if (n == 0) return n;
195 int writer = m_writer;
196 int here = m_size - writer;
197 float *const bufbase = m_buffer + writer;
200 for (int i = 0; i < n; ++i) {
204 for (int i = 0; i < here; ++i) {
207 const int nh = n - here;
208 for (int i = 0; i < nh; ++i) {
214 while (writer >= m_size) writer -= m_size;
227 RingBuffer(const RingBuffer &); // not provided
228 RingBuffer &operator=(const RingBuffer &); // not provided
232 size_t m_inputStepSize; // value passed to wrapper initialise()
233 size_t m_inputBlockSize; // value passed to wrapper initialise()
234 size_t m_setStepSize; // value passed to setPluginStepSize()
235 size_t m_setBlockSize; // value passed to setPluginBlockSize()
236 size_t m_stepSize; // value actually used to initialise plugin
237 size_t m_blockSize; // value actually used to initialise plugin
239 vector<RingBuffer *> m_queue;
241 float m_inputSampleRate;
244 mutable OutputList m_outputs;
245 mutable std::map<int, bool> m_rewriteOutputTimes;
247 void processBlock(FeatureSet& allFeatureSets);
250 PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) :
251 PluginWrapper(plugin)
253 m_impl = new Impl(plugin, m_inputSampleRate);
256 PluginBufferingAdapter::~PluginBufferingAdapter()
262 PluginBufferingAdapter::getPreferredStepSize() const
264 return getPreferredBlockSize();
268 PluginBufferingAdapter::getPreferredBlockSize() const
270 return PluginWrapper::getPreferredBlockSize();
274 PluginBufferingAdapter::getPluginPreferredStepSize() const
276 return PluginWrapper::getPreferredStepSize();
280 PluginBufferingAdapter::getPluginPreferredBlockSize() const
282 return PluginWrapper::getPreferredBlockSize();
286 PluginBufferingAdapter::setPluginStepSize(size_t stepSize)
288 m_impl->setPluginStepSize(stepSize);
292 PluginBufferingAdapter::setPluginBlockSize(size_t blockSize)
294 m_impl->setPluginBlockSize(blockSize);
298 PluginBufferingAdapter::getActualStepAndBlockSizes(size_t &stepSize,
301 m_impl->getActualStepAndBlockSizes(stepSize, blockSize);
305 PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
307 return m_impl->initialise(channels, stepSize, blockSize);
310 PluginBufferingAdapter::OutputList
311 PluginBufferingAdapter::getOutputDescriptors() const
313 return m_impl->getOutputDescriptors();
317 PluginBufferingAdapter::setParameter(std::string name, float value)
319 m_impl->setParameter(name, value);
323 PluginBufferingAdapter::selectProgram(std::string name)
325 m_impl->selectProgram(name);
329 PluginBufferingAdapter::reset()
334 PluginBufferingAdapter::FeatureSet
335 PluginBufferingAdapter::process(const float *const *inputBuffers,
338 return m_impl->process(inputBuffers, timestamp);
341 PluginBufferingAdapter::FeatureSet
342 PluginBufferingAdapter::getRemainingFeatures()
344 return m_impl->getRemainingFeatures();
347 PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
358 m_inputSampleRate(inputSampleRate),
362 (void)getOutputDescriptors(); // set up m_outputs and m_rewriteOutputTimes
365 PluginBufferingAdapter::Impl::~Impl()
367 // the adapter will delete the plugin
369 for (size_t i = 0; i < m_channels; ++i) {
371 delete[] m_buffers[i];
377 PluginBufferingAdapter::Impl::setPluginStepSize(size_t stepSize)
379 if (m_inputStepSize != 0) {
380 std::cerr << "PluginBufferingAdapter::setPluginStepSize: ERROR: Cannot be called after initialise()" << std::endl;
383 m_setStepSize = stepSize;
387 PluginBufferingAdapter::Impl::setPluginBlockSize(size_t blockSize)
389 if (m_inputBlockSize != 0) {
390 std::cerr << "PluginBufferingAdapter::setPluginBlockSize: ERROR: Cannot be called after initialise()" << std::endl;
393 m_setBlockSize = blockSize;
397 PluginBufferingAdapter::Impl::getActualStepAndBlockSizes(size_t &stepSize,
400 stepSize = m_stepSize;
401 blockSize = m_blockSize;
405 PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
407 if (stepSize != blockSize) {
408 std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl;
412 m_channels = channels;
413 m_inputStepSize = stepSize;
414 m_inputBlockSize = blockSize;
416 // if the user has requested particular step or block sizes, use
417 // those; otherwise use the step and block sizes which the plugin
423 if (m_setStepSize > 0) {
424 m_stepSize = m_setStepSize;
426 if (m_setBlockSize > 0) {
427 m_blockSize = m_setBlockSize;
430 if (m_stepSize == 0 && m_blockSize == 0) {
431 m_stepSize = m_plugin->getPreferredStepSize();
432 m_blockSize = m_plugin->getPreferredBlockSize();
435 bool freq = (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain);
437 // or sensible defaults if it has no preference
438 if (m_blockSize == 0) {
439 if (m_stepSize == 0) {
442 m_stepSize = m_blockSize / 2;
444 m_stepSize = m_blockSize;
447 m_blockSize = m_stepSize * 2;
449 m_blockSize = m_stepSize;
451 } else if (m_stepSize == 0) { // m_blockSize != 0 (that was handled above)
453 m_stepSize = m_blockSize/2;
455 m_stepSize = m_blockSize;
459 // current implementation breaks if step is greater than block
460 if (m_stepSize > m_blockSize) {
463 newBlockSize = m_stepSize * 2;
465 newBlockSize = m_stepSize;
467 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;
468 m_blockSize = newBlockSize;
471 // std::cerr << "PluginBufferingAdapter::initialise: NOTE: stepSize " << m_inputStepSize << " -> " << m_stepSize
472 // << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;
474 m_buffers = new float *[m_channels];
476 for (size_t i = 0; i < m_channels; ++i) {
477 m_queue.push_back(new RingBuffer(m_blockSize + m_inputBlockSize));
478 m_buffers[i] = new float[m_blockSize];
481 bool success = m_plugin->initialise(m_channels, m_stepSize, m_blockSize);
483 // std::cerr << "PluginBufferingAdapter::initialise: success = " << success << std::endl;
486 // Re-query outputs; properties such as bin count may have
487 // changed on initialise
489 (void)getOutputDescriptors();
495 PluginBufferingAdapter::OutputList
496 PluginBufferingAdapter::Impl::getOutputDescriptors() const
498 if (m_outputs.empty()) {
499 // std::cerr << "PluginBufferingAdapter::getOutputDescriptors: querying anew" << std::endl;
501 m_outputs = m_plugin->getOutputDescriptors();
504 PluginBufferingAdapter::OutputList outs = m_outputs;
506 for (size_t i = 0; i < outs.size(); ++i) {
508 switch (outs[i].sampleType) {
510 case OutputDescriptor::OneSamplePerStep:
511 outs[i].sampleType = OutputDescriptor::FixedSampleRate;
512 outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize;
513 m_rewriteOutputTimes[i] = true;
516 case OutputDescriptor::FixedSampleRate:
517 if (outs[i].sampleRate == 0.f) {
518 outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize;
520 // We actually only need to rewrite output times for
521 // features that don't have timestamps already, but we
522 // can't tell from here whether our features will have
524 m_rewriteOutputTimes[i] = true;
527 case OutputDescriptor::VariableSampleRate:
528 m_rewriteOutputTimes[i] = false;
537 PluginBufferingAdapter::Impl::setParameter(std::string name, float value)
539 m_plugin->setParameter(name, value);
541 // Re-query outputs; properties such as bin count may have changed
543 (void)getOutputDescriptors();
547 PluginBufferingAdapter::Impl::selectProgram(std::string name)
549 m_plugin->selectProgram(name);
551 // Re-query outputs; properties such as bin count may have changed
553 (void)getOutputDescriptors();
557 PluginBufferingAdapter::Impl::reset()
562 for (size_t i = 0; i < m_queue.size(); ++i) {
569 PluginBufferingAdapter::FeatureSet
570 PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
573 if (m_inputStepSize == 0) {
574 std::cerr << "PluginBufferingAdapter::process: ERROR: Plugin has not been initialised" << std::endl;
578 FeatureSet allFeatureSets;
581 m_frame = RealTime::realTime2Frame(timestamp,
582 int(m_inputSampleRate + 0.5));
586 // queue the new input
588 for (size_t i = 0; i < m_channels; ++i) {
589 int written = m_queue[i]->write(inputBuffers[i], m_inputBlockSize);
590 if (written < int(m_inputBlockSize) && i == 0) {
591 std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: "
592 << "Buffer overflow: wrote " << written
593 << " of " << m_inputBlockSize
594 << " input samples (for plugin step size "
595 << m_stepSize << ", block size " << m_blockSize << ")"
600 // process as much as we can
602 while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
603 processBlock(allFeatureSets);
606 return allFeatureSets;
609 PluginBufferingAdapter::FeatureSet
610 PluginBufferingAdapter::Impl::getRemainingFeatures()
612 FeatureSet allFeatureSets;
614 // process remaining samples in queue
615 while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
616 processBlock(allFeatureSets);
619 // pad any last samples remaining and process
620 if (m_queue[0]->getReadSpace() > 0) {
621 for (size_t i = 0; i < m_channels; ++i) {
622 m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace());
624 processBlock(allFeatureSets);
627 // get remaining features
629 FeatureSet featureSet = m_plugin->getRemainingFeatures();
631 for (map<int, FeatureList>::iterator iter = featureSet.begin();
632 iter != featureSet.end(); ++iter) {
633 FeatureList featureList = iter->second;
634 for (size_t i = 0; i < featureList.size(); ++i) {
635 allFeatureSets[iter->first].push_back(featureList[i]);
639 return allFeatureSets;
643 PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets)
645 for (size_t i = 0; i < m_channels; ++i) {
646 m_queue[i]->peek(m_buffers[i], m_blockSize);
649 long frame = m_frame;
650 RealTime timestamp = RealTime::frame2RealTime
651 (frame, int(m_inputSampleRate + 0.5));
653 FeatureSet featureSet = m_plugin->process(m_buffers, timestamp);
655 for (FeatureSet::iterator iter = featureSet.begin();
656 iter != featureSet.end(); ++iter) {
658 int outputNo = iter->first;
660 if (m_rewriteOutputTimes[outputNo]) {
662 FeatureList featureList = iter->second;
664 for (size_t i = 0; i < featureList.size(); ++i) {
666 switch (m_outputs[outputNo].sampleType) {
668 case OutputDescriptor::OneSamplePerStep:
669 // use our internal timestamp, always
670 featureList[i].timestamp = timestamp;
671 featureList[i].hasTimestamp = true;
674 case OutputDescriptor::FixedSampleRate:
675 // use our internal timestamp if feature lacks one
676 if (!featureList[i].hasTimestamp) {
677 featureList[i].timestamp = timestamp;
678 featureList[i].hasTimestamp = true;
682 case OutputDescriptor::VariableSampleRate:
683 break; // plugin must set timestamp
689 allFeatureSets[outputNo].push_back(featureList[i]);
692 for (size_t i = 0; i < iter->second.size(); ++i) {
693 allFeatureSets[outputNo].push_back(iter->second[i]);
700 for (size_t i = 0; i < m_channels; ++i) {
701 m_queue[i]->skip(m_stepSize);
704 // increment internal frame counter each time we step forward
705 m_frame += m_stepSize;
712 _VAMP_SDK_HOSTSPACE_END(PluginBufferingAdapter.cpp)