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
11 This file is based in part on Don Cross's public domain FFT
12 implementation.
13
14 Permission is hereby granted, free of charge, to any person
15 obtaining a copy of this software and associated documentation
16 files (the "Software"), to deal in the Software without
17 restriction, including without limitation the rights to use, copy,
18 modify, merge, publish, distribute, sublicense, and/or sell copies
19 of the Software, and to permit persons to whom the Software is
20 furnished to do so, subject to the following conditions:
21
22 The above copyright notice and this permission notice shall be
23 included in all copies or substantial portions of the Software.
24
25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
29 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
30 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
33 Except as contained in this notice, the names of the Centre for
34 Digital Music; Queen Mary, University of London; and Chris Cannam
35 shall not be used in advertising or otherwise to promote the sale,
36 use or other dealings in this Software without prior written
37 authorization.
38 */
39
40 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
41
42 #include <cmath>
43
44 #include "Window.h"
45
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <math.h>
49 #include <string.h>
50 #include <limits.h>
51
52 _VAMP_SDK_HOSTSPACE_BEGIN(PluginInputDomainAdapter.cpp)
53
54 #include "../vamp-sdk/FFTimpl.cpp"
55
56 namespace Vamp {
57
58 namespace HostExt {
59
60 class PluginInputDomainAdapter::Impl
61 {
62 public:
63 Impl(Plugin *plugin, float inputSampleRate);
64 ~Impl();
65
66 bool initialise(size_t channels, size_t stepSize, size_t blockSize);
67 void reset();
68
69 size_t getPreferredStepSize() const;
70 size_t getPreferredBlockSize() const;
71
72 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
73
74 void setProcessTimestampMethod(ProcessTimestampMethod m);
75 ProcessTimestampMethod getProcessTimestampMethod() const;
76
77 RealTime getTimestampAdjustment() const;
78
79 WindowType getWindowType() const;
80 void setWindowType(WindowType type);
81
82 protected:
83 Plugin *m_plugin;
84 float m_inputSampleRate;
85 int m_channels;
86 int m_stepSize;
87 int m_blockSize;
88 float **m_freqbuf;
89 Kiss::vamp_kiss_fft_scalar *m_ri;
90
91 WindowType m_windowType;
92 typedef Window<Kiss::vamp_kiss_fft_scalar> W;
93 W *m_window;
94
95 ProcessTimestampMethod m_method;
96 int m_processCount;
97 float **m_shiftBuffers;
98
99 Kiss::vamp_kiss_fftr_cfg m_cfg;
100 Kiss::vamp_kiss_fft_cpx *m_cbuf;
101
102 FeatureSet processShiftingTimestamp(const float *const *inputBuffers, RealTime timestamp);
103 FeatureSet processShiftingData(const float *const *inputBuffers, RealTime timestamp);
104
105 size_t makeBlockSizeAcceptable(size_t) const;
106
107 W::WindowType convertType(WindowType t) const;
108 };
109
PluginInputDomainAdapter(Plugin * plugin)110 PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) :
111 PluginWrapper(plugin)
112 {
113 m_impl = new Impl(plugin, m_inputSampleRate);
114 }
115
~PluginInputDomainAdapter()116 PluginInputDomainAdapter::~PluginInputDomainAdapter()
117 {
118 delete m_impl;
119 }
120
121 bool
initialise(size_t channels,size_t stepSize,size_t blockSize)122 PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
123 {
124 return m_impl->initialise(channels, stepSize, blockSize);
125 }
126
127 void
reset()128 PluginInputDomainAdapter::reset()
129 {
130 m_impl->reset();
131 }
132
133 Plugin::InputDomain
getInputDomain() const134 PluginInputDomainAdapter::getInputDomain() const
135 {
136 return TimeDomain;
137 }
138
139 size_t
getPreferredStepSize() const140 PluginInputDomainAdapter::getPreferredStepSize() const
141 {
142 return m_impl->getPreferredStepSize();
143 }
144
145 size_t
getPreferredBlockSize() const146 PluginInputDomainAdapter::getPreferredBlockSize() const
147 {
148 return m_impl->getPreferredBlockSize();
149 }
150
151 Plugin::FeatureSet
process(const float * const * inputBuffers,RealTime timestamp)152 PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
153 {
154 return m_impl->process(inputBuffers, timestamp);
155 }
156
157 void
setProcessTimestampMethod(ProcessTimestampMethod m)158 PluginInputDomainAdapter::setProcessTimestampMethod(ProcessTimestampMethod m)
159 {
160 m_impl->setProcessTimestampMethod(m);
161 }
162
163 PluginInputDomainAdapter::ProcessTimestampMethod
getProcessTimestampMethod() const164 PluginInputDomainAdapter::getProcessTimestampMethod() const
165 {
166 return m_impl->getProcessTimestampMethod();
167 }
168
169 RealTime
getTimestampAdjustment() const170 PluginInputDomainAdapter::getTimestampAdjustment() const
171 {
172 return m_impl->getTimestampAdjustment();
173 }
174
175 PluginInputDomainAdapter::WindowType
getWindowType() const176 PluginInputDomainAdapter::getWindowType() const
177 {
178 return m_impl->getWindowType();
179 }
180
181 void
setWindowType(WindowType w)182 PluginInputDomainAdapter::setWindowType(WindowType w)
183 {
184 m_impl->setWindowType(w);
185 }
186
187
Impl(Plugin * plugin,float inputSampleRate)188 PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
189 m_plugin(plugin),
190 m_inputSampleRate(inputSampleRate),
191 m_channels(0),
192 m_stepSize(0),
193 m_blockSize(0),
194 m_freqbuf(0),
195 m_ri(0),
196 m_windowType(HanningWindow),
197 m_window(0),
198 m_method(ShiftTimestamp),
199 m_processCount(0),
200 m_shiftBuffers(0),
201 m_cfg(0),
202 m_cbuf(0)
203 {
204 }
205
~Impl()206 PluginInputDomainAdapter::Impl::~Impl()
207 {
208 // the adapter will delete the plugin
209
210 if (m_shiftBuffers) {
211 for (int c = 0; c < m_channels; ++c) {
212 delete[] m_shiftBuffers[c];
213 }
214 delete[] m_shiftBuffers;
215 }
216
217 if (m_channels > 0) {
218 for (int c = 0; c < m_channels; ++c) {
219 delete[] m_freqbuf[c];
220 }
221 delete[] m_freqbuf;
222 delete[] m_ri;
223 if (m_cfg) {
224 Kiss::vamp_kiss_fftr_free(m_cfg);
225 m_cfg = 0;
226 delete[] m_cbuf;
227 m_cbuf = 0;
228 }
229 delete m_window;
230 }
231 }
232
233 // for some visual studii apparently
234 #ifndef M_PI
235 #define M_PI 3.14159265358979232846
236 #endif
237
238 bool
initialise(size_t channels,size_t stepSize,size_t blockSize)239 PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
240 {
241 if (m_plugin->getInputDomain() == TimeDomain) {
242
243 m_stepSize = int(stepSize);
244 m_blockSize = int(blockSize);
245 m_channels = int(channels);
246
247 return m_plugin->initialise(channels, stepSize, blockSize);
248 }
249
250 if (blockSize < 2) {
251 std::cerr << "ERROR: PluginInputDomainAdapter::initialise: blocksize < 2 not supported" << std::endl;
252 return false;
253 }
254
255 if (blockSize % 2) {
256 std::cerr << "ERROR: PluginInputDomainAdapter::initialise: odd blocksize " << blockSize << " not supported" << std::endl;
257 return false;
258 }
259
260 if (m_channels > 0) {
261 for (int c = 0; c < m_channels; ++c) {
262 delete[] m_freqbuf[c];
263 }
264 delete[] m_freqbuf;
265 delete[] m_ri;
266 if (m_cfg) {
267 Kiss::vamp_kiss_fftr_free(m_cfg);
268 m_cfg = 0;
269 delete[] m_cbuf;
270 m_cbuf = 0;
271 }
272 delete m_window;
273 }
274
275 m_stepSize = int(stepSize);
276 m_blockSize = int(blockSize);
277 m_channels = int(channels);
278
279 m_freqbuf = new float *[m_channels];
280 for (int c = 0; c < m_channels; ++c) {
281 m_freqbuf[c] = new float[m_blockSize + 2];
282 }
283 m_ri = new Kiss::vamp_kiss_fft_scalar[m_blockSize];
284
285 m_window = new W(convertType(m_windowType), m_blockSize);
286
287 m_cfg = Kiss::vamp_kiss_fftr_alloc(m_blockSize, false, 0, 0);
288 m_cbuf = new Kiss::vamp_kiss_fft_cpx[m_blockSize/2+1];
289
290 m_processCount = 0;
291
292 return m_plugin->initialise(channels, stepSize, m_blockSize);
293 }
294
295 void
reset()296 PluginInputDomainAdapter::Impl::reset()
297 {
298 m_processCount = 0;
299 m_plugin->reset();
300 }
301
302 size_t
getPreferredStepSize() const303 PluginInputDomainAdapter::Impl::getPreferredStepSize() const
304 {
305 size_t step = m_plugin->getPreferredStepSize();
306
307 if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
308 step = getPreferredBlockSize() / 2;
309 }
310
311 return step;
312 }
313
314 size_t
getPreferredBlockSize() const315 PluginInputDomainAdapter::Impl::getPreferredBlockSize() const
316 {
317 size_t block = m_plugin->getPreferredBlockSize();
318
319 if (m_plugin->getInputDomain() == FrequencyDomain) {
320 if (block == 0) {
321 block = 1024;
322 } else {
323 block = makeBlockSizeAcceptable(block);
324 }
325 }
326
327 return block;
328 }
329
330 size_t
makeBlockSizeAcceptable(size_t blockSize) const331 PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
332 {
333 if (blockSize < 2) {
334
335 std::cerr << "WARNING: PluginInputDomainAdapter::initialise: blocksize < 2 not" << std::endl
336 << "supported, increasing from " << blockSize << " to 2" << std::endl;
337 blockSize = 2;
338
339 } else if (blockSize % 2) {
340
341 std::cerr << "WARNING: PluginInputDomainAdapter::initialise: odd blocksize not" << std::endl
342 << "supported, increasing from " << blockSize << " to " << (blockSize+1) << std::endl;
343 blockSize = blockSize+1;
344 }
345
346 return blockSize;
347 }
348
349 RealTime
getTimestampAdjustment() const350 PluginInputDomainAdapter::Impl::getTimestampAdjustment() const
351 {
352 if (m_plugin->getInputDomain() == TimeDomain) {
353 return RealTime::zeroTime;
354 } else if (m_method == ShiftData || m_method == NoShift) {
355 return RealTime::zeroTime;
356 } else {
357 return RealTime::frame2RealTime
358 (m_blockSize/2, int(m_inputSampleRate + 0.5));
359 }
360 }
361
362 void
setProcessTimestampMethod(ProcessTimestampMethod m)363 PluginInputDomainAdapter::Impl::setProcessTimestampMethod(ProcessTimestampMethod m)
364 {
365 m_method = m;
366 }
367
368 PluginInputDomainAdapter::ProcessTimestampMethod
getProcessTimestampMethod() const369 PluginInputDomainAdapter::Impl::getProcessTimestampMethod() const
370 {
371 return m_method;
372 }
373
374 void
setWindowType(WindowType t)375 PluginInputDomainAdapter::Impl::setWindowType(WindowType t)
376 {
377 if (m_windowType == t) return;
378 m_windowType = t;
379 if (m_window) {
380 delete m_window;
381 m_window = new W(convertType(m_windowType), m_blockSize);
382 }
383 }
384
385 PluginInputDomainAdapter::WindowType
getWindowType() const386 PluginInputDomainAdapter::Impl::getWindowType() const
387 {
388 return m_windowType;
389 }
390
391 PluginInputDomainAdapter::Impl::W::WindowType
convertType(WindowType t) const392 PluginInputDomainAdapter::Impl::convertType(WindowType t) const
393 {
394 switch (t) {
395 case RectangularWindow:
396 return W::RectangularWindow;
397 case BartlettWindow:
398 return W::BartlettWindow;
399 case HammingWindow:
400 return W::HammingWindow;
401 case HanningWindow:
402 return W::HanningWindow;
403 case BlackmanWindow:
404 return W::BlackmanWindow;
405 case NuttallWindow:
406 return W::NuttallWindow;
407 case BlackmanHarrisWindow:
408 return W::BlackmanHarrisWindow;
409 default:
410 return W::HanningWindow;
411 }
412 }
413
414 Plugin::FeatureSet
process(const float * const * inputBuffers,RealTime timestamp)415 PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
416 RealTime timestamp)
417 {
418 if (m_plugin->getInputDomain() == TimeDomain) {
419 return m_plugin->process(inputBuffers, timestamp);
420 }
421
422 if (m_method == ShiftTimestamp || m_method == NoShift) {
423 return processShiftingTimestamp(inputBuffers, timestamp);
424 } else {
425 return processShiftingData(inputBuffers, timestamp);
426 }
427 }
428
429 Plugin::FeatureSet
processShiftingTimestamp(const float * const * inputBuffers,RealTime timestamp)430 PluginInputDomainAdapter::Impl::processShiftingTimestamp(const float *const *inputBuffers,
431 RealTime timestamp)
432 {
433 unsigned int roundedRate = 1;
434 if (m_inputSampleRate > 0.f) {
435 roundedRate = (unsigned int)round(m_inputSampleRate);
436 }
437
438 if (m_method == ShiftTimestamp) {
439 // we may need to add one nsec if timestamp +
440 // getTimestampAdjustment() rounds down
441 timestamp = timestamp + getTimestampAdjustment();
442 RealTime nsec(0, 1);
443 if (RealTime::realTime2Frame(timestamp, roundedRate) <
444 RealTime::realTime2Frame(timestamp + nsec, roundedRate)) {
445 timestamp = timestamp + nsec;
446 }
447 }
448
449 for (int c = 0; c < m_channels; ++c) {
450
451 m_window->cut(inputBuffers[c], m_ri);
452
453 for (int i = 0; i < m_blockSize/2; ++i) {
454 // FFT shift
455 Kiss::vamp_kiss_fft_scalar value = m_ri[i];
456 m_ri[i] = m_ri[i + m_blockSize/2];
457 m_ri[i + m_blockSize/2] = value;
458 }
459
460 Kiss::vamp_kiss_fftr(m_cfg, m_ri, m_cbuf);
461
462 for (int i = 0; i <= m_blockSize/2; ++i) {
463 m_freqbuf[c][i * 2] = float(m_cbuf[i].r);
464 m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i].i);
465 }
466 }
467
468 return m_plugin->process(m_freqbuf, timestamp);
469 }
470
471 Plugin::FeatureSet
processShiftingData(const float * const * inputBuffers,RealTime timestamp)472 PluginInputDomainAdapter::Impl::processShiftingData(const float *const *inputBuffers,
473 RealTime timestamp)
474 {
475 if (m_processCount == 0) {
476 if (!m_shiftBuffers) {
477 m_shiftBuffers = new float *[m_channels];
478 for (int c = 0; c < m_channels; ++c) {
479 m_shiftBuffers[c] = new float[m_blockSize + m_blockSize/2];
480 }
481 }
482 for (int c = 0; c < m_channels; ++c) {
483 for (int i = 0; i < m_blockSize + m_blockSize/2; ++i) {
484 m_shiftBuffers[c][i] = 0.f;
485 }
486 }
487 }
488
489 for (int c = 0; c < m_channels; ++c) {
490 for (int i = m_stepSize; i < m_blockSize + m_blockSize/2; ++i) {
491 m_shiftBuffers[c][i - m_stepSize] = m_shiftBuffers[c][i];
492 }
493 for (int i = 0; i < m_blockSize; ++i) {
494 m_shiftBuffers[c][i + m_blockSize/2] = inputBuffers[c][i];
495 }
496 }
497
498 for (int c = 0; c < m_channels; ++c) {
499
500 m_window->cut(m_shiftBuffers[c], m_ri);
501
502 for (int i = 0; i < m_blockSize/2; ++i) {
503 // FFT shift
504 Kiss::vamp_kiss_fft_scalar value = m_ri[i];
505 m_ri[i] = m_ri[i + m_blockSize/2];
506 m_ri[i + m_blockSize/2] = value;
507 }
508
509 Kiss::vamp_kiss_fftr(m_cfg, m_ri, m_cbuf);
510
511 for (int i = 0; i <= m_blockSize/2; ++i) {
512 m_freqbuf[c][i * 2] = float(m_cbuf[i].r);
513 m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i].i);
514 }
515 }
516
517 ++m_processCount;
518
519 return m_plugin->process(m_freqbuf, timestamp);
520 }
521
522 }
523
524 }
525
526 _VAMP_SDK_HOSTSPACE_END(PluginInputDomainAdapter.cpp)
527
528