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

* PluginBufferingAdapter: Rewrite OneSamplePerStep to FixedSampleRate, not VariableSampleRate (so result is still dense); do not change FixedSampleRate to VariableSampleRate either; do not do the work of rewriting outputs that don't need to be rewritten
author cannam
date Thu, 20 Mar 2008 13:22:02 +0000
parents 08d8c8ee6097
children c1dce0b033cb
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@102 10 This file by Mark Levy and Chris Cannam.
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@104 60 void reset();
cannam@104 61
cannam@92 62 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
cannam@92 63
cannam@92 64 FeatureSet getRemainingFeatures();
cannam@92 65
cannam@92 66 protected:
cannam@102 67 class RingBuffer
cannam@102 68 {
cannam@102 69 public:
cannam@102 70 RingBuffer(int n) :
cannam@102 71 m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { }
cannam@102 72 virtual ~RingBuffer() { delete[] m_buffer; }
cannam@102 73
cannam@102 74 int getSize() const { return m_size-1; }
cannam@102 75 void reset() { m_writer = 0; m_reader = 0; }
cannam@102 76
cannam@102 77 int getReadSpace() const {
cannam@102 78 int writer = m_writer, reader = m_reader, space;
cannam@102 79 if (writer > reader) space = writer - reader;
cannam@102 80 else if (writer < reader) space = (writer + m_size) - reader;
cannam@102 81 else space = 0;
cannam@102 82 return space;
cannam@102 83 }
cannam@102 84
cannam@102 85 int getWriteSpace() const {
cannam@102 86 int writer = m_writer;
cannam@102 87 int reader = m_reader;
cannam@102 88 int space = (reader + m_size - writer - 1);
cannam@102 89 if (space >= m_size) space -= m_size;
cannam@102 90 return space;
cannam@102 91 }
cannam@102 92
cannam@102 93 int peek(float *destination, int n) const {
cannam@102 94
cannam@102 95 int available = getReadSpace();
cannam@102 96
cannam@102 97 if (n > available) {
cannam@102 98 for (int i = available; i < n; ++i) {
cannam@102 99 destination[i] = 0.f;
cannam@102 100 }
cannam@102 101 n = available;
cannam@102 102 }
cannam@102 103 if (n == 0) return n;
cannam@102 104
cannam@102 105 int reader = m_reader;
cannam@102 106 int here = m_size - reader;
cannam@102 107 const float *const bufbase = m_buffer + reader;
cannam@102 108
cannam@102 109 if (here >= n) {
cannam@102 110 for (int i = 0; i < n; ++i) {
cannam@102 111 destination[i] = bufbase[i];
cannam@102 112 }
cannam@102 113 } else {
cannam@102 114 for (int i = 0; i < here; ++i) {
cannam@102 115 destination[i] = bufbase[i];
cannam@102 116 }
cannam@102 117 float *const destbase = destination + here;
cannam@102 118 const int nh = n - here;
cannam@102 119 for (int i = 0; i < nh; ++i) {
cannam@102 120 destbase[i] = m_buffer[i];
cannam@102 121 }
cannam@102 122 }
cannam@102 123
cannam@102 124 return n;
cannam@102 125 }
cannam@102 126
cannam@102 127 int skip(int n) {
cannam@102 128
cannam@102 129 int available = getReadSpace();
cannam@102 130 if (n > available) {
cannam@102 131 n = available;
cannam@102 132 }
cannam@102 133 if (n == 0) return n;
cannam@102 134
cannam@102 135 int reader = m_reader;
cannam@102 136 reader += n;
cannam@102 137 while (reader >= m_size) reader -= m_size;
cannam@102 138 m_reader = reader;
cannam@102 139 return n;
cannam@102 140 }
cannam@102 141
cannam@102 142 int write(const float *source, int n) {
cannam@102 143
cannam@102 144 int available = getWriteSpace();
cannam@102 145 if (n > available) {
cannam@102 146 n = available;
cannam@102 147 }
cannam@102 148 if (n == 0) return n;
cannam@102 149
cannam@102 150 int writer = m_writer;
cannam@102 151 int here = m_size - writer;
cannam@102 152 float *const bufbase = m_buffer + writer;
cannam@102 153
cannam@102 154 if (here >= n) {
cannam@102 155 for (int i = 0; i < n; ++i) {
cannam@102 156 bufbase[i] = source[i];
cannam@102 157 }
cannam@102 158 } else {
cannam@102 159 for (int i = 0; i < here; ++i) {
cannam@102 160 bufbase[i] = source[i];
cannam@102 161 }
cannam@102 162 const int nh = n - here;
cannam@102 163 const float *const srcbase = source + here;
cannam@102 164 float *const buf = m_buffer;
cannam@102 165 for (int i = 0; i < nh; ++i) {
cannam@102 166 buf[i] = srcbase[i];
cannam@102 167 }
cannam@102 168 }
cannam@102 169
cannam@102 170 writer += n;
cannam@102 171 while (writer >= m_size) writer -= m_size;
cannam@102 172 m_writer = writer;
cannam@102 173
cannam@102 174 return n;
cannam@102 175 }
cannam@102 176
cannam@102 177 int zero(int n) {
cannam@102 178
cannam@102 179 int available = getWriteSpace();
cannam@102 180 if (n > available) {
cannam@102 181 n = available;
cannam@102 182 }
cannam@102 183 if (n == 0) return n;
cannam@102 184
cannam@102 185 int writer = m_writer;
cannam@102 186 int here = m_size - writer;
cannam@102 187 float *const bufbase = m_buffer + writer;
cannam@102 188
cannam@102 189 if (here >= n) {
cannam@102 190 for (int i = 0; i < n; ++i) {
cannam@102 191 bufbase[i] = 0.f;
cannam@102 192 }
cannam@102 193 } else {
cannam@102 194 for (int i = 0; i < here; ++i) {
cannam@102 195 bufbase[i] = 0.f;
cannam@102 196 }
cannam@102 197 const int nh = n - here;
cannam@102 198 for (int i = 0; i < nh; ++i) {
cannam@102 199 m_buffer[i] = 0.f;
cannam@102 200 }
cannam@102 201 }
cannam@102 202
cannam@102 203 writer += n;
cannam@102 204 while (writer >= m_size) writer -= m_size;
cannam@102 205 m_writer = writer;
cannam@102 206
cannam@102 207 return n;
cannam@102 208 }
cannam@102 209
cannam@102 210 protected:
cannam@102 211 float *m_buffer;
cannam@102 212 int m_writer;
cannam@102 213 int m_reader;
cannam@102 214 int m_size;
cannam@102 215
cannam@102 216 private:
cannam@102 217 RingBuffer(const RingBuffer &); // not provided
cannam@102 218 RingBuffer &operator=(const RingBuffer &); // not provided
cannam@102 219 };
cannam@102 220
cannam@92 221 Plugin *m_plugin;
cannam@92 222 size_t m_inputStepSize;
cannam@92 223 size_t m_inputBlockSize;
cannam@92 224 size_t m_stepSize;
cannam@92 225 size_t m_blockSize;
cannam@92 226 size_t m_channels;
cannam@102 227 vector<RingBuffer *> m_queue;
cannam@102 228 float **m_buffers;
cannam@92 229 float m_inputSampleRate;
cannam@104 230 RealTime m_timestamp;
cannam@104 231 bool m_unrun;
cannam@133 232 mutable OutputList m_outputs;
cannam@133 233 mutable std::map<int, bool> m_rewriteOutputTimes;
cannam@92 234
cannam@92 235 void processBlock(FeatureSet& allFeatureSets, RealTime timestamp);
cannam@92 236 };
cannam@92 237
cannam@92 238 PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) :
cannam@92 239 PluginWrapper(plugin)
cannam@92 240 {
cannam@92 241 m_impl = new Impl(plugin, m_inputSampleRate);
cannam@92 242 }
cannam@92 243
cannam@92 244 PluginBufferingAdapter::~PluginBufferingAdapter()
cannam@92 245 {
cannam@92 246 delete m_impl;
cannam@92 247 }
cannam@92 248
cannam@92 249 bool
cannam@92 250 PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@92 251 {
cannam@92 252 return m_impl->initialise(channels, stepSize, blockSize);
cannam@92 253 }
cannam@92 254
cannam@92 255 PluginBufferingAdapter::OutputList
cannam@92 256 PluginBufferingAdapter::getOutputDescriptors() const
cannam@92 257 {
cannam@92 258 return m_impl->getOutputDescriptors();
cannam@92 259 }
cannam@104 260
cannam@104 261 void
cannam@104 262 PluginBufferingAdapter::reset()
cannam@104 263 {
cannam@104 264 m_impl->reset();
cannam@104 265 }
cannam@92 266
cannam@92 267 PluginBufferingAdapter::FeatureSet
cannam@92 268 PluginBufferingAdapter::process(const float *const *inputBuffers,
cannam@92 269 RealTime timestamp)
cannam@92 270 {
cannam@92 271 return m_impl->process(inputBuffers, timestamp);
cannam@92 272 }
cannam@92 273
cannam@92 274 PluginBufferingAdapter::FeatureSet
cannam@92 275 PluginBufferingAdapter::getRemainingFeatures()
cannam@92 276 {
cannam@92 277 return m_impl->getRemainingFeatures();
cannam@92 278 }
cannam@92 279
cannam@92 280 PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
cannam@92 281 m_plugin(plugin),
cannam@92 282 m_inputStepSize(0),
cannam@92 283 m_inputBlockSize(0),
cannam@92 284 m_stepSize(0),
cannam@92 285 m_blockSize(0),
cannam@92 286 m_channels(0),
cannam@102 287 m_queue(0),
cannam@92 288 m_buffers(0),
cannam@92 289 m_inputSampleRate(inputSampleRate),
cannam@104 290 m_timestamp(RealTime::zeroTime),
cannam@104 291 m_unrun(true)
cannam@92 292 {
cannam@133 293 (void)getOutputDescriptors(); // set up m_outputs and m_rewriteOutputTimes
cannam@92 294 }
cannam@92 295
cannam@92 296 PluginBufferingAdapter::Impl::~Impl()
cannam@92 297 {
cannam@92 298 // the adapter will delete the plugin
cannam@102 299
cannam@102 300 for (size_t i = 0; i < m_channels; ++i) {
cannam@102 301 delete m_queue[i];
cannam@102 302 delete[] m_buffers[i];
cannam@102 303 }
cannam@102 304 delete[] m_buffers;
cannam@92 305 }
cannam@92 306
cannam@92 307 size_t
cannam@92 308 PluginBufferingAdapter::getPreferredStepSize() const
cannam@92 309 {
cannam@92 310 return getPreferredBlockSize();
cannam@92 311 }
cannam@92 312
cannam@92 313 bool
cannam@92 314 PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@92 315 {
cannam@92 316 if (stepSize != blockSize) {
cannam@92 317 std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl;
cannam@92 318 return false;
cannam@92 319 }
cannam@92 320
cannam@92 321 m_channels = channels;
cannam@92 322 m_inputStepSize = stepSize;
cannam@92 323 m_inputBlockSize = blockSize;
cannam@92 324
cannam@92 325 // use the step and block sizes which the plugin prefers
cannam@92 326 m_stepSize = m_plugin->getPreferredStepSize();
cannam@92 327 m_blockSize = m_plugin->getPreferredBlockSize();
cannam@92 328
cannam@92 329 // or sensible defaults if it has no preference
cannam@92 330 if (m_blockSize == 0) {
cannam@92 331 m_blockSize = 1024;
cannam@92 332 }
cannam@92 333 if (m_stepSize == 0) {
cannam@92 334 if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
cannam@92 335 m_stepSize = m_blockSize/2;
cannam@92 336 } else {
cannam@92 337 m_stepSize = m_blockSize;
cannam@92 338 }
cannam@92 339 } else if (m_stepSize > m_blockSize) {
cannam@92 340 if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
cannam@92 341 m_blockSize = m_stepSize * 2;
cannam@92 342 } else {
cannam@92 343 m_blockSize = m_stepSize;
cannam@92 344 }
cannam@92 345 }
cannam@92 346
cannam@92 347 std::cerr << "PluginBufferingAdapter::initialise: stepSize " << m_inputStepSize << " -> " << m_stepSize
cannam@92 348 << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;
cannam@92 349
cannam@92 350 // current implementation breaks if step is greater than block
cannam@92 351 if (m_stepSize > m_blockSize) {
cannam@92 352 std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl;
cannam@92 353 return false;
cannam@92 354 }
cannam@102 355
cannam@102 356 m_buffers = new float *[m_channels];
cannam@102 357
cannam@102 358 for (size_t i = 0; i < m_channels; ++i) {
cannam@102 359 m_queue.push_back(new RingBuffer(m_blockSize + m_inputBlockSize));
cannam@102 360 m_buffers[i] = new float[m_blockSize];
cannam@102 361 }
cannam@92 362
cannam@92 363 return m_plugin->initialise(m_channels, m_stepSize, m_blockSize);
cannam@92 364 }
cannam@92 365
cannam@92 366 PluginBufferingAdapter::OutputList
cannam@92 367 PluginBufferingAdapter::Impl::getOutputDescriptors() const
cannam@92 368 {
cannam@133 369 if (m_outputs.empty()) {
cannam@133 370 m_outputs = m_plugin->getOutputDescriptors();
cannam@133 371 }
cannam@133 372
cannam@133 373 PluginBufferingAdapter::OutputList outs;
cannam@133 374
cannam@92 375 for (size_t i = 0; i < outs.size(); ++i) {
cannam@133 376
cannam@133 377 switch (outs[i].sampleType) {
cannam@133 378
cannam@133 379 case OutputDescriptor::OneSamplePerStep:
cannam@133 380 outs[i].sampleType = OutputDescriptor::FixedSampleRate;
cannam@92 381 outs[i].sampleRate = 1.f / m_stepSize;
cannam@133 382 m_rewriteOutputTimes[i] = true;
cannam@133 383 break;
cannam@133 384
cannam@133 385 case OutputDescriptor::FixedSampleRate:
cannam@133 386 if (outs[i].sampleRate == 0.f) {
cannam@133 387 outs[i].sampleRate = 1.f / m_stepSize;
cannam@133 388 }
cannam@133 389 // We actually only need to rewrite output times for
cannam@133 390 // features that don't have timestamps already, but we
cannam@133 391 // can't tell from here whether our features will have
cannam@133 392 // timestamps or not
cannam@133 393 m_rewriteOutputTimes[i] = true;
cannam@133 394 break;
cannam@133 395
cannam@133 396 case OutputDescriptor::VariableSampleRate:
cannam@133 397 m_rewriteOutputTimes[i] = false;
cannam@133 398 break;
cannam@92 399 }
cannam@92 400 }
cannam@133 401
cannam@92 402 return outs;
cannam@92 403 }
cannam@92 404
cannam@104 405 void
cannam@104 406 PluginBufferingAdapter::Impl::reset()
cannam@104 407 {
cannam@104 408 m_timestamp = RealTime::zeroTime;
cannam@104 409 m_unrun = true;
cannam@104 410
cannam@104 411 for (size_t i = 0; i < m_queue.size(); ++i) {
cannam@104 412 m_queue[i]->reset();
cannam@104 413 }
cannam@104 414 }
cannam@104 415
cannam@92 416 PluginBufferingAdapter::FeatureSet
cannam@92 417 PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
cannam@92 418 RealTime timestamp)
cannam@92 419 {
cannam@92 420 FeatureSet allFeatureSets;
cannam@104 421
cannam@104 422 if (m_unrun) {
cannam@104 423 m_timestamp = timestamp;
cannam@104 424 m_unrun = false;
cannam@104 425 }
cannam@92 426
cannam@92 427 // queue the new input
cannam@92 428
cannam@102 429 for (size_t i = 0; i < m_channels; ++i) {
cannam@102 430 int written = m_queue[i]->write(inputBuffers[i], m_inputBlockSize);
cannam@102 431 if (written < int(m_inputBlockSize) && i == 0) {
cannam@102 432 std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: "
cannam@102 433 << "Buffer overflow: wrote " << written
cannam@102 434 << " of " << m_inputBlockSize
cannam@102 435 << " input samples (for plugin step size "
cannam@102 436 << m_stepSize << ", block size " << m_blockSize << ")"
cannam@102 437 << std::endl;
cannam@102 438 }
cannam@102 439 }
cannam@92 440
cannam@92 441 // process as much as we can
cannam@102 442
cannam@102 443 while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
cannam@92 444 processBlock(allFeatureSets, timestamp);
cannam@92 445 }
cannam@92 446
cannam@92 447 return allFeatureSets;
cannam@92 448 }
cannam@92 449
cannam@92 450 PluginBufferingAdapter::FeatureSet
cannam@92 451 PluginBufferingAdapter::Impl::getRemainingFeatures()
cannam@92 452 {
cannam@92 453 FeatureSet allFeatureSets;
cannam@92 454
cannam@92 455 // process remaining samples in queue
cannam@102 456 while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
cannam@92 457 processBlock(allFeatureSets, m_timestamp);
cannam@92 458 }
cannam@92 459
cannam@92 460 // pad any last samples remaining and process
cannam@102 461 if (m_queue[0]->getReadSpace() > 0) {
cannam@102 462 for (size_t i = 0; i < m_channels; ++i) {
cannam@102 463 m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace());
cannam@102 464 }
cannam@92 465 processBlock(allFeatureSets, m_timestamp);
cannam@92 466 }
cannam@92 467
cannam@92 468 // get remaining features
cannam@102 469
cannam@92 470 FeatureSet featureSet = m_plugin->getRemainingFeatures();
cannam@102 471
cannam@92 472 for (map<int, FeatureList>::iterator iter = featureSet.begin();
cannam@102 473 iter != featureSet.end(); ++iter) {
cannam@92 474 FeatureList featureList = iter->second;
cannam@102 475 for (size_t i = 0; i < featureList.size(); ++i) {
cannam@102 476 allFeatureSets[iter->first].push_back(featureList[i]);
cannam@102 477 }
cannam@92 478 }
cannam@92 479
cannam@92 480 return allFeatureSets;
cannam@92 481 }
cannam@92 482
cannam@92 483 void
cannam@102 484 PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets,
cannam@102 485 RealTime timestamp)
cannam@92 486 {
cannam@102 487 for (size_t i = 0; i < m_channels; ++i) {
cannam@102 488 m_queue[i]->peek(m_buffers[i], m_blockSize);
cannam@102 489 }
cannam@102 490
cannam@92 491 FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp);
cannam@92 492
cannam@133 493 for (FeatureSet::iterator iter = featureSet.begin();
cannam@102 494 iter != featureSet.end(); ++iter) {
cannam@133 495
cannam@133 496 int outputNo = iter->first;
cannam@133 497
cannam@133 498 if (m_rewriteOutputTimes[outputNo]) {
cannam@133 499
cannam@133 500 // Make sure the timestamp is always set
cannam@92 501
cannam@133 502 FeatureList featureList = iter->second;
cannam@92 503
cannam@133 504 for (size_t i = 0; i < featureList.size(); ++i) {
cannam@133 505
cannam@133 506 switch (m_outputs[outputNo].sampleType) {
cannam@133 507
cannam@133 508 case OutputDescriptor::OneSamplePerStep:
cannam@133 509 // use our internal timestamp, always
cannam@133 510 featureList[i].timestamp = m_timestamp;
cannam@133 511 featureList[i].hasTimestamp = true;
cannam@133 512 break;
cannam@133 513
cannam@133 514 case OutputDescriptor::FixedSampleRate:
cannam@133 515 // use our internal timestamp if feature lacks one
cannam@133 516 if (!featureList[i].hasTimestamp) {
cannam@133 517 featureList[i].timestamp = m_timestamp;
cannam@133 518 featureList[i].hasTimestamp = true;
cannam@133 519 }
cannam@133 520 break;
cannam@133 521
cannam@133 522 case OutputDescriptor::VariableSampleRate:
cannam@133 523 break; // plugin must set timestamp
cannam@133 524
cannam@133 525 default:
cannam@133 526 break;
cannam@133 527 }
cannam@92 528
cannam@133 529 allFeatureSets[outputNo].push_back(featureList[i]);
cannam@92 530 }
cannam@133 531 } else {
cannam@133 532 for (size_t i = 0; i < iter->second.size(); ++i) {
cannam@133 533 allFeatureSets[outputNo].push_back(iter->second[i]);
cannam@133 534 }
cannam@92 535 }
cannam@92 536 }
cannam@92 537
cannam@92 538 // step forward
cannam@102 539
cannam@102 540 for (size_t i = 0; i < m_channels; ++i) {
cannam@102 541 m_queue[i]->skip(m_stepSize);
cannam@102 542 }
cannam@92 543
cannam@92 544 // fake up the timestamp each time we step forward
cannam@102 545
cannam@102 546 long frame = RealTime::realTime2Frame(m_timestamp,
cannam@102 547 int(m_inputSampleRate + 0.5));
cannam@102 548 m_timestamp = RealTime::frame2RealTime(frame + m_stepSize,
cannam@102 549 int(m_inputSampleRate + 0.5));
cannam@92 550 }
cannam@92 551
cannam@92 552 }
cannam@92 553
cannam@92 554 }
cannam@92 555
cannam@92 556