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
)