cannam@227: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ cannam@227: cannam@227: /* cannam@227: Vamp cannam@227: cannam@227: An API for audio analysis and feature extraction plugins. cannam@227: cannam@227: Centre for Digital Music, Queen Mary, University of London. cannam@227: Copyright 2006-2007 Chris Cannam and QMUL. cannam@227: This file by Mark Levy and Chris Cannam, Copyright 2007-2008 QMUL. cannam@227: cannam@227: Permission is hereby granted, free of charge, to any person cannam@227: obtaining a copy of this software and associated documentation cannam@227: files (the "Software"), to deal in the Software without cannam@227: restriction, including without limitation the rights to use, copy, cannam@227: modify, merge, publish, distribute, sublicense, and/or sell copies cannam@227: of the Software, and to permit persons to whom the Software is cannam@227: furnished to do so, subject to the following conditions: cannam@227: cannam@227: The above copyright notice and this permission notice shall be cannam@227: included in all copies or substantial portions of the Software. cannam@227: cannam@227: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, cannam@227: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF cannam@227: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND cannam@227: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR cannam@227: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF cannam@227: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION cannam@227: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cannam@227: cannam@227: Except as contained in this notice, the names of the Centre for cannam@227: Digital Music; Queen Mary, University of London; and Chris Cannam cannam@227: shall not be used in advertising or otherwise to promote the sale, cannam@227: use or other dealings in this Software without prior written cannam@227: authorization. cannam@227: */ cannam@227: cannam@227: #include cannam@227: #include cannam@227: cannam@227: #include "PluginBufferingAdapter.h" cannam@227: cannam@227: using std::vector; cannam@227: using std::map; cannam@227: cannam@227: namespace Vamp { cannam@227: cannam@227: namespace HostExt { cannam@227: cannam@227: class PluginBufferingAdapter::Impl cannam@227: { cannam@227: public: cannam@227: Impl(Plugin *plugin, float inputSampleRate); cannam@227: ~Impl(); cannam@227: cannam@227: void setPluginStepSize(size_t stepSize); cannam@227: void setPluginBlockSize(size_t blockSize); cannam@227: cannam@227: bool initialise(size_t channels, size_t stepSize, size_t blockSize); cannam@227: cannam@227: void getActualStepAndBlockSizes(size_t &stepSize, size_t &blockSize); cannam@227: cannam@227: OutputList getOutputDescriptors() const; cannam@227: cannam@227: void reset(); cannam@227: cannam@227: FeatureSet process(const float *const *inputBuffers, RealTime timestamp); cannam@227: cannam@227: FeatureSet getRemainingFeatures(); cannam@227: cannam@227: protected: cannam@227: class RingBuffer cannam@227: { cannam@227: public: cannam@227: RingBuffer(int n) : cannam@227: m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { } cannam@227: virtual ~RingBuffer() { delete[] m_buffer; } cannam@227: cannam@227: int getSize() const { return m_size-1; } cannam@227: void reset() { m_writer = 0; m_reader = 0; } cannam@227: cannam@227: int getReadSpace() const { cannam@227: int writer = m_writer, reader = m_reader, space; cannam@227: if (writer > reader) space = writer - reader; cannam@227: else if (writer < reader) space = (writer + m_size) - reader; cannam@227: else space = 0; cannam@227: return space; cannam@227: } cannam@227: cannam@227: int getWriteSpace() const { cannam@227: int writer = m_writer; cannam@227: int reader = m_reader; cannam@227: int space = (reader + m_size - writer - 1); cannam@227: if (space >= m_size) space -= m_size; cannam@227: return space; cannam@227: } cannam@227: cannam@227: int peek(float *destination, int n) const { cannam@227: cannam@227: int available = getReadSpace(); cannam@227: cannam@227: if (n > available) { cannam@227: for (int i = available; i < n; ++i) { cannam@227: destination[i] = 0.f; cannam@227: } cannam@227: n = available; cannam@227: } cannam@227: if (n == 0) return n; cannam@227: cannam@227: int reader = m_reader; cannam@227: int here = m_size - reader; cannam@227: const float *const bufbase = m_buffer + reader; cannam@227: cannam@227: if (here >= n) { cannam@227: for (int i = 0; i < n; ++i) { cannam@227: destination[i] = bufbase[i]; cannam@227: } cannam@227: } else { cannam@227: for (int i = 0; i < here; ++i) { cannam@227: destination[i] = bufbase[i]; cannam@227: } cannam@227: float *const destbase = destination + here; cannam@227: const int nh = n - here; cannam@227: for (int i = 0; i < nh; ++i) { cannam@227: destbase[i] = m_buffer[i]; cannam@227: } cannam@227: } cannam@227: cannam@227: return n; cannam@227: } cannam@227: cannam@227: int skip(int n) { cannam@227: cannam@227: int available = getReadSpace(); cannam@227: if (n > available) { cannam@227: n = available; cannam@227: } cannam@227: if (n == 0) return n; cannam@227: cannam@227: int reader = m_reader; cannam@227: reader += n; cannam@227: while (reader >= m_size) reader -= m_size; cannam@227: m_reader = reader; cannam@227: return n; cannam@227: } cannam@227: cannam@227: int write(const float *source, int n) { cannam@227: cannam@227: int available = getWriteSpace(); cannam@227: if (n > available) { cannam@227: n = available; cannam@227: } cannam@227: if (n == 0) return n; cannam@227: cannam@227: int writer = m_writer; cannam@227: int here = m_size - writer; cannam@227: float *const bufbase = m_buffer + writer; cannam@227: cannam@227: if (here >= n) { cannam@227: for (int i = 0; i < n; ++i) { cannam@227: bufbase[i] = source[i]; cannam@227: } cannam@227: } else { cannam@227: for (int i = 0; i < here; ++i) { cannam@227: bufbase[i] = source[i]; cannam@227: } cannam@227: const int nh = n - here; cannam@227: const float *const srcbase = source + here; cannam@227: float *const buf = m_buffer; cannam@227: for (int i = 0; i < nh; ++i) { cannam@227: buf[i] = srcbase[i]; cannam@227: } cannam@227: } cannam@227: cannam@227: writer += n; cannam@227: while (writer >= m_size) writer -= m_size; cannam@227: m_writer = writer; cannam@227: cannam@227: return n; cannam@227: } cannam@227: cannam@227: int zero(int n) { cannam@227: cannam@227: int available = getWriteSpace(); cannam@227: if (n > available) { cannam@227: n = available; cannam@227: } cannam@227: if (n == 0) return n; cannam@227: cannam@227: int writer = m_writer; cannam@227: int here = m_size - writer; cannam@227: float *const bufbase = m_buffer + writer; cannam@227: cannam@227: if (here >= n) { cannam@227: for (int i = 0; i < n; ++i) { cannam@227: bufbase[i] = 0.f; cannam@227: } cannam@227: } else { cannam@227: for (int i = 0; i < here; ++i) { cannam@227: bufbase[i] = 0.f; cannam@227: } cannam@227: const int nh = n - here; cannam@227: for (int i = 0; i < nh; ++i) { cannam@227: m_buffer[i] = 0.f; cannam@227: } cannam@227: } cannam@227: cannam@227: writer += n; cannam@227: while (writer >= m_size) writer -= m_size; cannam@227: m_writer = writer; cannam@227: cannam@227: return n; cannam@227: } cannam@227: cannam@227: protected: cannam@227: float *m_buffer; cannam@227: int m_writer; cannam@227: int m_reader; cannam@227: int m_size; cannam@227: cannam@227: private: cannam@227: RingBuffer(const RingBuffer &); // not provided cannam@227: RingBuffer &operator=(const RingBuffer &); // not provided cannam@227: }; cannam@227: cannam@227: Plugin *m_plugin; cannam@227: size_t m_inputStepSize; // value passed to wrapper initialise() cannam@227: size_t m_inputBlockSize; // value passed to wrapper initialise() cannam@227: size_t m_setStepSize; // value passed to setPluginStepSize() cannam@227: size_t m_setBlockSize; // value passed to setPluginBlockSize() cannam@227: size_t m_stepSize; // value actually used to initialise plugin cannam@227: size_t m_blockSize; // value actually used to initialise plugin cannam@227: size_t m_channels; cannam@227: vector m_queue; cannam@227: float **m_buffers; cannam@227: float m_inputSampleRate; cannam@227: long m_frame; cannam@227: bool m_unrun; cannam@227: mutable OutputList m_outputs; cannam@227: mutable std::map m_rewriteOutputTimes; cannam@227: cannam@227: void processBlock(FeatureSet& allFeatureSets); cannam@227: }; cannam@227: cannam@227: PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) : cannam@227: PluginWrapper(plugin) cannam@227: { cannam@227: m_impl = new Impl(plugin, m_inputSampleRate); cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::~PluginBufferingAdapter() cannam@227: { cannam@227: delete m_impl; cannam@227: } cannam@227: cannam@227: size_t cannam@227: PluginBufferingAdapter::getPreferredStepSize() const cannam@227: { cannam@227: return getPreferredBlockSize(); cannam@227: } cannam@227: cannam@227: size_t cannam@227: PluginBufferingAdapter::getPreferredBlockSize() const cannam@227: { cannam@227: return PluginWrapper::getPreferredBlockSize(); cannam@227: } cannam@227: cannam@227: size_t cannam@227: PluginBufferingAdapter::getPluginPreferredStepSize() const cannam@227: { cannam@227: return PluginWrapper::getPreferredStepSize(); cannam@227: } cannam@227: cannam@227: size_t cannam@227: PluginBufferingAdapter::getPluginPreferredBlockSize() const cannam@227: { cannam@227: return PluginWrapper::getPreferredBlockSize(); cannam@227: } cannam@227: cannam@227: void cannam@227: PluginBufferingAdapter::setPluginStepSize(size_t stepSize) cannam@227: { cannam@227: m_impl->setPluginStepSize(stepSize); cannam@227: } cannam@227: cannam@227: void cannam@227: PluginBufferingAdapter::setPluginBlockSize(size_t blockSize) cannam@227: { cannam@227: m_impl->setPluginBlockSize(blockSize); cannam@227: } cannam@227: cannam@227: void cannam@227: PluginBufferingAdapter::getActualStepAndBlockSizes(size_t &stepSize, cannam@227: size_t &blockSize) cannam@227: { cannam@227: m_impl->getActualStepAndBlockSizes(stepSize, blockSize); cannam@227: } cannam@227: cannam@227: bool cannam@227: PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize) cannam@227: { cannam@227: return m_impl->initialise(channels, stepSize, blockSize); cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::OutputList cannam@227: PluginBufferingAdapter::getOutputDescriptors() const cannam@227: { cannam@227: return m_impl->getOutputDescriptors(); cannam@227: } cannam@227: cannam@227: void cannam@227: PluginBufferingAdapter::reset() cannam@227: { cannam@227: m_impl->reset(); cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::FeatureSet cannam@227: PluginBufferingAdapter::process(const float *const *inputBuffers, cannam@227: RealTime timestamp) cannam@227: { cannam@227: return m_impl->process(inputBuffers, timestamp); cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::FeatureSet cannam@227: PluginBufferingAdapter::getRemainingFeatures() cannam@227: { cannam@227: return m_impl->getRemainingFeatures(); cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : cannam@227: m_plugin(plugin), cannam@227: m_inputStepSize(0), cannam@227: m_inputBlockSize(0), cannam@227: m_setStepSize(0), cannam@227: m_setBlockSize(0), cannam@227: m_stepSize(0), cannam@227: m_blockSize(0), cannam@227: m_channels(0), cannam@227: m_queue(0), cannam@227: m_buffers(0), cannam@227: m_inputSampleRate(inputSampleRate), cannam@227: m_frame(0), cannam@227: m_unrun(true) cannam@227: { cannam@227: (void)getOutputDescriptors(); // set up m_outputs and m_rewriteOutputTimes cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::Impl::~Impl() cannam@227: { cannam@227: // the adapter will delete the plugin cannam@227: cannam@227: for (size_t i = 0; i < m_channels; ++i) { cannam@227: delete m_queue[i]; cannam@227: delete[] m_buffers[i]; cannam@227: } cannam@227: delete[] m_buffers; cannam@227: } cannam@227: cannam@227: void cannam@227: PluginBufferingAdapter::Impl::setPluginStepSize(size_t stepSize) cannam@227: { cannam@227: if (m_inputStepSize != 0) { cannam@227: std::cerr << "PluginBufferingAdapter::setPluginStepSize: ERROR: Cannot be called after initialise()" << std::endl; cannam@227: return; cannam@227: } cannam@227: m_setStepSize = stepSize; cannam@227: } cannam@227: cannam@227: void cannam@227: PluginBufferingAdapter::Impl::setPluginBlockSize(size_t blockSize) cannam@227: { cannam@227: if (m_inputBlockSize != 0) { cannam@227: std::cerr << "PluginBufferingAdapter::setPluginBlockSize: ERROR: Cannot be called after initialise()" << std::endl; cannam@227: return; cannam@227: } cannam@227: m_setBlockSize = blockSize; cannam@227: } cannam@227: cannam@227: void cannam@227: PluginBufferingAdapter::Impl::getActualStepAndBlockSizes(size_t &stepSize, cannam@227: size_t &blockSize) cannam@227: { cannam@227: stepSize = m_stepSize; cannam@227: blockSize = m_blockSize; cannam@227: } cannam@227: cannam@227: bool cannam@227: PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize) cannam@227: { cannam@227: if (stepSize != blockSize) { cannam@227: std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl; cannam@227: return false; cannam@227: } cannam@227: cannam@227: m_channels = channels; cannam@227: m_inputStepSize = stepSize; cannam@227: m_inputBlockSize = blockSize; cannam@227: cannam@227: // if the user has requested particular step or block sizes, use cannam@227: // those; otherwise use the step and block sizes which the plugin cannam@227: // prefers cannam@227: cannam@227: m_stepSize = 0; cannam@227: m_blockSize = 0; cannam@227: cannam@227: if (m_setStepSize > 0) { cannam@227: m_stepSize = m_setStepSize; cannam@227: } cannam@227: if (m_setBlockSize > 0) { cannam@227: m_blockSize = m_setBlockSize; cannam@227: } cannam@227: cannam@227: if (m_stepSize == 0 && m_blockSize == 0) { cannam@227: m_stepSize = m_plugin->getPreferredStepSize(); cannam@227: m_blockSize = m_plugin->getPreferredBlockSize(); cannam@227: } cannam@227: cannam@227: bool freq = (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain); cannam@227: cannam@227: // or sensible defaults if it has no preference cannam@227: if (m_blockSize == 0) { cannam@227: if (m_stepSize == 0) { cannam@227: m_blockSize = 1024; cannam@227: } else if (freq) { cannam@227: m_blockSize = m_stepSize * 2; cannam@227: } else { cannam@227: m_blockSize = m_stepSize; cannam@227: } cannam@227: } else if (m_stepSize == 0) { // m_blockSize != 0 (that was handled above) cannam@227: if (freq) { cannam@227: m_stepSize = m_blockSize/2; cannam@227: } else { cannam@227: m_stepSize = m_blockSize; cannam@227: } cannam@227: } cannam@227: cannam@227: // current implementation breaks if step is greater than block cannam@227: if (m_stepSize > m_blockSize) { cannam@227: size_t newBlockSize; cannam@227: if (freq) { cannam@227: newBlockSize = m_stepSize * 2; cannam@227: } else { cannam@227: newBlockSize = m_stepSize; cannam@227: } cannam@227: 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; cannam@227: m_blockSize = newBlockSize; cannam@227: } cannam@227: cannam@227: std::cerr << "PluginBufferingAdapter::initialise: NOTE: stepSize " << m_inputStepSize << " -> " << m_stepSize cannam@227: << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl; cannam@227: cannam@227: m_buffers = new float *[m_channels]; cannam@227: cannam@227: for (size_t i = 0; i < m_channels; ++i) { cannam@227: m_queue.push_back(new RingBuffer(m_blockSize + m_inputBlockSize)); cannam@227: m_buffers[i] = new float[m_blockSize]; cannam@227: } cannam@227: cannam@227: return m_plugin->initialise(m_channels, m_stepSize, m_blockSize); cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::OutputList cannam@227: PluginBufferingAdapter::Impl::getOutputDescriptors() const cannam@227: { cannam@227: if (m_outputs.empty()) { cannam@227: m_outputs = m_plugin->getOutputDescriptors(); cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::OutputList outs = m_outputs; cannam@227: cannam@227: for (size_t i = 0; i < outs.size(); ++i) { cannam@227: cannam@227: switch (outs[i].sampleType) { cannam@227: cannam@227: case OutputDescriptor::OneSamplePerStep: cannam@227: outs[i].sampleType = OutputDescriptor::FixedSampleRate; cannam@227: outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize; cannam@227: m_rewriteOutputTimes[i] = true; cannam@227: break; cannam@227: cannam@227: case OutputDescriptor::FixedSampleRate: cannam@227: if (outs[i].sampleRate == 0.f) { cannam@227: outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize; cannam@227: } cannam@227: // We actually only need to rewrite output times for cannam@227: // features that don't have timestamps already, but we cannam@227: // can't tell from here whether our features will have cannam@227: // timestamps or not cannam@227: m_rewriteOutputTimes[i] = true; cannam@227: break; cannam@227: cannam@227: case OutputDescriptor::VariableSampleRate: cannam@227: m_rewriteOutputTimes[i] = false; cannam@227: break; cannam@227: } cannam@227: } cannam@227: cannam@227: return outs; cannam@227: } cannam@227: cannam@227: void cannam@227: PluginBufferingAdapter::Impl::reset() cannam@227: { cannam@227: m_frame = 0; cannam@227: m_unrun = true; cannam@227: cannam@227: for (size_t i = 0; i < m_queue.size(); ++i) { cannam@227: m_queue[i]->reset(); cannam@227: } cannam@227: cannam@227: m_plugin->reset(); cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::FeatureSet cannam@227: PluginBufferingAdapter::Impl::process(const float *const *inputBuffers, cannam@227: RealTime timestamp) cannam@227: { cannam@227: if (m_inputStepSize == 0) { cannam@227: std::cerr << "PluginBufferingAdapter::process: ERROR: Plugin has not been initialised" << std::endl; cannam@227: return FeatureSet(); cannam@227: } cannam@227: cannam@227: FeatureSet allFeatureSets; cannam@227: cannam@227: if (m_unrun) { cannam@227: m_frame = RealTime::realTime2Frame(timestamp, cannam@227: int(m_inputSampleRate + 0.5)); cannam@227: m_unrun = false; cannam@227: } cannam@227: cannam@227: // queue the new input cannam@227: cannam@227: for (size_t i = 0; i < m_channels; ++i) { cannam@227: int written = m_queue[i]->write(inputBuffers[i], m_inputBlockSize); cannam@227: if (written < int(m_inputBlockSize) && i == 0) { cannam@227: std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: " cannam@227: << "Buffer overflow: wrote " << written cannam@227: << " of " << m_inputBlockSize cannam@227: << " input samples (for plugin step size " cannam@227: << m_stepSize << ", block size " << m_blockSize << ")" cannam@227: << std::endl; cannam@227: } cannam@227: } cannam@227: cannam@227: // process as much as we can cannam@227: cannam@227: while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { cannam@227: processBlock(allFeatureSets); cannam@227: } cannam@227: cannam@227: return allFeatureSets; cannam@227: } cannam@227: cannam@227: PluginBufferingAdapter::FeatureSet cannam@227: PluginBufferingAdapter::Impl::getRemainingFeatures() cannam@227: { cannam@227: FeatureSet allFeatureSets; cannam@227: cannam@227: // process remaining samples in queue cannam@227: while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { cannam@227: processBlock(allFeatureSets); cannam@227: } cannam@227: cannam@227: // pad any last samples remaining and process cannam@227: if (m_queue[0]->getReadSpace() > 0) { cannam@227: for (size_t i = 0; i < m_channels; ++i) { cannam@227: m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace()); cannam@227: } cannam@227: processBlock(allFeatureSets); cannam@227: } cannam@227: cannam@227: // get remaining features cannam@227: cannam@227: FeatureSet featureSet = m_plugin->getRemainingFeatures(); cannam@227: cannam@227: for (map::iterator iter = featureSet.begin(); cannam@227: iter != featureSet.end(); ++iter) { cannam@227: FeatureList featureList = iter->second; cannam@227: for (size_t i = 0; i < featureList.size(); ++i) { cannam@227: allFeatureSets[iter->first].push_back(featureList[i]); cannam@227: } cannam@227: } cannam@227: cannam@227: return allFeatureSets; cannam@227: } cannam@227: cannam@227: void cannam@227: PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets) cannam@227: { cannam@227: for (size_t i = 0; i < m_channels; ++i) { cannam@227: m_queue[i]->peek(m_buffers[i], m_blockSize); cannam@227: } cannam@227: cannam@227: long frame = m_frame; cannam@227: RealTime timestamp = RealTime::frame2RealTime cannam@227: (frame, int(m_inputSampleRate + 0.5)); cannam@227: cannam@227: FeatureSet featureSet = m_plugin->process(m_buffers, timestamp); cannam@227: cannam@227: for (FeatureSet::iterator iter = featureSet.begin(); cannam@227: iter != featureSet.end(); ++iter) { cannam@227: cannam@227: int outputNo = iter->first; cannam@227: cannam@227: if (m_rewriteOutputTimes[outputNo]) { cannam@227: cannam@227: FeatureList featureList = iter->second; cannam@227: cannam@227: for (size_t i = 0; i < featureList.size(); ++i) { cannam@227: cannam@227: switch (m_outputs[outputNo].sampleType) { cannam@227: cannam@227: case OutputDescriptor::OneSamplePerStep: cannam@227: // use our internal timestamp, always cannam@227: featureList[i].timestamp = timestamp; cannam@227: featureList[i].hasTimestamp = true; cannam@227: break; cannam@227: cannam@227: case OutputDescriptor::FixedSampleRate: cannam@227: // use our internal timestamp if feature lacks one cannam@227: if (!featureList[i].hasTimestamp) { cannam@227: featureList[i].timestamp = timestamp; cannam@227: featureList[i].hasTimestamp = true; cannam@227: } cannam@227: break; cannam@227: cannam@227: case OutputDescriptor::VariableSampleRate: cannam@227: break; // plugin must set timestamp cannam@227: cannam@227: default: cannam@227: break; cannam@227: } cannam@227: cannam@227: allFeatureSets[outputNo].push_back(featureList[i]); cannam@227: } cannam@227: } else { cannam@227: for (size_t i = 0; i < iter->second.size(); ++i) { cannam@227: allFeatureSets[outputNo].push_back(iter->second[i]); cannam@227: } cannam@227: } cannam@227: } cannam@227: cannam@227: // step forward cannam@227: cannam@227: for (size_t i = 0; i < m_channels; ++i) { cannam@227: m_queue[i]->skip(m_stepSize); cannam@227: } cannam@227: cannam@227: // increment internal frame counter each time we step forward cannam@227: m_frame += m_stepSize; cannam@227: } cannam@227: cannam@227: } cannam@227: cannam@227: } cannam@227: cannam@227: