annotate vamp-sdk/hostext/PluginInputDomainAdapter.cpp @ 211:caa9d07bb9bd

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