1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
6
An API for audio analysis and feature extraction plugins.
8
Centre for Digital Music, Queen Mary, University of London.
9
Copyright 2006-2009 Chris Cannam and QMUL.
11
This file is based in part on Don Cross's public domain FFT
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:
22
The above copyright notice and this permission notice shall be
23
included in all copies or substantial portions of the Software.
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.
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
40
#include "vamp-hostsdk/PluginInputDomainAdapter.h"
46
* If you want to compile using FFTW instead of the built-in FFT
47
* implementation for the PluginInputDomainAdapter, define HAVE_FFTW3
50
* Be aware that FFTW is licensed under the GPL -- unlike this SDK,
51
* which is provided under a more liberal BSD license in order to
52
* permit use in closed source applications. The use of FFTW would
53
* mean that your code would need to be licensed under the GPL as
54
* well. Do not define this symbol unless you understand and accept
55
* the implications of this.
57
* Parties such as Linux distribution packagers who redistribute this
58
* SDK for use in other programs should _not_ define this symbol, as
59
* it would change the effective licensing terms under which the SDK
60
* was available to third party developers.
62
* The default is not to use FFTW, and to use the built-in FFT instead.
64
* Note: The FFTW code uses FFTW_MEASURE, and so will perform badly on
65
* its first invocation unless the host has saved and restored FFTW
66
* wisdom (see the FFTW documentation).
73
_VAMP_SDK_HOSTSPACE_BEGIN(PluginInputDomainAdapter.cpp)
79
class PluginInputDomainAdapter::Impl
82
Impl(Plugin *plugin, float inputSampleRate);
85
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
88
size_t getPreferredStepSize() const;
89
size_t getPreferredBlockSize() const;
91
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
93
void setProcessTimestampMethod(ProcessTimestampMethod m);
94
ProcessTimestampMethod getProcessTimestampMethod() const;
96
RealTime getTimestampAdjustment() const;
100
float m_inputSampleRate;
109
ProcessTimestampMethod m_method;
111
float **m_shiftBuffers;
115
fftw_complex *m_cbuf;
119
void fft(unsigned int n, bool inverse,
120
double *ri, double *ii, double *ro, double *io);
123
FeatureSet processShiftingTimestamp(const float *const *inputBuffers, RealTime timestamp);
124
FeatureSet processShiftingData(const float *const *inputBuffers, RealTime timestamp);
126
size_t makeBlockSizeAcceptable(size_t) const;
129
PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) :
130
PluginWrapper(plugin)
132
m_impl = new Impl(plugin, m_inputSampleRate);
135
PluginInputDomainAdapter::~PluginInputDomainAdapter()
141
PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
143
return m_impl->initialise(channels, stepSize, blockSize);
147
PluginInputDomainAdapter::reset()
153
PluginInputDomainAdapter::getInputDomain() const
159
PluginInputDomainAdapter::getPreferredStepSize() const
161
return m_impl->getPreferredStepSize();
165
PluginInputDomainAdapter::getPreferredBlockSize() const
167
return m_impl->getPreferredBlockSize();
171
PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
173
return m_impl->process(inputBuffers, timestamp);
177
PluginInputDomainAdapter::setProcessTimestampMethod(ProcessTimestampMethod m)
179
m_impl->setProcessTimestampMethod(m);
182
PluginInputDomainAdapter::ProcessTimestampMethod
183
PluginInputDomainAdapter::getProcessTimestampMethod() const
185
return m_impl->getProcessTimestampMethod();
189
PluginInputDomainAdapter::getTimestampAdjustment() const
191
return m_impl->getTimestampAdjustment();
195
PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
197
m_inputSampleRate(inputSampleRate),
204
m_method(ShiftTimestamp),
217
PluginInputDomainAdapter::Impl::~Impl()
219
// the adapter will delete the plugin
221
if (m_shiftBuffers) {
222
for (int c = 0; c < m_channels; ++c) {
223
delete[] m_shiftBuffers[c];
225
delete[] m_shiftBuffers;
228
if (m_channels > 0) {
229
for (int c = 0; c < m_channels; ++c) {
230
delete[] m_freqbuf[c];
235
fftw_destroy_plan(m_plan);
249
// for some visual studii apparently
251
#define M_PI 3.14159265358979232846
255
PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
257
if (m_plugin->getInputDomain() == TimeDomain) {
259
m_stepSize = int(stepSize);
260
m_blockSize = int(blockSize);
261
m_channels = int(channels);
263
return m_plugin->initialise(channels, stepSize, blockSize);
267
std::cerr << "ERROR: PluginInputDomainAdapter::initialise: blocksize < 2 not supported" << std::endl;
271
if (blockSize & (blockSize-1)) {
272
std::cerr << "ERROR: PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << blockSize << " not supported" << std::endl;
276
if (m_channels > 0) {
277
for (int c = 0; c < m_channels; ++c) {
278
delete[] m_freqbuf[c];
283
fftw_destroy_plan(m_plan);
296
m_stepSize = int(stepSize);
297
m_blockSize = int(blockSize);
298
m_channels = int(channels);
300
m_freqbuf = new float *[m_channels];
301
for (int c = 0; c < m_channels; ++c) {
302
m_freqbuf[c] = new float[m_blockSize + 2];
304
m_window = new double[m_blockSize];
306
for (int i = 0; i < m_blockSize; ++i) {
308
m_window[i] = (0.50 - 0.50 * cos((2.0 * M_PI * i) / m_blockSize));
312
m_ri = (double *)fftw_malloc(blockSize * sizeof(double));
313
m_cbuf = (fftw_complex *)fftw_malloc((blockSize/2 + 1) * sizeof(fftw_complex));
314
m_plan = fftw_plan_dft_r2c_1d(blockSize, m_ri, m_cbuf, FFTW_MEASURE);
316
m_ri = new double[m_blockSize];
317
m_ro = new double[m_blockSize];
318
m_io = new double[m_blockSize];
323
return m_plugin->initialise(channels, stepSize, blockSize);
327
PluginInputDomainAdapter::Impl::reset()
334
PluginInputDomainAdapter::Impl::getPreferredStepSize() const
336
size_t step = m_plugin->getPreferredStepSize();
338
if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
339
step = getPreferredBlockSize() / 2;
346
PluginInputDomainAdapter::Impl::getPreferredBlockSize() const
348
size_t block = m_plugin->getPreferredBlockSize();
350
if (m_plugin->getInputDomain() == FrequencyDomain) {
354
block = makeBlockSizeAcceptable(block);
362
PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
366
std::cerr << "WARNING: PluginInputDomainAdapter::initialise: blocksize < 2 not" << std::endl
367
<< "supported, increasing from " << blockSize << " to 2" << std::endl;
370
} else if (blockSize & (blockSize-1)) {
373
// not an issue with FFTW
376
// not a power of two, can't handle that with our built-in FFT
379
size_t nearest = blockSize;
381
while (nearest > 1) {
391
if (blockSize - nearest > (nearest*2) - blockSize) {
395
std::cerr << "WARNING: PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << blockSize << " not supported, using blocksize " << nearest << " instead" << std::endl;
405
PluginInputDomainAdapter::Impl::getTimestampAdjustment() const
407
if (m_plugin->getInputDomain() == TimeDomain) {
408
return RealTime::zeroTime;
409
} else if (m_method == ShiftData || m_method == NoShift) {
410
return RealTime::zeroTime;
412
return RealTime::frame2RealTime
413
(m_blockSize/2, int(m_inputSampleRate + 0.5));
418
PluginInputDomainAdapter::Impl::setProcessTimestampMethod(ProcessTimestampMethod m)
423
PluginInputDomainAdapter::ProcessTimestampMethod
424
PluginInputDomainAdapter::Impl::getProcessTimestampMethod() const
430
PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
433
if (m_plugin->getInputDomain() == TimeDomain) {
434
return m_plugin->process(inputBuffers, timestamp);
437
if (m_method == ShiftTimestamp || m_method == NoShift) {
438
return processShiftingTimestamp(inputBuffers, timestamp);
440
return processShiftingData(inputBuffers, timestamp);
445
PluginInputDomainAdapter::Impl::processShiftingTimestamp(const float *const *inputBuffers,
448
if (m_method == ShiftTimestamp) {
449
timestamp = timestamp + getTimestampAdjustment();
452
for (int c = 0; c < m_channels; ++c) {
454
for (int i = 0; i < m_blockSize; ++i) {
455
m_ri[i] = double(inputBuffers[c][i]) * m_window[i];
458
for (int i = 0; i < m_blockSize/2; ++i) {
460
double value = m_ri[i];
461
m_ri[i] = m_ri[i + m_blockSize/2];
462
m_ri[i + m_blockSize/2] = value;
466
fftw_execute(m_plan);
468
for (int i = 0; i <= m_blockSize/2; ++i) {
469
m_freqbuf[c][i * 2] = float(m_cbuf[i][0]);
470
m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i][1]);
473
fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
475
for (int i = 0; i <= m_blockSize/2; ++i) {
476
m_freqbuf[c][i * 2] = float(m_ro[i]);
477
m_freqbuf[c][i * 2 + 1] = float(m_io[i]);
482
return m_plugin->process(m_freqbuf, timestamp);
486
PluginInputDomainAdapter::Impl::processShiftingData(const float *const *inputBuffers,
489
if (m_processCount == 0) {
490
if (!m_shiftBuffers) {
491
m_shiftBuffers = new float *[m_channels];
492
for (int c = 0; c < m_channels; ++c) {
493
m_shiftBuffers[c] = new float[m_blockSize + m_blockSize/2];
496
for (int c = 0; c < m_channels; ++c) {
497
for (int i = 0; i < m_blockSize + m_blockSize/2; ++i) {
498
m_shiftBuffers[c][i] = 0.f;
503
for (int c = 0; c < m_channels; ++c) {
504
for (int i = m_stepSize; i < m_blockSize + m_blockSize/2; ++i) {
505
m_shiftBuffers[c][i - m_stepSize] = m_shiftBuffers[c][i];
507
for (int i = 0; i < m_blockSize; ++i) {
508
m_shiftBuffers[c][i + m_blockSize/2] = inputBuffers[c][i];
512
for (int c = 0; c < m_channels; ++c) {
514
for (int i = 0; i < m_blockSize; ++i) {
515
m_ri[i] = double(m_shiftBuffers[c][i]) * m_window[i];
518
for (int i = 0; i < m_blockSize/2; ++i) {
520
double value = m_ri[i];
521
m_ri[i] = m_ri[i + m_blockSize/2];
522
m_ri[i + m_blockSize/2] = value;
526
fftw_execute(m_plan);
528
for (int i = 0; i <= m_blockSize/2; ++i) {
529
m_freqbuf[c][i * 2] = float(m_cbuf[i][0]);
530
m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i][1]);
533
fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
535
for (int i = 0; i <= m_blockSize/2; ++i) {
536
m_freqbuf[c][i * 2] = float(m_ro[i]);
537
m_freqbuf[c][i * 2 + 1] = float(m_io[i]);
544
return m_plugin->process(m_freqbuf, timestamp);
550
PluginInputDomainAdapter::Impl::fft(unsigned int n, bool inverse,
551
double *ri, double *ii, double *ro, double *io)
553
if (!ri || !ro || !io) return;
556
unsigned int i, j, k, m;
557
unsigned int blockSize, blockEnd;
562
if (n & (n-1)) return;
564
double angle = 2.0 * M_PI;
565
if (inverse) angle = -angle;
574
static unsigned int tableSize = 0;
575
static int *table = 0;
577
if (tableSize != n) {
583
for (i = 0; i < n; ++i) {
587
for (j = k = 0; j < bits; ++j) {
588
k = (k << 1) | (m & 1);
599
for (i = 0; i < n; ++i) {
600
ro[table[i]] = ri[i];
601
io[table[i]] = ii[i];
604
for (i = 0; i < n; ++i) {
605
ro[table[i]] = ri[i];
612
for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
614
double delta = angle / (double)blockSize;
615
double sm2 = -sin(-2 * delta);
616
double sm1 = -sin(-delta);
617
double cm2 = cos(-2 * delta);
618
double cm1 = cos(-delta);
622
for (i = 0; i < n; i += blockSize) {
630
for (j = i, m = 0; m < blockEnd; j++, m++) {
632
ar[0] = w * ar[1] - ar[2];
636
ai[0] = w * ai[1] - ai[2];
641
tr = ar[0] * ro[k] - ai[0] * io[k];
642
ti = ar[0] * io[k] + ai[0] * ro[k];
652
blockEnd = blockSize;
657
double denom = (double)n;
659
for (i = 0; i < n; i++) {
672
_VAMP_SDK_HOSTSPACE_END(PluginInputDomainAdapter.cpp)