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