1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Vamp
5 
6     An API for audio analysis and feature extraction plugins.
7 
8     Centre for Digital Music, Queen Mary, University of London.
9     Copyright 2006-2009 Chris Cannam and QMUL.
10     This file by Mark Levy and Chris Cannam, Copyright 2007-2009 QMUL.
11 
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:
19 
20     The above copyright notice and this permission notice shall be
21     included in all copies or substantial portions of the Software.
22 
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.
30 
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
35     authorization.
36 */
37 
38 #include <vector>
39 #include <map>
40 
41 #include <vamp-hostsdk/PluginBufferingAdapter.h>
42 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
43 
44 using std::vector;
45 using std::map;
46 
47 _VAMP_SDK_HOSTSPACE_BEGIN(PluginBufferingAdapter.cpp)
48 
49 namespace Vamp {
50 
51 namespace HostExt {
52 
53 class PluginBufferingAdapter::Impl
54 {
55 public:
56     Impl(Plugin *plugin, float inputSampleRate);
57     ~Impl();
58 
59     void setPluginStepSize(size_t stepSize);
60     void setPluginBlockSize(size_t blockSize);
61 
62     bool initialise(size_t channels, size_t stepSize, size_t blockSize);
63 
64     void getActualStepAndBlockSizes(size_t &stepSize, size_t &blockSize);
65 
66     OutputList getOutputDescriptors() const;
67 
68     void setParameter(std::string, float);
69     void selectProgram(std::string);
70 
71     void reset();
72 
73     FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
74 
75     FeatureSet getRemainingFeatures();
76 
77 protected:
78     class RingBuffer
79     {
80     public:
RingBuffer(int n)81         RingBuffer(int n) :
82             m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { }
~RingBuffer()83         virtual ~RingBuffer() { delete[] m_buffer; }
84 
getSize() const85         int getSize() const { return m_size-1; }
reset()86         void reset() { m_writer = 0; m_reader = 0; }
87 
getReadSpace() const88         int getReadSpace() const {
89             int writer = m_writer, reader = m_reader, space;
90             if (writer > reader) space = writer - reader;
91             else if (writer < reader) space = (writer + m_size) - reader;
92             else space = 0;
93             return space;
94         }
95 
getWriteSpace() const96         int getWriteSpace() const {
97             int writer = m_writer;
98             int reader = m_reader;
99             int space = (reader + m_size - writer - 1);
100             if (space >= m_size) space -= m_size;
101             return space;
102         }
103 
peek(float * destination,int n) const104         int peek(float *destination, int n) const {
105 
106             int available = getReadSpace();
107 
108             if (n > available) {
109                 for (int i = available; i < n; ++i) {
110                     destination[i] = 0.f;
111                 }
112                 n = available;
113             }
114             if (n == 0) return n;
115 
116             int reader = m_reader;
117             int here = m_size - reader;
118             const float *const bufbase = m_buffer + reader;
119 
120             if (here >= n) {
121                 for (int i = 0; i < n; ++i) {
122                     destination[i] = bufbase[i];
123                 }
124             } else {
125                 for (int i = 0; i < here; ++i) {
126                     destination[i] = bufbase[i];
127                 }
128                 float *const destbase = destination + here;
129                 const int nh = n - here;
130                 for (int i = 0; i < nh; ++i) {
131                     destbase[i] = m_buffer[i];
132                 }
133             }
134 
135             return n;
136         }
137 
skip(int n)138         int skip(int n) {
139 
140             int available = getReadSpace();
141             if (n > available) {
142                 n = available;
143             }
144             if (n == 0) return n;
145 
146             int reader = m_reader;
147             reader += n;
148             while (reader >= m_size) reader -= m_size;
149             m_reader = reader;
150             return n;
151         }
152 
write(const float * source,int n)153         int write(const float *source, int n) {
154 
155             int available = getWriteSpace();
156             if (n > available) {
157                 n = available;
158             }
159             if (n == 0) return n;
160 
161             int writer = m_writer;
162             int here = m_size - writer;
163             float *const bufbase = m_buffer + writer;
164 
165             if (here >= n) {
166                 for (int i = 0; i < n; ++i) {
167                     bufbase[i] = source[i];
168                 }
169             } else {
170                 for (int i = 0; i < here; ++i) {
171                     bufbase[i] = source[i];
172                 }
173                 const int nh = n - here;
174                 const float *const srcbase = source + here;
175                 float *const buf = m_buffer;
176                 for (int i = 0; i < nh; ++i) {
177                     buf[i] = srcbase[i];
178                 }
179             }
180 
181             writer += n;
182             while (writer >= m_size) writer -= m_size;
183             m_writer = writer;
184 
185             return n;
186         }
187 
zero(int n)188         int zero(int n) {
189 
190             int available = getWriteSpace();
191             if (n > available) {
192                 n = available;
193             }
194             if (n == 0) return n;
195 
196             int writer = m_writer;
197             int here = m_size - writer;
198             float *const bufbase = m_buffer + writer;
199 
200             if (here >= n) {
201                 for (int i = 0; i < n; ++i) {
202                     bufbase[i] = 0.f;
203                 }
204             } else {
205                 for (int i = 0; i < here; ++i) {
206                     bufbase[i] = 0.f;
207                 }
208                 const int nh = n - here;
209                 for (int i = 0; i < nh; ++i) {
210                     m_buffer[i] = 0.f;
211                 }
212             }
213 
214             writer += n;
215             while (writer >= m_size) writer -= m_size;
216             m_writer = writer;
217 
218             return n;
219         }
220 
221     protected:
222         float *m_buffer;
223         int    m_writer;
224         int    m_reader;
225         int    m_size;
226 
227     private:
228         RingBuffer(const RingBuffer &); // not provided
229         RingBuffer &operator=(const RingBuffer &); // not provided
230     };
231 
232     Plugin *m_plugin;
233     size_t m_inputStepSize;  // value passed to wrapper initialise()
234     size_t m_inputBlockSize; // value passed to wrapper initialise()
235     size_t m_setStepSize;    // value passed to setPluginStepSize()
236     size_t m_setBlockSize;   // value passed to setPluginBlockSize()
237     size_t m_stepSize;       // value actually used to initialise plugin
238     size_t m_blockSize;      // value actually used to initialise plugin
239     size_t m_channels;
240     vector<RingBuffer *> m_queue;
241     float **m_buffers;
242     float m_inputSampleRate;
243     long m_frame;
244     bool m_unrun;
245     mutable OutputList m_outputs;
246     mutable std::map<int, bool> m_rewriteOutputTimes;
247     std::map<int, int> m_fixedRateFeatureNos; // output no -> feature no
248 
249     void processBlock(FeatureSet& allFeatureSets);
250     void adjustFixedRateFeatureTime(int outputNo, Feature &);
251 };
252 
PluginBufferingAdapter(Plugin * plugin)253 PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) :
254     PluginWrapper(plugin)
255 {
256     m_impl = new Impl(plugin, m_inputSampleRate);
257 }
258 
~PluginBufferingAdapter()259 PluginBufferingAdapter::~PluginBufferingAdapter()
260 {
261     delete m_impl;
262 }
263 
264 size_t
getPreferredStepSize() const265 PluginBufferingAdapter::getPreferredStepSize() const
266 {
267     return getPreferredBlockSize();
268 }
269 
270 size_t
getPreferredBlockSize() const271 PluginBufferingAdapter::getPreferredBlockSize() const
272 {
273     return PluginWrapper::getPreferredBlockSize();
274 }
275 
276 size_t
getPluginPreferredStepSize() const277 PluginBufferingAdapter::getPluginPreferredStepSize() const
278 {
279     return PluginWrapper::getPreferredStepSize();
280 }
281 
282 size_t
getPluginPreferredBlockSize() const283 PluginBufferingAdapter::getPluginPreferredBlockSize() const
284 {
285     return PluginWrapper::getPreferredBlockSize();
286 }
287 
288 void
setPluginStepSize(size_t stepSize)289 PluginBufferingAdapter::setPluginStepSize(size_t stepSize)
290 {
291     m_impl->setPluginStepSize(stepSize);
292 }
293 
294 void
setPluginBlockSize(size_t blockSize)295 PluginBufferingAdapter::setPluginBlockSize(size_t blockSize)
296 {
297     m_impl->setPluginBlockSize(blockSize);
298 }
299 
300 void
getActualStepAndBlockSizes(size_t & stepSize,size_t & blockSize)301 PluginBufferingAdapter::getActualStepAndBlockSizes(size_t &stepSize,
302                                                    size_t &blockSize)
303 {
304     m_impl->getActualStepAndBlockSizes(stepSize, blockSize);
305 }
306 
307 bool
initialise(size_t channels,size_t stepSize,size_t blockSize)308 PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
309 {
310     return m_impl->initialise(channels, stepSize, blockSize);
311 }
312 
313 PluginBufferingAdapter::OutputList
getOutputDescriptors() const314 PluginBufferingAdapter::getOutputDescriptors() const
315 {
316     return m_impl->getOutputDescriptors();
317 }
318 
319 void
setParameter(std::string name,float value)320 PluginBufferingAdapter::setParameter(std::string name, float value)
321 {
322     m_impl->setParameter(name, value);
323 }
324 
325 void
selectProgram(std::string name)326 PluginBufferingAdapter::selectProgram(std::string name)
327 {
328     m_impl->selectProgram(name);
329 }
330 
331 void
reset()332 PluginBufferingAdapter::reset()
333 {
334     m_impl->reset();
335 }
336 
337 PluginBufferingAdapter::FeatureSet
process(const float * const * inputBuffers,RealTime timestamp)338 PluginBufferingAdapter::process(const float *const *inputBuffers,
339                                 RealTime timestamp)
340 {
341     return m_impl->process(inputBuffers, timestamp);
342 }
343 
344 PluginBufferingAdapter::FeatureSet
getRemainingFeatures()345 PluginBufferingAdapter::getRemainingFeatures()
346 {
347     return m_impl->getRemainingFeatures();
348 }
349 
Impl(Plugin * plugin,float inputSampleRate)350 PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
351     m_plugin(plugin),
352     m_inputStepSize(0),
353     m_inputBlockSize(0),
354     m_setStepSize(0),
355     m_setBlockSize(0),
356     m_stepSize(0),
357     m_blockSize(0),
358     m_channels(0),
359     m_queue(0),
360     m_buffers(0),
361     m_inputSampleRate(inputSampleRate),
362     m_frame(0),
363     m_unrun(true)
364 {
365     (void)getOutputDescriptors(); // set up m_outputs and m_rewriteOutputTimes
366 }
367 
~Impl()368 PluginBufferingAdapter::Impl::~Impl()
369 {
370     // the adapter will delete the plugin
371 
372     for (size_t i = 0; i < m_channels; ++i) {
373         delete m_queue[i];
374         delete[] m_buffers[i];
375     }
376     delete[] m_buffers;
377 }
378 
379 void
setPluginStepSize(size_t stepSize)380 PluginBufferingAdapter::Impl::setPluginStepSize(size_t stepSize)
381 {
382     if (m_inputStepSize != 0) {
383         std::cerr << "PluginBufferingAdapter::setPluginStepSize: ERROR: Cannot be called after initialise()" << std::endl;
384         return;
385     }
386     m_setStepSize = stepSize;
387 }
388 
389 void
setPluginBlockSize(size_t blockSize)390 PluginBufferingAdapter::Impl::setPluginBlockSize(size_t blockSize)
391 {
392     if (m_inputBlockSize != 0) {
393         std::cerr << "PluginBufferingAdapter::setPluginBlockSize: ERROR: Cannot be called after initialise()" << std::endl;
394         return;
395     }
396     m_setBlockSize = blockSize;
397 }
398 
399 void
getActualStepAndBlockSizes(size_t & stepSize,size_t & blockSize)400 PluginBufferingAdapter::Impl::getActualStepAndBlockSizes(size_t &stepSize,
401                                                          size_t &blockSize)
402 {
403     stepSize = m_stepSize;
404     blockSize = m_blockSize;
405 }
406 
407 bool
initialise(size_t channels,size_t stepSize,size_t blockSize)408 PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
409 {
410     if (stepSize != blockSize) {
411         std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl;
412         return false;
413     }
414 
415     m_channels = channels;
416     m_inputStepSize = stepSize;
417     m_inputBlockSize = blockSize;
418 
419     // if the user has requested particular step or block sizes, use
420     // those; otherwise use the step and block sizes which the plugin
421     // prefers
422 
423     m_stepSize = 0;
424     m_blockSize = 0;
425 
426     if (m_setStepSize > 0) {
427         m_stepSize = m_setStepSize;
428     }
429     if (m_setBlockSize > 0) {
430         m_blockSize = m_setBlockSize;
431     }
432 
433     if (m_stepSize == 0 && m_blockSize == 0) {
434         m_stepSize = m_plugin->getPreferredStepSize();
435         m_blockSize = m_plugin->getPreferredBlockSize();
436     }
437 
438     bool freq = (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain);
439 
440     // or sensible defaults if it has no preference
441     if (m_blockSize == 0) {
442         if (m_stepSize == 0) {
443             m_blockSize = 1024;
444             if (freq) {
445                 m_stepSize = m_blockSize / 2;
446             } else {
447                 m_stepSize = m_blockSize;
448             }
449         } else if (freq) {
450             m_blockSize = m_stepSize * 2;
451         } else {
452             m_blockSize = m_stepSize;
453         }
454     } else if (m_stepSize == 0) { // m_blockSize != 0 (that was handled above)
455         if (freq) {
456             m_stepSize = m_blockSize/2;
457         } else {
458             m_stepSize = m_blockSize;
459         }
460     }
461 
462     // current implementation breaks if step is greater than block
463     if (m_stepSize > m_blockSize) {
464         size_t newBlockSize;
465         if (freq) {
466             newBlockSize = m_stepSize * 2;
467         } else {
468             newBlockSize = m_stepSize;
469         }
470         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;
471         m_blockSize = newBlockSize;
472     }
473 
474 //    std::cerr << "PluginBufferingAdapter::initialise: NOTE: stepSize " << m_inputStepSize << " -> " << m_stepSize
475 //              << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;
476 
477     m_buffers = new float *[m_channels];
478 
479     for (size_t i = 0; i < m_channels; ++i) {
480         m_queue.push_back(new RingBuffer(m_blockSize + m_inputBlockSize));
481         m_buffers[i] = new float[m_blockSize];
482     }
483 
484     bool success = m_plugin->initialise(m_channels, m_stepSize, m_blockSize);
485 
486 //    std::cerr << "PluginBufferingAdapter::initialise: success = " << success << std::endl;
487 
488     if (success) {
489         // Re-query outputs; properties such as bin count may have
490         // changed on initialise
491         m_outputs.clear();
492         (void)getOutputDescriptors();
493     }
494 
495     return success;
496 }
497 
498 PluginBufferingAdapter::OutputList
getOutputDescriptors() const499 PluginBufferingAdapter::Impl::getOutputDescriptors() const
500 {
501     if (m_outputs.empty()) {
502 //    std::cerr << "PluginBufferingAdapter::getOutputDescriptors: querying anew" << std::endl;
503 
504         m_outputs = m_plugin->getOutputDescriptors();
505     }
506 
507     PluginBufferingAdapter::OutputList outs = m_outputs;
508 
509     for (size_t i = 0; i < outs.size(); ++i) {
510 
511         switch (outs[i].sampleType) {
512 
513         case OutputDescriptor::OneSamplePerStep:
514             outs[i].sampleType = OutputDescriptor::FixedSampleRate;
515             outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize;
516             m_rewriteOutputTimes[i] = true;
517             break;
518 
519         case OutputDescriptor::FixedSampleRate:
520             if (outs[i].sampleRate == 0.f) {
521                 outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize;
522             }
523             // We actually only need to rewrite output times for
524             // features that don't have timestamps already, but we
525             // can't tell from here whether our features will have
526             // timestamps or not
527             m_rewriteOutputTimes[i] = true;
528             break;
529 
530         case OutputDescriptor::VariableSampleRate:
531             m_rewriteOutputTimes[i] = false;
532             break;
533         }
534     }
535 
536     return outs;
537 }
538 
539 void
setParameter(std::string name,float value)540 PluginBufferingAdapter::Impl::setParameter(std::string name, float value)
541 {
542     m_plugin->setParameter(name, value);
543 
544     // Re-query outputs; properties such as bin count may have changed
545     m_outputs.clear();
546     (void)getOutputDescriptors();
547 }
548 
549 void
selectProgram(std::string name)550 PluginBufferingAdapter::Impl::selectProgram(std::string name)
551 {
552     m_plugin->selectProgram(name);
553 
554     // Re-query outputs; properties such as bin count may have changed
555     m_outputs.clear();
556     (void)getOutputDescriptors();
557 }
558 
559 void
reset()560 PluginBufferingAdapter::Impl::reset()
561 {
562     m_frame = 0;
563     m_unrun = true;
564 
565     for (size_t i = 0; i < m_queue.size(); ++i) {
566         m_queue[i]->reset();
567     }
568 
569     m_plugin->reset();
570 }
571 
572 PluginBufferingAdapter::FeatureSet
process(const float * const * inputBuffers,RealTime timestamp)573 PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
574                                       RealTime timestamp)
575 {
576     if (m_inputStepSize == 0) {
577         std::cerr << "PluginBufferingAdapter::process: ERROR: Plugin has not been initialised" << std::endl;
578         return FeatureSet();
579     }
580 
581     FeatureSet allFeatureSets;
582 
583     if (m_unrun) {
584         m_frame = RealTime::realTime2Frame(timestamp,
585                                            int(m_inputSampleRate + 0.5));
586         m_unrun = false;
587     }
588 
589     // queue the new input
590 
591     for (size_t i = 0; i < m_channels; ++i) {
592         int written = m_queue[i]->write(inputBuffers[i], m_inputBlockSize);
593         if (written < int(m_inputBlockSize) && i == 0) {
594             std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: "
595                       << "Buffer overflow: wrote " << written
596                       << " of " << m_inputBlockSize
597                       << " input samples (for plugin step size "
598                       << m_stepSize << ", block size " << m_blockSize << ")"
599                       << std::endl;
600         }
601     }
602 
603     // process as much as we can
604 
605     while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
606         processBlock(allFeatureSets);
607     }
608 
609     return allFeatureSets;
610 }
611 
612 void
adjustFixedRateFeatureTime(int outputNo,Feature & feature)613 PluginBufferingAdapter::Impl::adjustFixedRateFeatureTime(int outputNo,
614                                                          Feature &feature)
615 {
616     if (feature.hasTimestamp) {
617         double secs = feature.timestamp.sec;
618         secs += feature.timestamp.nsec / 1e9;
619         m_fixedRateFeatureNos[outputNo] =
620             int(secs * double(m_outputs[outputNo].sampleRate) + 0.5);
621     }
622 
623     feature.timestamp = RealTime::fromSeconds
624         (m_fixedRateFeatureNos[outputNo] / double(m_outputs[outputNo].sampleRate));
625 
626     feature.hasTimestamp = true;
627 
628     m_fixedRateFeatureNos[outputNo] = m_fixedRateFeatureNos[outputNo] + 1;
629 }
630 
631 PluginBufferingAdapter::FeatureSet
getRemainingFeatures()632 PluginBufferingAdapter::Impl::getRemainingFeatures()
633 {
634     FeatureSet allFeatureSets;
635 
636     // process remaining samples in queue
637     while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
638         processBlock(allFeatureSets);
639     }
640 
641     // pad any last samples remaining and process
642     if (m_queue[0]->getReadSpace() > 0) {
643         for (size_t i = 0; i < m_channels; ++i) {
644             m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace());
645         }
646         processBlock(allFeatureSets);
647     }
648 
649     // get remaining features
650 
651     FeatureSet featureSet = m_plugin->getRemainingFeatures();
652 
653     for (map<int, FeatureList>::iterator iter = featureSet.begin();
654          iter != featureSet.end(); ++iter) {
655 
656         int outputNo = iter->first;
657         FeatureList featureList = iter->second;
658 
659         for (size_t i = 0; i < featureList.size(); ++i) {
660 
661             if (m_outputs[outputNo].sampleType ==
662                 OutputDescriptor::FixedSampleRate) {
663                 adjustFixedRateFeatureTime(outputNo, featureList[i]);
664             }
665 
666             allFeatureSets[outputNo].push_back(featureList[i]);
667         }
668     }
669 
670     return allFeatureSets;
671 }
672 
673 void
processBlock(FeatureSet & allFeatureSets)674 PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets)
675 {
676     for (size_t i = 0; i < m_channels; ++i) {
677         m_queue[i]->peek(m_buffers[i], m_blockSize);
678     }
679 
680     long frame = m_frame;
681     RealTime timestamp = RealTime::frame2RealTime
682         (frame, int(m_inputSampleRate + 0.5));
683 
684     FeatureSet featureSet = m_plugin->process(m_buffers, timestamp);
685 
686     PluginWrapper *wrapper = dynamic_cast<PluginWrapper *>(m_plugin);
687     RealTime adjustment;
688     if (wrapper) {
689         PluginInputDomainAdapter *ida =
690             wrapper->getWrapper<PluginInputDomainAdapter>();
691         if (ida) adjustment = ida->getTimestampAdjustment();
692     }
693 
694     for (FeatureSet::iterator iter = featureSet.begin();
695          iter != featureSet.end(); ++iter) {
696 
697         int outputNo = iter->first;
698 
699         if (m_rewriteOutputTimes[outputNo]) {
700 
701             FeatureList featureList = iter->second;
702 
703             for (size_t i = 0; i < featureList.size(); ++i) {
704 
705                 switch (m_outputs[outputNo].sampleType) {
706 
707                 case OutputDescriptor::OneSamplePerStep:
708                     // use our internal timestamp, always
709                     featureList[i].timestamp = timestamp + adjustment;
710                     featureList[i].hasTimestamp = true;
711                     break;
712 
713                 case OutputDescriptor::FixedSampleRate:
714                     adjustFixedRateFeatureTime(outputNo, featureList[i]);
715                     break;
716 
717                 case OutputDescriptor::VariableSampleRate:
718                     // plugin must set timestamp
719                     break;
720 
721                 default:
722                     break;
723                 }
724 
725                 allFeatureSets[outputNo].push_back(featureList[i]);
726             }
727         } else {
728             for (size_t i = 0; i < iter->second.size(); ++i) {
729                 allFeatureSets[outputNo].push_back(iter->second[i]);
730             }
731         }
732     }
733 
734     // step forward
735 
736     for (size_t i = 0; i < m_channels; ++i) {
737         m_queue[i]->skip(m_stepSize);
738     }
739 
740     // increment internal frame counter each time we step forward
741     m_frame += m_stepSize;
742 }
743 
744 }
745 
746 }
747 
748 _VAMP_SDK_HOSTSPACE_END(PluginBufferingAdapter.cpp)
749