annotate src/vamp-hostsdk/hostext/PluginInputDomainAdapter.cpp @ 228:e58242c9ff85 distinct-libraries

* more moving
author cannam
date Thu, 06 Nov 2008 14:21:59 +0000
parents 6b30e064cab7
children 5ee166dccfff
rev   line source
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