annotate vamp-sdk/hostext/PluginInputDomainAdapter.cpp @ 108:1e7ab0399852

* marginal performance improvement through use of reserve() in PluginHostAdapter::convertFeatures
author cannam
date Fri, 08 Feb 2008 10:50:31 +0000
parents 2cb46126ef59
children 1982246a3902
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@70 89
cannam@70 90 protected:
cannam@70 91 Plugin *m_plugin;
cannam@70 92 float m_inputSampleRate;
cannam@101 93 int m_channels;
cannam@101 94 int m_blockSize;
cannam@70 95 float **m_freqbuf;
cannam@101 96
cannam@70 97 double *m_ri;
cannam@101 98 double *m_window;
cannam@101 99
cannam@101 100 #ifdef HAVE_FFTW3
cannam@101 101 fftw_plan m_plan;
cannam@101 102 fftw_complex *m_cbuf;
cannam@101 103 #else
cannam@70 104 double *m_ro;
cannam@70 105 double *m_io;
cannam@70 106 void fft(unsigned int n, bool inverse,
cannam@70 107 double *ri, double *ii, double *ro, double *io);
cannam@101 108 #endif
cannam@70 109
cannam@70 110 size_t makeBlockSizeAcceptable(size_t) const;
cannam@70 111 };
cannam@70 112
cannam@64 113 PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) :
cannam@70 114 PluginWrapper(plugin)
cannam@70 115 {
cannam@70 116 m_impl = new Impl(plugin, m_inputSampleRate);
cannam@70 117 }
cannam@70 118
cannam@70 119 PluginInputDomainAdapter::~PluginInputDomainAdapter()
cannam@70 120 {
cannam@70 121 delete m_impl;
cannam@70 122 }
cannam@70 123
cannam@70 124 bool
cannam@70 125 PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@70 126 {
cannam@70 127 return m_impl->initialise(channels, stepSize, blockSize);
cannam@70 128 }
cannam@70 129
cannam@70 130 Plugin::InputDomain
cannam@70 131 PluginInputDomainAdapter::getInputDomain() const
cannam@70 132 {
cannam@70 133 return TimeDomain;
cannam@70 134 }
cannam@70 135
cannam@70 136 size_t
cannam@70 137 PluginInputDomainAdapter::getPreferredStepSize() const
cannam@70 138 {
cannam@70 139 return m_impl->getPreferredStepSize();
cannam@70 140 }
cannam@70 141
cannam@70 142 size_t
cannam@70 143 PluginInputDomainAdapter::getPreferredBlockSize() const
cannam@70 144 {
cannam@70 145 return m_impl->getPreferredBlockSize();
cannam@70 146 }
cannam@70 147
cannam@70 148 Plugin::FeatureSet
cannam@70 149 PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
cannam@70 150 {
cannam@70 151 return m_impl->process(inputBuffers, timestamp);
cannam@70 152 }
cannam@70 153
cannam@92 154 PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
cannam@70 155 m_plugin(plugin),
cannam@70 156 m_inputSampleRate(inputSampleRate),
cannam@64 157 m_channels(0),
cannam@64 158 m_blockSize(0),
cannam@101 159 m_freqbuf(0),
cannam@101 160 m_ri(0),
cannam@101 161 m_window(0),
cannam@101 162 #ifdef HAVE_FFTW3
cannam@101 163 m_plan(0),
cannam@101 164 m_cbuf(0)
cannam@101 165 #else
cannam@101 166 m_ro(0),
cannam@101 167 m_io(0)
cannam@101 168 #endif
cannam@64 169 {
cannam@64 170 }
cannam@64 171
cannam@70 172 PluginInputDomainAdapter::Impl::~Impl()
cannam@64 173 {
cannam@70 174 // the adapter will delete the plugin
cannam@70 175
cannam@70 176 if (m_channels > 0) {
cannam@101 177 for (int c = 0; c < m_channels; ++c) {
cannam@70 178 delete[] m_freqbuf[c];
cannam@70 179 }
cannam@70 180 delete[] m_freqbuf;
cannam@101 181 #ifdef HAVE_FFTW3
cannam@101 182 if (m_plan) {
cannam@101 183 fftw_destroy_plan(m_plan);
cannam@101 184 fftw_free(m_ri);
cannam@101 185 fftw_free(m_cbuf);
cannam@101 186 m_plan = 0;
cannam@101 187 }
cannam@101 188 #else
cannam@70 189 delete[] m_ri;
cannam@70 190 delete[] m_ro;
cannam@70 191 delete[] m_io;
cannam@101 192 #endif
cannam@101 193 delete[] m_window;
cannam@70 194 }
cannam@64 195 }
cannam@101 196
cannam@101 197 // for some visual studii apparently
cannam@101 198 #ifndef M_PI
cannam@101 199 #define M_PI 3.14159265358979232846
cannam@101 200 #endif
cannam@64 201
cannam@64 202 bool
cannam@70 203 PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@64 204 {
cannam@64 205 if (m_plugin->getInputDomain() == TimeDomain) {
cannam@64 206
cannam@101 207 m_blockSize = int(blockSize);
cannam@101 208 m_channels = int(channels);
cannam@64 209
cannam@64 210 return m_plugin->initialise(channels, stepSize, blockSize);
cannam@64 211 }
cannam@64 212
cannam@64 213 if (blockSize < 2) {
cannam@70 214 std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: blocksize < 2 not supported" << std::endl;
cannam@64 215 return false;
cannam@64 216 }
cannam@64 217
cannam@64 218 if (blockSize & (blockSize-1)) {
cannam@70 219 std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: non-power-of-two\nblocksize " << blockSize << " not supported" << std::endl;
cannam@64 220 return false;
cannam@64 221 }
cannam@64 222
cannam@64 223 if (m_channels > 0) {
cannam@101 224 for (int c = 0; c < m_channels; ++c) {
cannam@64 225 delete[] m_freqbuf[c];
cannam@64 226 }
cannam@64 227 delete[] m_freqbuf;
cannam@101 228 #ifdef HAVE_FFTW3
cannam@101 229 if (m_plan) {
cannam@101 230 fftw_destroy_plan(m_plan);
cannam@101 231 fftw_free(m_ri);
cannam@101 232 fftw_free(m_cbuf);
cannam@101 233 m_plan = 0;
cannam@101 234 }
cannam@101 235 #else
cannam@64 236 delete[] m_ri;
cannam@64 237 delete[] m_ro;
cannam@64 238 delete[] m_io;
cannam@101 239 #endif
cannam@101 240 delete[] m_window;
cannam@64 241 }
cannam@64 242
cannam@101 243 m_blockSize = int(blockSize);
cannam@101 244 m_channels = int(channels);
cannam@64 245
cannam@64 246 m_freqbuf = new float *[m_channels];
cannam@101 247 for (int c = 0; c < m_channels; ++c) {
cannam@64 248 m_freqbuf[c] = new float[m_blockSize + 2];
cannam@64 249 }
cannam@101 250 m_window = new double[m_blockSize];
cannam@101 251
cannam@101 252 for (int i = 0; i < m_blockSize; ++i) {
cannam@101 253 // Hanning window
cannam@101 254 m_window[i] = (0.50 - 0.50 * cos((2.0 * M_PI * i) / m_blockSize));
cannam@101 255 }
cannam@101 256
cannam@101 257 #ifdef HAVE_FFTW3
cannam@101 258 m_ri = (double *)fftw_malloc(blockSize * sizeof(double));
cannam@101 259 m_cbuf = (fftw_complex *)fftw_malloc((blockSize/2 + 1) * sizeof(fftw_complex));
cannam@101 260 m_plan = fftw_plan_dft_r2c_1d(blockSize, m_ri, m_cbuf, FFTW_MEASURE);
cannam@101 261 #else
cannam@64 262 m_ri = new double[m_blockSize];
cannam@64 263 m_ro = new double[m_blockSize];
cannam@64 264 m_io = new double[m_blockSize];
cannam@101 265 #endif
cannam@64 266
cannam@64 267 return m_plugin->initialise(channels, stepSize, blockSize);
cannam@64 268 }
cannam@64 269
cannam@64 270 size_t
cannam@70 271 PluginInputDomainAdapter::Impl::getPreferredStepSize() const
cannam@64 272 {
cannam@64 273 size_t step = m_plugin->getPreferredStepSize();
cannam@64 274
cannam@64 275 if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
cannam@64 276 step = getPreferredBlockSize() / 2;
cannam@64 277 }
cannam@64 278
cannam@64 279 return step;
cannam@64 280 }
cannam@64 281
cannam@64 282 size_t
cannam@70 283 PluginInputDomainAdapter::Impl::getPreferredBlockSize() const
cannam@64 284 {
cannam@64 285 size_t block = m_plugin->getPreferredBlockSize();
cannam@64 286
cannam@64 287 if (m_plugin->getInputDomain() == FrequencyDomain) {
cannam@64 288 if (block == 0) {
cannam@64 289 block = 1024;
cannam@64 290 } else {
cannam@64 291 block = makeBlockSizeAcceptable(block);
cannam@64 292 }
cannam@64 293 }
cannam@64 294
cannam@64 295 return block;
cannam@64 296 }
cannam@64 297
cannam@64 298 size_t
cannam@70 299 PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
cannam@64 300 {
cannam@64 301 if (blockSize < 2) {
cannam@64 302
cannam@70 303 std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: blocksize < 2 not" << std::endl
cannam@64 304 << "supported, increasing from " << blockSize << " to 2" << std::endl;
cannam@64 305 blockSize = 2;
cannam@64 306
cannam@64 307 } else if (blockSize & (blockSize-1)) {
cannam@64 308
cannam@101 309 #ifdef HAVE_FFTW3
cannam@101 310 // not an issue with FFTW
cannam@101 311 #else
cannam@101 312
cannam@101 313 // not a power of two, can't handle that with our built-in FFT
cannam@64 314 // implementation
cannam@64 315
cannam@64 316 size_t nearest = blockSize;
cannam@64 317 size_t power = 0;
cannam@64 318 while (nearest > 1) {
cannam@64 319 nearest >>= 1;
cannam@64 320 ++power;
cannam@64 321 }
cannam@64 322 nearest = 1;
cannam@64 323 while (power) {
cannam@64 324 nearest <<= 1;
cannam@64 325 --power;
cannam@64 326 }
cannam@64 327
cannam@64 328 if (blockSize - nearest > (nearest*2) - blockSize) {
cannam@64 329 nearest = nearest*2;
cannam@64 330 }
cannam@64 331
cannam@70 332 std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: non-power-of-two\nblocksize " << blockSize << " not supported, using blocksize " << nearest << " instead" << std::endl;
cannam@64 333 blockSize = nearest;
cannam@101 334
cannam@101 335 #endif
cannam@64 336 }
cannam@64 337
cannam@64 338 return blockSize;
cannam@64 339 }
cannam@64 340
cannam@64 341 Plugin::FeatureSet
cannam@70 342 PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
cannam@70 343 RealTime timestamp)
cannam@64 344 {
cannam@64 345 if (m_plugin->getInputDomain() == TimeDomain) {
cannam@64 346 return m_plugin->process(inputBuffers, timestamp);
cannam@64 347 }
cannam@64 348
cannam@64 349 // The timestamp supplied should be (according to the Vamp::Plugin
cannam@64 350 // spec) the time of the start of the time-domain input block.
cannam@64 351 // However, we want to pass to the plugin an FFT output calculated
cannam@64 352 // from the block of samples _centred_ on that timestamp.
cannam@64 353 //
cannam@64 354 // We have two options:
cannam@64 355 //
cannam@64 356 // 1. Buffer the input, calculating the fft of the values at the
cannam@64 357 // passed-in block minus blockSize/2 rather than starting at the
cannam@64 358 // passed-in block. So each time we call process on the plugin,
cannam@64 359 // we are passing in the same timestamp as was passed to our own
cannam@64 360 // process plugin, but not (the frequency domain representation
cannam@64 361 // of) the same set of samples. Advantages: avoids confusion in
cannam@64 362 // the host by ensuring the returned values have timestamps
cannam@64 363 // comparable with that passed in to this function (in fact this
cannam@64 364 // is pretty much essential for one-value-per-block outputs);
cannam@64 365 // consistent with hosts such as SV that deal with the
cannam@64 366 // frequency-domain transform themselves. Disadvantages: means
cannam@64 367 // making the not necessarily correct assumption that the samples
cannam@64 368 // preceding the first official block are all zero (or some other
cannam@64 369 // known value).
cannam@64 370 //
cannam@64 371 // 2. Increase the passed-in timestamps by half the blocksize. So
cannam@64 372 // when we call process, we are passing in the frequency domain
cannam@64 373 // representation of the same set of samples as passed to us, but
cannam@64 374 // with a different timestamp. Advantages: simplicity; avoids
cannam@64 375 // iffy assumption mentioned above. Disadvantages: inconsistency
cannam@64 376 // with SV in cases where stepSize != blockSize/2; potential
cannam@64 377 // confusion arising from returned timestamps being calculated
cannam@64 378 // from the adjusted input timestamps rather than the original
cannam@64 379 // ones (and inaccuracy where the returned timestamp is implied,
cannam@64 380 // as in one-value-per-block).
cannam@64 381 //
cannam@64 382 // Neither way is ideal, but I don't think either is strictly
cannam@64 383 // incorrect either. I think this is just a case where the same
cannam@64 384 // plugin can legitimately produce differing results from the same
cannam@64 385 // input data, depending on how that data is packaged.
cannam@64 386 //
cannam@64 387 // We'll go for option 2, adjusting the timestamps. Note in
cannam@64 388 // particular that this means some results can differ from those
cannam@64 389 // produced by SV.
cannam@64 390
cannam@65 391 // std::cerr << "PluginInputDomainAdapter: sampleRate " << m_inputSampleRate << ", blocksize " << m_blockSize << ", adjusting time from " << timestamp;
cannam@64 392
cannam@68 393 timestamp = timestamp + RealTime::frame2RealTime
cannam@68 394 (m_blockSize/2, int(m_inputSampleRate + 0.5));
cannam@64 395
cannam@65 396 // std::cerr << " to " << timestamp << std::endl;
cannam@64 397
cannam@101 398 for (int c = 0; c < m_channels; ++c) {
cannam@64 399
cannam@101 400 for (int i = 0; i < m_blockSize; ++i) {
cannam@101 401 m_ri[i] = double(inputBuffers[c][i]) * m_window[i];
cannam@64 402 }
cannam@64 403
cannam@101 404 for (int i = 0; i < m_blockSize/2; ++i) {
cannam@64 405 // FFT shift
cannam@64 406 double value = m_ri[i];
cannam@64 407 m_ri[i] = m_ri[i + m_blockSize/2];
cannam@64 408 m_ri[i + m_blockSize/2] = value;
cannam@64 409 }
cannam@64 410
cannam@101 411 #ifdef HAVE_FFTW3
cannam@101 412
cannam@101 413 fftw_execute(m_plan);
cannam@101 414
cannam@101 415 for (int i = 0; i <= m_blockSize/2; ++i) {
cannam@101 416 m_freqbuf[c][i * 2] = float(m_cbuf[i][0]);
cannam@101 417 m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i][1]);
cannam@101 418 }
cannam@101 419
cannam@101 420 #else
cannam@101 421
cannam@64 422 fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
cannam@64 423
cannam@101 424 for (int i = 0; i <= m_blockSize/2; ++i) {
cannam@101 425 m_freqbuf[c][i * 2] = float(m_ro[i]);
cannam@101 426 m_freqbuf[c][i * 2 + 1] = float(m_io[i]);
cannam@64 427 }
cannam@101 428
cannam@101 429 #endif
cannam@64 430 }
cannam@64 431
cannam@64 432 return m_plugin->process(m_freqbuf, timestamp);
cannam@64 433 }
cannam@64 434
cannam@101 435 #ifndef HAVE_FFTW3
cannam@101 436
cannam@64 437 void
cannam@70 438 PluginInputDomainAdapter::Impl::fft(unsigned int n, bool inverse,
cannam@70 439 double *ri, double *ii, double *ro, double *io)
cannam@64 440 {
cannam@64 441 if (!ri || !ro || !io) return;
cannam@64 442
cannam@64 443 unsigned int bits;
cannam@64 444 unsigned int i, j, k, m;
cannam@64 445 unsigned int blockSize, blockEnd;
cannam@64 446
cannam@64 447 double tr, ti;
cannam@64 448
cannam@64 449 if (n < 2) return;
cannam@64 450 if (n & (n-1)) return;
cannam@64 451
cannam@64 452 double angle = 2.0 * M_PI;
cannam@64 453 if (inverse) angle = -angle;
cannam@64 454
cannam@64 455 for (i = 0; ; ++i) {
cannam@64 456 if (n & (1 << i)) {
cannam@64 457 bits = i;
cannam@64 458 break;
cannam@64 459 }
cannam@64 460 }
cannam@64 461
cannam@64 462 static unsigned int tableSize = 0;
cannam@64 463 static int *table = 0;
cannam@64 464
cannam@64 465 if (tableSize != n) {
cannam@64 466
cannam@64 467 delete[] table;
cannam@64 468
cannam@64 469 table = new int[n];
cannam@64 470
cannam@64 471 for (i = 0; i < n; ++i) {
cannam@64 472
cannam@64 473 m = i;
cannam@64 474
cannam@64 475 for (j = k = 0; j < bits; ++j) {
cannam@64 476 k = (k << 1) | (m & 1);
cannam@64 477 m >>= 1;
cannam@64 478 }
cannam@64 479
cannam@64 480 table[i] = k;
cannam@64 481 }
cannam@64 482
cannam@64 483 tableSize = n;
cannam@64 484 }
cannam@64 485
cannam@64 486 if (ii) {
cannam@64 487 for (i = 0; i < n; ++i) {
cannam@64 488 ro[table[i]] = ri[i];
cannam@64 489 io[table[i]] = ii[i];
cannam@64 490 }
cannam@64 491 } else {
cannam@64 492 for (i = 0; i < n; ++i) {
cannam@64 493 ro[table[i]] = ri[i];
cannam@64 494 io[table[i]] = 0.0;
cannam@64 495 }
cannam@64 496 }
cannam@64 497
cannam@64 498 blockEnd = 1;
cannam@64 499
cannam@64 500 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
cannam@64 501
cannam@64 502 double delta = angle / (double)blockSize;
cannam@64 503 double sm2 = -sin(-2 * delta);
cannam@64 504 double sm1 = -sin(-delta);
cannam@64 505 double cm2 = cos(-2 * delta);
cannam@64 506 double cm1 = cos(-delta);
cannam@64 507 double w = 2 * cm1;
cannam@64 508 double ar[3], ai[3];
cannam@64 509
cannam@64 510 for (i = 0; i < n; i += blockSize) {
cannam@64 511
cannam@64 512 ar[2] = cm2;
cannam@64 513 ar[1] = cm1;
cannam@64 514
cannam@64 515 ai[2] = sm2;
cannam@64 516 ai[1] = sm1;
cannam@64 517
cannam@64 518 for (j = i, m = 0; m < blockEnd; j++, m++) {
cannam@64 519
cannam@64 520 ar[0] = w * ar[1] - ar[2];
cannam@64 521 ar[2] = ar[1];
cannam@64 522 ar[1] = ar[0];
cannam@64 523
cannam@64 524 ai[0] = w * ai[1] - ai[2];
cannam@64 525 ai[2] = ai[1];
cannam@64 526 ai[1] = ai[0];
cannam@64 527
cannam@64 528 k = j + blockEnd;
cannam@64 529 tr = ar[0] * ro[k] - ai[0] * io[k];
cannam@64 530 ti = ar[0] * io[k] + ai[0] * ro[k];
cannam@64 531
cannam@64 532 ro[k] = ro[j] - tr;
cannam@64 533 io[k] = io[j] - ti;
cannam@64 534
cannam@64 535 ro[j] += tr;
cannam@64 536 io[j] += ti;
cannam@64 537 }
cannam@64 538 }
cannam@64 539
cannam@64 540 blockEnd = blockSize;
cannam@64 541 }
cannam@64 542
cannam@64 543 if (inverse) {
cannam@64 544
cannam@64 545 double denom = (double)n;
cannam@64 546
cannam@64 547 for (i = 0; i < n; i++) {
cannam@64 548 ro[i] /= denom;
cannam@64 549 io[i] /= denom;
cannam@64 550 }
cannam@64 551 }
cannam@64 552 }
cannam@64 553
cannam@101 554 #endif
cannam@101 555
cannam@64 556 }
cannam@64 557
cannam@64 558 }
cannam@64 559