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