cannam@227
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@227
|
2
|
cannam@227
|
3 /*
|
cannam@227
|
4 Vamp
|
cannam@227
|
5
|
cannam@227
|
6 An API for audio analysis and feature extraction plugins.
|
cannam@227
|
7
|
cannam@227
|
8 Centre for Digital Music, Queen Mary, University of London.
|
cannam@227
|
9 Copyright 2006-2007 Chris Cannam and QMUL.
|
cannam@227
|
10
|
cannam@227
|
11 This file is based in part on Don Cross's public domain FFT
|
cannam@227
|
12 implementation.
|
cannam@227
|
13
|
cannam@227
|
14 Permission is hereby granted, free of charge, to any person
|
cannam@227
|
15 obtaining a copy of this software and associated documentation
|
cannam@227
|
16 files (the "Software"), to deal in the Software without
|
cannam@227
|
17 restriction, including without limitation the rights to use, copy,
|
cannam@227
|
18 modify, merge, publish, distribute, sublicense, and/or sell copies
|
cannam@227
|
19 of the Software, and to permit persons to whom the Software is
|
cannam@227
|
20 furnished to do so, subject to the following conditions:
|
cannam@227
|
21
|
cannam@227
|
22 The above copyright notice and this permission notice shall be
|
cannam@227
|
23 included in all copies or substantial portions of the Software.
|
cannam@227
|
24
|
cannam@227
|
25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@227
|
26 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@227
|
27 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
cannam@227
|
28 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
cannam@227
|
29 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@227
|
30 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@227
|
31 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@227
|
32
|
cannam@227
|
33 Except as contained in this notice, the names of the Centre for
|
cannam@227
|
34 Digital Music; Queen Mary, University of London; and Chris Cannam
|
cannam@227
|
35 shall not be used in advertising or otherwise to promote the sale,
|
cannam@227
|
36 use or other dealings in this Software without prior written
|
cannam@227
|
37 authorization.
|
cannam@227
|
38 */
|
cannam@227
|
39
|
cannam@227
|
40 #include "PluginInputDomainAdapter.h"
|
cannam@227
|
41
|
cannam@227
|
42 #include <cmath>
|
cannam@227
|
43
|
cannam@227
|
44
|
cannam@227
|
45 /**
|
cannam@227
|
46 * If you want to compile using FFTW instead of the built-in FFT
|
cannam@227
|
47 * implementation for the PluginInputDomainAdapter, define HAVE_FFTW3
|
cannam@227
|
48 * in the Makefile.
|
cannam@227
|
49 *
|
cannam@227
|
50 * Be aware that FFTW is licensed under the GPL -- unlike this SDK,
|
cannam@227
|
51 * which is provided under a more liberal BSD license in order to
|
cannam@227
|
52 * permit use in closed source applications. The use of FFTW would
|
cannam@227
|
53 * mean that your code would need to be licensed under the GPL as
|
cannam@227
|
54 * well. Do not define this symbol unless you understand and accept
|
cannam@227
|
55 * the implications of this.
|
cannam@227
|
56 *
|
cannam@227
|
57 * Parties such as Linux distribution packagers who redistribute this
|
cannam@227
|
58 * SDK for use in other programs should _not_ define this symbol, as
|
cannam@227
|
59 * it would change the effective licensing terms under which the SDK
|
cannam@227
|
60 * was available to third party developers.
|
cannam@227
|
61 *
|
cannam@227
|
62 * The default is not to use FFTW, and to use the built-in FFT instead.
|
cannam@227
|
63 *
|
cannam@227
|
64 * Note: The FFTW code uses FFTW_MEASURE, and so will perform badly on
|
cannam@227
|
65 * its first invocation unless the host has saved and restored FFTW
|
cannam@227
|
66 * wisdom (see the FFTW documentation).
|
cannam@227
|
67 */
|
cannam@227
|
68 #ifdef HAVE_FFTW3
|
cannam@227
|
69 #include <fftw3.h>
|
cannam@227
|
70 #endif
|
cannam@227
|
71
|
cannam@227
|
72
|
cannam@227
|
73 namespace Vamp {
|
cannam@227
|
74
|
cannam@227
|
75 namespace HostExt {
|
cannam@227
|
76
|
cannam@227
|
77 class PluginInputDomainAdapter::Impl
|
cannam@227
|
78 {
|
cannam@227
|
79 public:
|
cannam@227
|
80 Impl(Plugin *plugin, float inputSampleRate);
|
cannam@227
|
81 ~Impl();
|
cannam@227
|
82
|
cannam@227
|
83 bool initialise(size_t channels, size_t stepSize, size_t blockSize);
|
cannam@227
|
84
|
cannam@227
|
85 size_t getPreferredStepSize() const;
|
cannam@227
|
86 size_t getPreferredBlockSize() const;
|
cannam@227
|
87
|
cannam@227
|
88 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
|
cannam@227
|
89
|
cannam@227
|
90 RealTime getTimestampAdjustment() const;
|
cannam@227
|
91
|
cannam@227
|
92 protected:
|
cannam@227
|
93 Plugin *m_plugin;
|
cannam@227
|
94 float m_inputSampleRate;
|
cannam@227
|
95 int m_channels;
|
cannam@227
|
96 int m_blockSize;
|
cannam@227
|
97 float **m_freqbuf;
|
cannam@227
|
98
|
cannam@227
|
99 double *m_ri;
|
cannam@227
|
100 double *m_window;
|
cannam@227
|
101
|
cannam@227
|
102 #ifdef HAVE_FFTW3
|
cannam@227
|
103 fftw_plan m_plan;
|
cannam@227
|
104 fftw_complex *m_cbuf;
|
cannam@227
|
105 #else
|
cannam@227
|
106 double *m_ro;
|
cannam@227
|
107 double *m_io;
|
cannam@227
|
108 void fft(unsigned int n, bool inverse,
|
cannam@227
|
109 double *ri, double *ii, double *ro, double *io);
|
cannam@227
|
110 #endif
|
cannam@227
|
111
|
cannam@227
|
112 size_t makeBlockSizeAcceptable(size_t) const;
|
cannam@227
|
113 };
|
cannam@227
|
114
|
cannam@227
|
115 PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) :
|
cannam@227
|
116 PluginWrapper(plugin)
|
cannam@227
|
117 {
|
cannam@227
|
118 m_impl = new Impl(plugin, m_inputSampleRate);
|
cannam@227
|
119 }
|
cannam@227
|
120
|
cannam@227
|
121 PluginInputDomainAdapter::~PluginInputDomainAdapter()
|
cannam@227
|
122 {
|
cannam@227
|
123 delete m_impl;
|
cannam@227
|
124 }
|
cannam@227
|
125
|
cannam@227
|
126 bool
|
cannam@227
|
127 PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
cannam@227
|
128 {
|
cannam@227
|
129 return m_impl->initialise(channels, stepSize, blockSize);
|
cannam@227
|
130 }
|
cannam@227
|
131
|
cannam@227
|
132 Plugin::InputDomain
|
cannam@227
|
133 PluginInputDomainAdapter::getInputDomain() const
|
cannam@227
|
134 {
|
cannam@227
|
135 return TimeDomain;
|
cannam@227
|
136 }
|
cannam@227
|
137
|
cannam@227
|
138 size_t
|
cannam@227
|
139 PluginInputDomainAdapter::getPreferredStepSize() const
|
cannam@227
|
140 {
|
cannam@227
|
141 return m_impl->getPreferredStepSize();
|
cannam@227
|
142 }
|
cannam@227
|
143
|
cannam@227
|
144 size_t
|
cannam@227
|
145 PluginInputDomainAdapter::getPreferredBlockSize() const
|
cannam@227
|
146 {
|
cannam@227
|
147 return m_impl->getPreferredBlockSize();
|
cannam@227
|
148 }
|
cannam@227
|
149
|
cannam@227
|
150 Plugin::FeatureSet
|
cannam@227
|
151 PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
|
cannam@227
|
152 {
|
cannam@227
|
153 return m_impl->process(inputBuffers, timestamp);
|
cannam@227
|
154 }
|
cannam@227
|
155
|
cannam@227
|
156 RealTime
|
cannam@227
|
157 PluginInputDomainAdapter::getTimestampAdjustment() const
|
cannam@227
|
158 {
|
cannam@227
|
159 return m_impl->getTimestampAdjustment();
|
cannam@227
|
160 }
|
cannam@227
|
161
|
cannam@227
|
162
|
cannam@227
|
163 PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
|
cannam@227
|
164 m_plugin(plugin),
|
cannam@227
|
165 m_inputSampleRate(inputSampleRate),
|
cannam@227
|
166 m_channels(0),
|
cannam@227
|
167 m_blockSize(0),
|
cannam@227
|
168 m_freqbuf(0),
|
cannam@227
|
169 m_ri(0),
|
cannam@227
|
170 m_window(0),
|
cannam@227
|
171 #ifdef HAVE_FFTW3
|
cannam@227
|
172 m_plan(0),
|
cannam@227
|
173 m_cbuf(0)
|
cannam@227
|
174 #else
|
cannam@227
|
175 m_ro(0),
|
cannam@227
|
176 m_io(0)
|
cannam@227
|
177 #endif
|
cannam@227
|
178 {
|
cannam@227
|
179 }
|
cannam@227
|
180
|
cannam@227
|
181 PluginInputDomainAdapter::Impl::~Impl()
|
cannam@227
|
182 {
|
cannam@227
|
183 // the adapter will delete the plugin
|
cannam@227
|
184
|
cannam@227
|
185 if (m_channels > 0) {
|
cannam@227
|
186 for (int c = 0; c < m_channels; ++c) {
|
cannam@227
|
187 delete[] m_freqbuf[c];
|
cannam@227
|
188 }
|
cannam@227
|
189 delete[] m_freqbuf;
|
cannam@227
|
190 #ifdef HAVE_FFTW3
|
cannam@227
|
191 if (m_plan) {
|
cannam@227
|
192 fftw_destroy_plan(m_plan);
|
cannam@227
|
193 fftw_free(m_ri);
|
cannam@227
|
194 fftw_free(m_cbuf);
|
cannam@227
|
195 m_plan = 0;
|
cannam@227
|
196 }
|
cannam@227
|
197 #else
|
cannam@227
|
198 delete[] m_ri;
|
cannam@227
|
199 delete[] m_ro;
|
cannam@227
|
200 delete[] m_io;
|
cannam@227
|
201 #endif
|
cannam@227
|
202 delete[] m_window;
|
cannam@227
|
203 }
|
cannam@227
|
204 }
|
cannam@227
|
205
|
cannam@227
|
206 // for some visual studii apparently
|
cannam@227
|
207 #ifndef M_PI
|
cannam@227
|
208 #define M_PI 3.14159265358979232846
|
cannam@227
|
209 #endif
|
cannam@227
|
210
|
cannam@227
|
211 bool
|
cannam@227
|
212 PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
cannam@227
|
213 {
|
cannam@227
|
214 if (m_plugin->getInputDomain() == TimeDomain) {
|
cannam@227
|
215
|
cannam@227
|
216 m_blockSize = int(blockSize);
|
cannam@227
|
217 m_channels = int(channels);
|
cannam@227
|
218
|
cannam@227
|
219 return m_plugin->initialise(channels, stepSize, blockSize);
|
cannam@227
|
220 }
|
cannam@227
|
221
|
cannam@227
|
222 if (blockSize < 2) {
|
cannam@227
|
223 std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: blocksize < 2 not supported" << std::endl;
|
cannam@227
|
224 return false;
|
cannam@227
|
225 }
|
cannam@227
|
226
|
cannam@227
|
227 if (blockSize & (blockSize-1)) {
|
cannam@227
|
228 std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: non-power-of-two\nblocksize " << blockSize << " not supported" << std::endl;
|
cannam@227
|
229 return false;
|
cannam@227
|
230 }
|
cannam@227
|
231
|
cannam@227
|
232 if (m_channels > 0) {
|
cannam@227
|
233 for (int c = 0; c < m_channels; ++c) {
|
cannam@227
|
234 delete[] m_freqbuf[c];
|
cannam@227
|
235 }
|
cannam@227
|
236 delete[] m_freqbuf;
|
cannam@227
|
237 #ifdef HAVE_FFTW3
|
cannam@227
|
238 if (m_plan) {
|
cannam@227
|
239 fftw_destroy_plan(m_plan);
|
cannam@227
|
240 fftw_free(m_ri);
|
cannam@227
|
241 fftw_free(m_cbuf);
|
cannam@227
|
242 m_plan = 0;
|
cannam@227
|
243 }
|
cannam@227
|
244 #else
|
cannam@227
|
245 delete[] m_ri;
|
cannam@227
|
246 delete[] m_ro;
|
cannam@227
|
247 delete[] m_io;
|
cannam@227
|
248 #endif
|
cannam@227
|
249 delete[] m_window;
|
cannam@227
|
250 }
|
cannam@227
|
251
|
cannam@227
|
252 m_blockSize = int(blockSize);
|
cannam@227
|
253 m_channels = int(channels);
|
cannam@227
|
254
|
cannam@227
|
255 m_freqbuf = new float *[m_channels];
|
cannam@227
|
256 for (int c = 0; c < m_channels; ++c) {
|
cannam@227
|
257 m_freqbuf[c] = new float[m_blockSize + 2];
|
cannam@227
|
258 }
|
cannam@227
|
259 m_window = new double[m_blockSize];
|
cannam@227
|
260
|
cannam@227
|
261 for (int i = 0; i < m_blockSize; ++i) {
|
cannam@227
|
262 // Hanning window
|
cannam@227
|
263 m_window[i] = (0.50 - 0.50 * cos((2.0 * M_PI * i) / m_blockSize));
|
cannam@227
|
264 }
|
cannam@227
|
265
|
cannam@227
|
266 #ifdef HAVE_FFTW3
|
cannam@227
|
267 m_ri = (double *)fftw_malloc(blockSize * sizeof(double));
|
cannam@227
|
268 m_cbuf = (fftw_complex *)fftw_malloc((blockSize/2 + 1) * sizeof(fftw_complex));
|
cannam@227
|
269 m_plan = fftw_plan_dft_r2c_1d(blockSize, m_ri, m_cbuf, FFTW_MEASURE);
|
cannam@227
|
270 #else
|
cannam@227
|
271 m_ri = new double[m_blockSize];
|
cannam@227
|
272 m_ro = new double[m_blockSize];
|
cannam@227
|
273 m_io = new double[m_blockSize];
|
cannam@227
|
274 #endif
|
cannam@227
|
275
|
cannam@227
|
276 return m_plugin->initialise(channels, stepSize, blockSize);
|
cannam@227
|
277 }
|
cannam@227
|
278
|
cannam@227
|
279 size_t
|
cannam@227
|
280 PluginInputDomainAdapter::Impl::getPreferredStepSize() const
|
cannam@227
|
281 {
|
cannam@227
|
282 size_t step = m_plugin->getPreferredStepSize();
|
cannam@227
|
283
|
cannam@227
|
284 if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
|
cannam@227
|
285 step = getPreferredBlockSize() / 2;
|
cannam@227
|
286 }
|
cannam@227
|
287
|
cannam@227
|
288 return step;
|
cannam@227
|
289 }
|
cannam@227
|
290
|
cannam@227
|
291 size_t
|
cannam@227
|
292 PluginInputDomainAdapter::Impl::getPreferredBlockSize() const
|
cannam@227
|
293 {
|
cannam@227
|
294 size_t block = m_plugin->getPreferredBlockSize();
|
cannam@227
|
295
|
cannam@227
|
296 if (m_plugin->getInputDomain() == FrequencyDomain) {
|
cannam@227
|
297 if (block == 0) {
|
cannam@227
|
298 block = 1024;
|
cannam@227
|
299 } else {
|
cannam@227
|
300 block = makeBlockSizeAcceptable(block);
|
cannam@227
|
301 }
|
cannam@227
|
302 }
|
cannam@227
|
303
|
cannam@227
|
304 return block;
|
cannam@227
|
305 }
|
cannam@227
|
306
|
cannam@227
|
307 size_t
|
cannam@227
|
308 PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
|
cannam@227
|
309 {
|
cannam@227
|
310 if (blockSize < 2) {
|
cannam@227
|
311
|
cannam@227
|
312 std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: blocksize < 2 not" << std::endl
|
cannam@227
|
313 << "supported, increasing from " << blockSize << " to 2" << std::endl;
|
cannam@227
|
314 blockSize = 2;
|
cannam@227
|
315
|
cannam@227
|
316 } else if (blockSize & (blockSize-1)) {
|
cannam@227
|
317
|
cannam@227
|
318 #ifdef HAVE_FFTW3
|
cannam@227
|
319 // not an issue with FFTW
|
cannam@227
|
320 #else
|
cannam@227
|
321
|
cannam@227
|
322 // not a power of two, can't handle that with our built-in FFT
|
cannam@227
|
323 // implementation
|
cannam@227
|
324
|
cannam@227
|
325 size_t nearest = blockSize;
|
cannam@227
|
326 size_t power = 0;
|
cannam@227
|
327 while (nearest > 1) {
|
cannam@227
|
328 nearest >>= 1;
|
cannam@227
|
329 ++power;
|
cannam@227
|
330 }
|
cannam@227
|
331 nearest = 1;
|
cannam@227
|
332 while (power) {
|
cannam@227
|
333 nearest <<= 1;
|
cannam@227
|
334 --power;
|
cannam@227
|
335 }
|
cannam@227
|
336
|
cannam@227
|
337 if (blockSize - nearest > (nearest*2) - blockSize) {
|
cannam@227
|
338 nearest = nearest*2;
|
cannam@227
|
339 }
|
cannam@227
|
340
|
cannam@227
|
341 std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: non-power-of-two\nblocksize " << blockSize << " not supported, using blocksize " << nearest << " instead" << std::endl;
|
cannam@227
|
342 blockSize = nearest;
|
cannam@227
|
343
|
cannam@227
|
344 #endif
|
cannam@227
|
345 }
|
cannam@227
|
346
|
cannam@227
|
347 return blockSize;
|
cannam@227
|
348 }
|
cannam@227
|
349
|
cannam@227
|
350 RealTime
|
cannam@227
|
351 PluginInputDomainAdapter::Impl::getTimestampAdjustment() const
|
cannam@227
|
352 {
|
cannam@227
|
353 if (m_plugin->getInputDomain() == TimeDomain) {
|
cannam@227
|
354 return RealTime::zeroTime;
|
cannam@227
|
355 } else {
|
cannam@227
|
356 return RealTime::frame2RealTime
|
cannam@227
|
357 (m_blockSize/2, int(m_inputSampleRate + 0.5));
|
cannam@227
|
358 }
|
cannam@227
|
359 }
|
cannam@227
|
360
|
cannam@227
|
361 Plugin::FeatureSet
|
cannam@227
|
362 PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
|
cannam@227
|
363 RealTime timestamp)
|
cannam@227
|
364 {
|
cannam@227
|
365 if (m_plugin->getInputDomain() == TimeDomain) {
|
cannam@227
|
366 return m_plugin->process(inputBuffers, timestamp);
|
cannam@227
|
367 }
|
cannam@227
|
368
|
cannam@227
|
369 // The timestamp supplied should be (according to the Vamp::Plugin
|
cannam@227
|
370 // spec) the time of the start of the time-domain input block.
|
cannam@227
|
371 // However, we want to pass to the plugin an FFT output calculated
|
cannam@227
|
372 // from the block of samples _centred_ on that timestamp.
|
cannam@227
|
373 //
|
cannam@227
|
374 // We have two options:
|
cannam@227
|
375 //
|
cannam@227
|
376 // 1. Buffer the input, calculating the fft of the values at the
|
cannam@227
|
377 // passed-in block minus blockSize/2 rather than starting at the
|
cannam@227
|
378 // passed-in block. So each time we call process on the plugin,
|
cannam@227
|
379 // we are passing in the same timestamp as was passed to our own
|
cannam@227
|
380 // process plugin, but not (the frequency domain representation
|
cannam@227
|
381 // of) the same set of samples. Advantages: avoids confusion in
|
cannam@227
|
382 // the host by ensuring the returned values have timestamps
|
cannam@227
|
383 // comparable with that passed in to this function (in fact this
|
cannam@227
|
384 // is pretty much essential for one-value-per-block outputs);
|
cannam@227
|
385 // consistent with hosts such as SV that deal with the
|
cannam@227
|
386 // frequency-domain transform themselves. Disadvantages: means
|
cannam@227
|
387 // making the not necessarily correct assumption that the samples
|
cannam@227
|
388 // preceding the first official block are all zero (or some other
|
cannam@227
|
389 // known value).
|
cannam@227
|
390 //
|
cannam@227
|
391 // 2. Increase the passed-in timestamps by half the blocksize. So
|
cannam@227
|
392 // when we call process, we are passing in the frequency domain
|
cannam@227
|
393 // representation of the same set of samples as passed to us, but
|
cannam@227
|
394 // with a different timestamp. Advantages: simplicity; avoids
|
cannam@227
|
395 // iffy assumption mentioned above. Disadvantages: inconsistency
|
cannam@227
|
396 // with SV in cases where stepSize != blockSize/2; potential
|
cannam@227
|
397 // confusion arising from returned timestamps being calculated
|
cannam@227
|
398 // from the adjusted input timestamps rather than the original
|
cannam@227
|
399 // ones (and inaccuracy where the returned timestamp is implied,
|
cannam@227
|
400 // as in one-value-per-block).
|
cannam@227
|
401 //
|
cannam@227
|
402 // Neither way is ideal, but I don't think either is strictly
|
cannam@227
|
403 // incorrect either. I think this is just a case where the same
|
cannam@227
|
404 // plugin can legitimately produce differing results from the same
|
cannam@227
|
405 // input data, depending on how that data is packaged.
|
cannam@227
|
406 //
|
cannam@227
|
407 // We'll go for option 2, adjusting the timestamps. Note in
|
cannam@227
|
408 // particular that this means some results can differ from those
|
cannam@227
|
409 // produced by SV.
|
cannam@227
|
410
|
cannam@227
|
411 // std::cerr << "PluginInputDomainAdapter: sampleRate " << m_inputSampleRate << ", blocksize " << m_blockSize << ", adjusting time from " << timestamp;
|
cannam@227
|
412
|
cannam@227
|
413 timestamp = timestamp + getTimestampAdjustment();
|
cannam@227
|
414
|
cannam@227
|
415 // std::cerr << " to " << timestamp << std::endl;
|
cannam@227
|
416
|
cannam@227
|
417 for (int c = 0; c < m_channels; ++c) {
|
cannam@227
|
418
|
cannam@227
|
419 for (int i = 0; i < m_blockSize; ++i) {
|
cannam@227
|
420 m_ri[i] = double(inputBuffers[c][i]) * m_window[i];
|
cannam@227
|
421 }
|
cannam@227
|
422
|
cannam@227
|
423 for (int i = 0; i < m_blockSize/2; ++i) {
|
cannam@227
|
424 // FFT shift
|
cannam@227
|
425 double value = m_ri[i];
|
cannam@227
|
426 m_ri[i] = m_ri[i + m_blockSize/2];
|
cannam@227
|
427 m_ri[i + m_blockSize/2] = value;
|
cannam@227
|
428 }
|
cannam@227
|
429
|
cannam@227
|
430 #ifdef HAVE_FFTW3
|
cannam@227
|
431
|
cannam@227
|
432 fftw_execute(m_plan);
|
cannam@227
|
433
|
cannam@227
|
434 for (int i = 0; i <= m_blockSize/2; ++i) {
|
cannam@227
|
435 m_freqbuf[c][i * 2] = float(m_cbuf[i][0]);
|
cannam@227
|
436 m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i][1]);
|
cannam@227
|
437 }
|
cannam@227
|
438
|
cannam@227
|
439 #else
|
cannam@227
|
440
|
cannam@227
|
441 fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
|
cannam@227
|
442
|
cannam@227
|
443 for (int i = 0; i <= m_blockSize/2; ++i) {
|
cannam@227
|
444 m_freqbuf[c][i * 2] = float(m_ro[i]);
|
cannam@227
|
445 m_freqbuf[c][i * 2 + 1] = float(m_io[i]);
|
cannam@227
|
446 }
|
cannam@227
|
447
|
cannam@227
|
448 #endif
|
cannam@227
|
449 }
|
cannam@227
|
450
|
cannam@227
|
451 return m_plugin->process(m_freqbuf, timestamp);
|
cannam@227
|
452 }
|
cannam@227
|
453
|
cannam@227
|
454 #ifndef HAVE_FFTW3
|
cannam@227
|
455
|
cannam@227
|
456 void
|
cannam@227
|
457 PluginInputDomainAdapter::Impl::fft(unsigned int n, bool inverse,
|
cannam@227
|
458 double *ri, double *ii, double *ro, double *io)
|
cannam@227
|
459 {
|
cannam@227
|
460 if (!ri || !ro || !io) return;
|
cannam@227
|
461
|
cannam@227
|
462 unsigned int bits;
|
cannam@227
|
463 unsigned int i, j, k, m;
|
cannam@227
|
464 unsigned int blockSize, blockEnd;
|
cannam@227
|
465
|
cannam@227
|
466 double tr, ti;
|
cannam@227
|
467
|
cannam@227
|
468 if (n < 2) return;
|
cannam@227
|
469 if (n & (n-1)) return;
|
cannam@227
|
470
|
cannam@227
|
471 double angle = 2.0 * M_PI;
|
cannam@227
|
472 if (inverse) angle = -angle;
|
cannam@227
|
473
|
cannam@227
|
474 for (i = 0; ; ++i) {
|
cannam@227
|
475 if (n & (1 << i)) {
|
cannam@227
|
476 bits = i;
|
cannam@227
|
477 break;
|
cannam@227
|
478 }
|
cannam@227
|
479 }
|
cannam@227
|
480
|
cannam@227
|
481 static unsigned int tableSize = 0;
|
cannam@227
|
482 static int *table = 0;
|
cannam@227
|
483
|
cannam@227
|
484 if (tableSize != n) {
|
cannam@227
|
485
|
cannam@227
|
486 delete[] table;
|
cannam@227
|
487
|
cannam@227
|
488 table = new int[n];
|
cannam@227
|
489
|
cannam@227
|
490 for (i = 0; i < n; ++i) {
|
cannam@227
|
491
|
cannam@227
|
492 m = i;
|
cannam@227
|
493
|
cannam@227
|
494 for (j = k = 0; j < bits; ++j) {
|
cannam@227
|
495 k = (k << 1) | (m & 1);
|
cannam@227
|
496 m >>= 1;
|
cannam@227
|
497 }
|
cannam@227
|
498
|
cannam@227
|
499 table[i] = k;
|
cannam@227
|
500 }
|
cannam@227
|
501
|
cannam@227
|
502 tableSize = n;
|
cannam@227
|
503 }
|
cannam@227
|
504
|
cannam@227
|
505 if (ii) {
|
cannam@227
|
506 for (i = 0; i < n; ++i) {
|
cannam@227
|
507 ro[table[i]] = ri[i];
|
cannam@227
|
508 io[table[i]] = ii[i];
|
cannam@227
|
509 }
|
cannam@227
|
510 } else {
|
cannam@227
|
511 for (i = 0; i < n; ++i) {
|
cannam@227
|
512 ro[table[i]] = ri[i];
|
cannam@227
|
513 io[table[i]] = 0.0;
|
cannam@227
|
514 }
|
cannam@227
|
515 }
|
cannam@227
|
516
|
cannam@227
|
517 blockEnd = 1;
|
cannam@227
|
518
|
cannam@227
|
519 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
|
cannam@227
|
520
|
cannam@227
|
521 double delta = angle / (double)blockSize;
|
cannam@227
|
522 double sm2 = -sin(-2 * delta);
|
cannam@227
|
523 double sm1 = -sin(-delta);
|
cannam@227
|
524 double cm2 = cos(-2 * delta);
|
cannam@227
|
525 double cm1 = cos(-delta);
|
cannam@227
|
526 double w = 2 * cm1;
|
cannam@227
|
527 double ar[3], ai[3];
|
cannam@227
|
528
|
cannam@227
|
529 for (i = 0; i < n; i += blockSize) {
|
cannam@227
|
530
|
cannam@227
|
531 ar[2] = cm2;
|
cannam@227
|
532 ar[1] = cm1;
|
cannam@227
|
533
|
cannam@227
|
534 ai[2] = sm2;
|
cannam@227
|
535 ai[1] = sm1;
|
cannam@227
|
536
|
cannam@227
|
537 for (j = i, m = 0; m < blockEnd; j++, m++) {
|
cannam@227
|
538
|
cannam@227
|
539 ar[0] = w * ar[1] - ar[2];
|
cannam@227
|
540 ar[2] = ar[1];
|
cannam@227
|
541 ar[1] = ar[0];
|
cannam@227
|
542
|
cannam@227
|
543 ai[0] = w * ai[1] - ai[2];
|
cannam@227
|
544 ai[2] = ai[1];
|
cannam@227
|
545 ai[1] = ai[0];
|
cannam@227
|
546
|
cannam@227
|
547 k = j + blockEnd;
|
cannam@227
|
548 tr = ar[0] * ro[k] - ai[0] * io[k];
|
cannam@227
|
549 ti = ar[0] * io[k] + ai[0] * ro[k];
|
cannam@227
|
550
|
cannam@227
|
551 ro[k] = ro[j] - tr;
|
cannam@227
|
552 io[k] = io[j] - ti;
|
cannam@227
|
553
|
cannam@227
|
554 ro[j] += tr;
|
cannam@227
|
555 io[j] += ti;
|
cannam@227
|
556 }
|
cannam@227
|
557 }
|
cannam@227
|
558
|
cannam@227
|
559 blockEnd = blockSize;
|
cannam@227
|
560 }
|
cannam@227
|
561
|
cannam@227
|
562 if (inverse) {
|
cannam@227
|
563
|
cannam@227
|
564 double denom = (double)n;
|
cannam@227
|
565
|
cannam@227
|
566 for (i = 0; i < n; i++) {
|
cannam@227
|
567 ro[i] /= denom;
|
cannam@227
|
568 io[i] /= denom;
|
cannam@227
|
569 }
|
cannam@227
|
570 }
|
cannam@227
|
571 }
|
cannam@227
|
572
|
cannam@227
|
573 #endif
|
cannam@227
|
574
|
cannam@227
|
575 }
|
cannam@227
|
576
|
cannam@227
|
577 }
|
cannam@227
|
578
|