Mercurial > hg > vamp-plugin-sdk
comparison src/vamp-hostsdk/PluginBufferingAdapter.cpp @ 233:521734d2b498 distinct-libraries
* Flatten directory tree a bit, update doxygen
author | cannam |
---|---|
date | Fri, 07 Nov 2008 15:28:33 +0000 |
parents | |
children | 4454843ff384 |
comparison
equal
deleted
inserted
replaced
232:71ea10a3cbe7 | 233:521734d2b498 |
---|---|
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-2007 Chris Cannam and QMUL. | |
10 This file by Mark Levy and Chris Cannam, Copyright 2007-2008 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 | |
43 using std::vector; | |
44 using std::map; | |
45 | |
46 namespace Vamp { | |
47 | |
48 namespace HostExt { | |
49 | |
50 class PluginBufferingAdapter::Impl | |
51 { | |
52 public: | |
53 Impl(Plugin *plugin, float inputSampleRate); | |
54 ~Impl(); | |
55 | |
56 void setPluginStepSize(size_t stepSize); | |
57 void setPluginBlockSize(size_t blockSize); | |
58 | |
59 bool initialise(size_t channels, size_t stepSize, size_t blockSize); | |
60 | |
61 void getActualStepAndBlockSizes(size_t &stepSize, size_t &blockSize); | |
62 | |
63 OutputList getOutputDescriptors() const; | |
64 | |
65 void reset(); | |
66 | |
67 FeatureSet process(const float *const *inputBuffers, RealTime timestamp); | |
68 | |
69 FeatureSet getRemainingFeatures(); | |
70 | |
71 protected: | |
72 class RingBuffer | |
73 { | |
74 public: | |
75 RingBuffer(int n) : | |
76 m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { } | |
77 virtual ~RingBuffer() { delete[] m_buffer; } | |
78 | |
79 int getSize() const { return m_size-1; } | |
80 void reset() { m_writer = 0; m_reader = 0; } | |
81 | |
82 int getReadSpace() const { | |
83 int writer = m_writer, reader = m_reader, space; | |
84 if (writer > reader) space = writer - reader; | |
85 else if (writer < reader) space = (writer + m_size) - reader; | |
86 else space = 0; | |
87 return space; | |
88 } | |
89 | |
90 int getWriteSpace() const { | |
91 int writer = m_writer; | |
92 int reader = m_reader; | |
93 int space = (reader + m_size - writer - 1); | |
94 if (space >= m_size) space -= m_size; | |
95 return space; | |
96 } | |
97 | |
98 int peek(float *destination, int n) const { | |
99 | |
100 int available = getReadSpace(); | |
101 | |
102 if (n > available) { | |
103 for (int i = available; i < n; ++i) { | |
104 destination[i] = 0.f; | |
105 } | |
106 n = available; | |
107 } | |
108 if (n == 0) return n; | |
109 | |
110 int reader = m_reader; | |
111 int here = m_size - reader; | |
112 const float *const bufbase = m_buffer + reader; | |
113 | |
114 if (here >= n) { | |
115 for (int i = 0; i < n; ++i) { | |
116 destination[i] = bufbase[i]; | |
117 } | |
118 } else { | |
119 for (int i = 0; i < here; ++i) { | |
120 destination[i] = bufbase[i]; | |
121 } | |
122 float *const destbase = destination + here; | |
123 const int nh = n - here; | |
124 for (int i = 0; i < nh; ++i) { | |
125 destbase[i] = m_buffer[i]; | |
126 } | |
127 } | |
128 | |
129 return n; | |
130 } | |
131 | |
132 int skip(int n) { | |
133 | |
134 int available = getReadSpace(); | |
135 if (n > available) { | |
136 n = available; | |
137 } | |
138 if (n == 0) return n; | |
139 | |
140 int reader = m_reader; | |
141 reader += n; | |
142 while (reader >= m_size) reader -= m_size; | |
143 m_reader = reader; | |
144 return n; | |
145 } | |
146 | |
147 int write(const float *source, int n) { | |
148 | |
149 int available = getWriteSpace(); | |
150 if (n > available) { | |
151 n = available; | |
152 } | |
153 if (n == 0) return n; | |
154 | |
155 int writer = m_writer; | |
156 int here = m_size - writer; | |
157 float *const bufbase = m_buffer + writer; | |
158 | |
159 if (here >= n) { | |
160 for (int i = 0; i < n; ++i) { | |
161 bufbase[i] = source[i]; | |
162 } | |
163 } else { | |
164 for (int i = 0; i < here; ++i) { | |
165 bufbase[i] = source[i]; | |
166 } | |
167 const int nh = n - here; | |
168 const float *const srcbase = source + here; | |
169 float *const buf = m_buffer; | |
170 for (int i = 0; i < nh; ++i) { | |
171 buf[i] = srcbase[i]; | |
172 } | |
173 } | |
174 | |
175 writer += n; | |
176 while (writer >= m_size) writer -= m_size; | |
177 m_writer = writer; | |
178 | |
179 return n; | |
180 } | |
181 | |
182 int zero(int n) { | |
183 | |
184 int available = getWriteSpace(); | |
185 if (n > available) { | |
186 n = available; | |
187 } | |
188 if (n == 0) return n; | |
189 | |
190 int writer = m_writer; | |
191 int here = m_size - writer; | |
192 float *const bufbase = m_buffer + writer; | |
193 | |
194 if (here >= n) { | |
195 for (int i = 0; i < n; ++i) { | |
196 bufbase[i] = 0.f; | |
197 } | |
198 } else { | |
199 for (int i = 0; i < here; ++i) { | |
200 bufbase[i] = 0.f; | |
201 } | |
202 const int nh = n - here; | |
203 for (int i = 0; i < nh; ++i) { | |
204 m_buffer[i] = 0.f; | |
205 } | |
206 } | |
207 | |
208 writer += n; | |
209 while (writer >= m_size) writer -= m_size; | |
210 m_writer = writer; | |
211 | |
212 return n; | |
213 } | |
214 | |
215 protected: | |
216 float *m_buffer; | |
217 int m_writer; | |
218 int m_reader; | |
219 int m_size; | |
220 | |
221 private: | |
222 RingBuffer(const RingBuffer &); // not provided | |
223 RingBuffer &operator=(const RingBuffer &); // not provided | |
224 }; | |
225 | |
226 Plugin *m_plugin; | |
227 size_t m_inputStepSize; // value passed to wrapper initialise() | |
228 size_t m_inputBlockSize; // value passed to wrapper initialise() | |
229 size_t m_setStepSize; // value passed to setPluginStepSize() | |
230 size_t m_setBlockSize; // value passed to setPluginBlockSize() | |
231 size_t m_stepSize; // value actually used to initialise plugin | |
232 size_t m_blockSize; // value actually used to initialise plugin | |
233 size_t m_channels; | |
234 vector<RingBuffer *> m_queue; | |
235 float **m_buffers; | |
236 float m_inputSampleRate; | |
237 long m_frame; | |
238 bool m_unrun; | |
239 mutable OutputList m_outputs; | |
240 mutable std::map<int, bool> m_rewriteOutputTimes; | |
241 | |
242 void processBlock(FeatureSet& allFeatureSets); | |
243 }; | |
244 | |
245 PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) : | |
246 PluginWrapper(plugin) | |
247 { | |
248 m_impl = new Impl(plugin, m_inputSampleRate); | |
249 } | |
250 | |
251 PluginBufferingAdapter::~PluginBufferingAdapter() | |
252 { | |
253 delete m_impl; | |
254 } | |
255 | |
256 size_t | |
257 PluginBufferingAdapter::getPreferredStepSize() const | |
258 { | |
259 return getPreferredBlockSize(); | |
260 } | |
261 | |
262 size_t | |
263 PluginBufferingAdapter::getPreferredBlockSize() const | |
264 { | |
265 return PluginWrapper::getPreferredBlockSize(); | |
266 } | |
267 | |
268 size_t | |
269 PluginBufferingAdapter::getPluginPreferredStepSize() const | |
270 { | |
271 return PluginWrapper::getPreferredStepSize(); | |
272 } | |
273 | |
274 size_t | |
275 PluginBufferingAdapter::getPluginPreferredBlockSize() const | |
276 { | |
277 return PluginWrapper::getPreferredBlockSize(); | |
278 } | |
279 | |
280 void | |
281 PluginBufferingAdapter::setPluginStepSize(size_t stepSize) | |
282 { | |
283 m_impl->setPluginStepSize(stepSize); | |
284 } | |
285 | |
286 void | |
287 PluginBufferingAdapter::setPluginBlockSize(size_t blockSize) | |
288 { | |
289 m_impl->setPluginBlockSize(blockSize); | |
290 } | |
291 | |
292 void | |
293 PluginBufferingAdapter::getActualStepAndBlockSizes(size_t &stepSize, | |
294 size_t &blockSize) | |
295 { | |
296 m_impl->getActualStepAndBlockSizes(stepSize, blockSize); | |
297 } | |
298 | |
299 bool | |
300 PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize) | |
301 { | |
302 return m_impl->initialise(channels, stepSize, blockSize); | |
303 } | |
304 | |
305 PluginBufferingAdapter::OutputList | |
306 PluginBufferingAdapter::getOutputDescriptors() const | |
307 { | |
308 return m_impl->getOutputDescriptors(); | |
309 } | |
310 | |
311 void | |
312 PluginBufferingAdapter::reset() | |
313 { | |
314 m_impl->reset(); | |
315 } | |
316 | |
317 PluginBufferingAdapter::FeatureSet | |
318 PluginBufferingAdapter::process(const float *const *inputBuffers, | |
319 RealTime timestamp) | |
320 { | |
321 return m_impl->process(inputBuffers, timestamp); | |
322 } | |
323 | |
324 PluginBufferingAdapter::FeatureSet | |
325 PluginBufferingAdapter::getRemainingFeatures() | |
326 { | |
327 return m_impl->getRemainingFeatures(); | |
328 } | |
329 | |
330 PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : | |
331 m_plugin(plugin), | |
332 m_inputStepSize(0), | |
333 m_inputBlockSize(0), | |
334 m_setStepSize(0), | |
335 m_setBlockSize(0), | |
336 m_stepSize(0), | |
337 m_blockSize(0), | |
338 m_channels(0), | |
339 m_queue(0), | |
340 m_buffers(0), | |
341 m_inputSampleRate(inputSampleRate), | |
342 m_frame(0), | |
343 m_unrun(true) | |
344 { | |
345 (void)getOutputDescriptors(); // set up m_outputs and m_rewriteOutputTimes | |
346 } | |
347 | |
348 PluginBufferingAdapter::Impl::~Impl() | |
349 { | |
350 // the adapter will delete the plugin | |
351 | |
352 for (size_t i = 0; i < m_channels; ++i) { | |
353 delete m_queue[i]; | |
354 delete[] m_buffers[i]; | |
355 } | |
356 delete[] m_buffers; | |
357 } | |
358 | |
359 void | |
360 PluginBufferingAdapter::Impl::setPluginStepSize(size_t stepSize) | |
361 { | |
362 if (m_inputStepSize != 0) { | |
363 std::cerr << "PluginBufferingAdapter::setPluginStepSize: ERROR: Cannot be called after initialise()" << std::endl; | |
364 return; | |
365 } | |
366 m_setStepSize = stepSize; | |
367 } | |
368 | |
369 void | |
370 PluginBufferingAdapter::Impl::setPluginBlockSize(size_t blockSize) | |
371 { | |
372 if (m_inputBlockSize != 0) { | |
373 std::cerr << "PluginBufferingAdapter::setPluginBlockSize: ERROR: Cannot be called after initialise()" << std::endl; | |
374 return; | |
375 } | |
376 m_setBlockSize = blockSize; | |
377 } | |
378 | |
379 void | |
380 PluginBufferingAdapter::Impl::getActualStepAndBlockSizes(size_t &stepSize, | |
381 size_t &blockSize) | |
382 { | |
383 stepSize = m_stepSize; | |
384 blockSize = m_blockSize; | |
385 } | |
386 | |
387 bool | |
388 PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize) | |
389 { | |
390 if (stepSize != blockSize) { | |
391 std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl; | |
392 return false; | |
393 } | |
394 | |
395 m_channels = channels; | |
396 m_inputStepSize = stepSize; | |
397 m_inputBlockSize = blockSize; | |
398 | |
399 // if the user has requested particular step or block sizes, use | |
400 // those; otherwise use the step and block sizes which the plugin | |
401 // prefers | |
402 | |
403 m_stepSize = 0; | |
404 m_blockSize = 0; | |
405 | |
406 if (m_setStepSize > 0) { | |
407 m_stepSize = m_setStepSize; | |
408 } | |
409 if (m_setBlockSize > 0) { | |
410 m_blockSize = m_setBlockSize; | |
411 } | |
412 | |
413 if (m_stepSize == 0 && m_blockSize == 0) { | |
414 m_stepSize = m_plugin->getPreferredStepSize(); | |
415 m_blockSize = m_plugin->getPreferredBlockSize(); | |
416 } | |
417 | |
418 bool freq = (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain); | |
419 | |
420 // or sensible defaults if it has no preference | |
421 if (m_blockSize == 0) { | |
422 if (m_stepSize == 0) { | |
423 m_blockSize = 1024; | |
424 } else if (freq) { | |
425 m_blockSize = m_stepSize * 2; | |
426 } else { | |
427 m_blockSize = m_stepSize; | |
428 } | |
429 } else if (m_stepSize == 0) { // m_blockSize != 0 (that was handled above) | |
430 if (freq) { | |
431 m_stepSize = m_blockSize/2; | |
432 } else { | |
433 m_stepSize = m_blockSize; | |
434 } | |
435 } | |
436 | |
437 // current implementation breaks if step is greater than block | |
438 if (m_stepSize > m_blockSize) { | |
439 size_t newBlockSize; | |
440 if (freq) { | |
441 newBlockSize = m_stepSize * 2; | |
442 } else { | |
443 newBlockSize = m_stepSize; | |
444 } | |
445 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; | |
446 m_blockSize = newBlockSize; | |
447 } | |
448 | |
449 std::cerr << "PluginBufferingAdapter::initialise: NOTE: stepSize " << m_inputStepSize << " -> " << m_stepSize | |
450 << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl; | |
451 | |
452 m_buffers = new float *[m_channels]; | |
453 | |
454 for (size_t i = 0; i < m_channels; ++i) { | |
455 m_queue.push_back(new RingBuffer(m_blockSize + m_inputBlockSize)); | |
456 m_buffers[i] = new float[m_blockSize]; | |
457 } | |
458 | |
459 return m_plugin->initialise(m_channels, m_stepSize, m_blockSize); | |
460 } | |
461 | |
462 PluginBufferingAdapter::OutputList | |
463 PluginBufferingAdapter::Impl::getOutputDescriptors() const | |
464 { | |
465 if (m_outputs.empty()) { | |
466 m_outputs = m_plugin->getOutputDescriptors(); | |
467 } | |
468 | |
469 PluginBufferingAdapter::OutputList outs = m_outputs; | |
470 | |
471 for (size_t i = 0; i < outs.size(); ++i) { | |
472 | |
473 switch (outs[i].sampleType) { | |
474 | |
475 case OutputDescriptor::OneSamplePerStep: | |
476 outs[i].sampleType = OutputDescriptor::FixedSampleRate; | |
477 outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize; | |
478 m_rewriteOutputTimes[i] = true; | |
479 break; | |
480 | |
481 case OutputDescriptor::FixedSampleRate: | |
482 if (outs[i].sampleRate == 0.f) { | |
483 outs[i].sampleRate = (1.f / m_inputSampleRate) * m_stepSize; | |
484 } | |
485 // We actually only need to rewrite output times for | |
486 // features that don't have timestamps already, but we | |
487 // can't tell from here whether our features will have | |
488 // timestamps or not | |
489 m_rewriteOutputTimes[i] = true; | |
490 break; | |
491 | |
492 case OutputDescriptor::VariableSampleRate: | |
493 m_rewriteOutputTimes[i] = false; | |
494 break; | |
495 } | |
496 } | |
497 | |
498 return outs; | |
499 } | |
500 | |
501 void | |
502 PluginBufferingAdapter::Impl::reset() | |
503 { | |
504 m_frame = 0; | |
505 m_unrun = true; | |
506 | |
507 for (size_t i = 0; i < m_queue.size(); ++i) { | |
508 m_queue[i]->reset(); | |
509 } | |
510 | |
511 m_plugin->reset(); | |
512 } | |
513 | |
514 PluginBufferingAdapter::FeatureSet | |
515 PluginBufferingAdapter::Impl::process(const float *const *inputBuffers, | |
516 RealTime timestamp) | |
517 { | |
518 if (m_inputStepSize == 0) { | |
519 std::cerr << "PluginBufferingAdapter::process: ERROR: Plugin has not been initialised" << std::endl; | |
520 return FeatureSet(); | |
521 } | |
522 | |
523 FeatureSet allFeatureSets; | |
524 | |
525 if (m_unrun) { | |
526 m_frame = RealTime::realTime2Frame(timestamp, | |
527 int(m_inputSampleRate + 0.5)); | |
528 m_unrun = false; | |
529 } | |
530 | |
531 // queue the new input | |
532 | |
533 for (size_t i = 0; i < m_channels; ++i) { | |
534 int written = m_queue[i]->write(inputBuffers[i], m_inputBlockSize); | |
535 if (written < int(m_inputBlockSize) && i == 0) { | |
536 std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: " | |
537 << "Buffer overflow: wrote " << written | |
538 << " of " << m_inputBlockSize | |
539 << " input samples (for plugin step size " | |
540 << m_stepSize << ", block size " << m_blockSize << ")" | |
541 << std::endl; | |
542 } | |
543 } | |
544 | |
545 // process as much as we can | |
546 | |
547 while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { | |
548 processBlock(allFeatureSets); | |
549 } | |
550 | |
551 return allFeatureSets; | |
552 } | |
553 | |
554 PluginBufferingAdapter::FeatureSet | |
555 PluginBufferingAdapter::Impl::getRemainingFeatures() | |
556 { | |
557 FeatureSet allFeatureSets; | |
558 | |
559 // process remaining samples in queue | |
560 while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { | |
561 processBlock(allFeatureSets); | |
562 } | |
563 | |
564 // pad any last samples remaining and process | |
565 if (m_queue[0]->getReadSpace() > 0) { | |
566 for (size_t i = 0; i < m_channels; ++i) { | |
567 m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace()); | |
568 } | |
569 processBlock(allFeatureSets); | |
570 } | |
571 | |
572 // get remaining features | |
573 | |
574 FeatureSet featureSet = m_plugin->getRemainingFeatures(); | |
575 | |
576 for (map<int, FeatureList>::iterator iter = featureSet.begin(); | |
577 iter != featureSet.end(); ++iter) { | |
578 FeatureList featureList = iter->second; | |
579 for (size_t i = 0; i < featureList.size(); ++i) { | |
580 allFeatureSets[iter->first].push_back(featureList[i]); | |
581 } | |
582 } | |
583 | |
584 return allFeatureSets; | |
585 } | |
586 | |
587 void | |
588 PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets) | |
589 { | |
590 for (size_t i = 0; i < m_channels; ++i) { | |
591 m_queue[i]->peek(m_buffers[i], m_blockSize); | |
592 } | |
593 | |
594 long frame = m_frame; | |
595 RealTime timestamp = RealTime::frame2RealTime | |
596 (frame, int(m_inputSampleRate + 0.5)); | |
597 | |
598 FeatureSet featureSet = m_plugin->process(m_buffers, timestamp); | |
599 | |
600 for (FeatureSet::iterator iter = featureSet.begin(); | |
601 iter != featureSet.end(); ++iter) { | |
602 | |
603 int outputNo = iter->first; | |
604 | |
605 if (m_rewriteOutputTimes[outputNo]) { | |
606 | |
607 FeatureList featureList = iter->second; | |
608 | |
609 for (size_t i = 0; i < featureList.size(); ++i) { | |
610 | |
611 switch (m_outputs[outputNo].sampleType) { | |
612 | |
613 case OutputDescriptor::OneSamplePerStep: | |
614 // use our internal timestamp, always | |
615 featureList[i].timestamp = timestamp; | |
616 featureList[i].hasTimestamp = true; | |
617 break; | |
618 | |
619 case OutputDescriptor::FixedSampleRate: | |
620 // use our internal timestamp if feature lacks one | |
621 if (!featureList[i].hasTimestamp) { | |
622 featureList[i].timestamp = timestamp; | |
623 featureList[i].hasTimestamp = true; | |
624 } | |
625 break; | |
626 | |
627 case OutputDescriptor::VariableSampleRate: | |
628 break; // plugin must set timestamp | |
629 | |
630 default: | |
631 break; | |
632 } | |
633 | |
634 allFeatureSets[outputNo].push_back(featureList[i]); | |
635 } | |
636 } else { | |
637 for (size_t i = 0; i < iter->second.size(); ++i) { | |
638 allFeatureSets[outputNo].push_back(iter->second[i]); | |
639 } | |
640 } | |
641 } | |
642 | |
643 // step forward | |
644 | |
645 for (size_t i = 0; i < m_channels; ++i) { | |
646 m_queue[i]->skip(m_stepSize); | |
647 } | |
648 | |
649 // increment internal frame counter each time we step forward | |
650 m_frame += m_stepSize; | |
651 } | |
652 | |
653 } | |
654 | |
655 } | |
656 | |
657 |