annotate SimpleCepstrum.cpp @ 4:3467d995ea2b

Attempt a complex inverse transform -- doesn't seem to work well?
author Chris Cannam
date Fri, 22 Jun 2012 23:56:37 +0100
parents a6f9ece68482
children 05c558f1a23b
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 #include "SimpleCepstrum.h"
Chris@0 4
Chris@0 5 #include <vector>
Chris@0 6 #include <algorithm>
Chris@0 7
Chris@0 8 #include <cstdio>
Chris@0 9 #include <cmath>
Chris@4 10 #include <complex>
Chris@0 11
Chris@0 12 using std::string;
Chris@0 13
Chris@0 14 SimpleCepstrum::SimpleCepstrum(float inputSampleRate) :
Chris@0 15 Plugin(inputSampleRate),
Chris@0 16 m_channels(0),
Chris@0 17 m_stepSize(256),
Chris@0 18 m_blockSize(1024),
Chris@0 19 m_fmin(50),
Chris@0 20 m_fmax(1000),
Chris@3 21 m_clamp(false),
Chris@3 22 m_method(InverseSymmetric)
Chris@0 23 {
Chris@0 24 }
Chris@0 25
Chris@0 26 SimpleCepstrum::~SimpleCepstrum()
Chris@0 27 {
Chris@0 28 }
Chris@0 29
Chris@0 30 string
Chris@0 31 SimpleCepstrum::getIdentifier() const
Chris@0 32 {
Chris@0 33 return "simple-cepstrum";
Chris@0 34 }
Chris@0 35
Chris@0 36 string
Chris@0 37 SimpleCepstrum::getName() const
Chris@0 38 {
Chris@0 39 return "Simple Cepstrum";
Chris@0 40 }
Chris@0 41
Chris@0 42 string
Chris@0 43 SimpleCepstrum::getDescription() const
Chris@0 44 {
Chris@2 45 return "Return simple cepstral data from DFT bins. This plugin is intended for casual inspection of cepstral data. It returns a lot of different sorts of data and is quite slow; it's not a good way to extract a single feature rapidly.";
Chris@0 46 }
Chris@0 47
Chris@0 48 string
Chris@0 49 SimpleCepstrum::getMaker() const
Chris@0 50 {
Chris@0 51 // Your name here
Chris@0 52 return "";
Chris@0 53 }
Chris@0 54
Chris@0 55 int
Chris@0 56 SimpleCepstrum::getPluginVersion() const
Chris@0 57 {
Chris@0 58 // Increment this each time you release a version that behaves
Chris@0 59 // differently from the previous one
Chris@0 60 return 1;
Chris@0 61 }
Chris@0 62
Chris@0 63 string
Chris@0 64 SimpleCepstrum::getCopyright() const
Chris@0 65 {
Chris@0 66 // This function is not ideally named. It does not necessarily
Chris@0 67 // need to say who made the plugin -- getMaker does that -- but it
Chris@0 68 // should indicate the terms under which it is distributed. For
Chris@0 69 // example, "Copyright (year). All Rights Reserved", or "GPL"
Chris@0 70 return "";
Chris@0 71 }
Chris@0 72
Chris@0 73 SimpleCepstrum::InputDomain
Chris@0 74 SimpleCepstrum::getInputDomain() const
Chris@0 75 {
Chris@0 76 return FrequencyDomain;
Chris@0 77 }
Chris@0 78
Chris@0 79 size_t
Chris@0 80 SimpleCepstrum::getPreferredBlockSize() const
Chris@0 81 {
Chris@0 82 return 1024;
Chris@0 83 }
Chris@0 84
Chris@0 85 size_t
Chris@0 86 SimpleCepstrum::getPreferredStepSize() const
Chris@0 87 {
Chris@0 88 return 256;
Chris@0 89 }
Chris@0 90
Chris@0 91 size_t
Chris@0 92 SimpleCepstrum::getMinChannelCount() const
Chris@0 93 {
Chris@0 94 return 1;
Chris@0 95 }
Chris@0 96
Chris@0 97 size_t
Chris@0 98 SimpleCepstrum::getMaxChannelCount() const
Chris@0 99 {
Chris@0 100 return 1;
Chris@0 101 }
Chris@0 102
Chris@0 103 SimpleCepstrum::ParameterList
Chris@0 104 SimpleCepstrum::getParameterDescriptors() const
Chris@0 105 {
Chris@0 106 ParameterList list;
Chris@0 107
Chris@0 108 ParameterDescriptor d;
Chris@0 109
Chris@0 110 d.identifier = "fmin";
Chris@0 111 d.name = "Minimum frequency";
Chris@0 112 d.description = "";
Chris@0 113 d.unit = "Hz";
Chris@0 114 d.minValue = m_inputSampleRate / m_blockSize;
Chris@0 115 d.maxValue = m_inputSampleRate / 2;
Chris@0 116 d.defaultValue = 50;
Chris@0 117 d.isQuantized = false;
Chris@0 118 list.push_back(d);
Chris@0 119
Chris@0 120 d.identifier = "fmax";
Chris@0 121 d.name = "Maximum frequency";
Chris@0 122 d.description = "";
Chris@0 123 d.unit = "Hz";
Chris@0 124 d.minValue = m_inputSampleRate / m_blockSize;
Chris@0 125 d.maxValue = m_inputSampleRate / 2;
Chris@0 126 d.defaultValue = 1000;
Chris@0 127 d.isQuantized = false;
Chris@0 128 list.push_back(d);
Chris@0 129
Chris@3 130 d.identifier = "method";
Chris@3 131 d.name = "Cepstrum transform method";
Chris@3 132 d.unit = "";
Chris@3 133 d.minValue = 0;
Chris@4 134 d.maxValue = 5;
Chris@3 135 d.defaultValue = 0;
Chris@3 136 d.isQuantized = true;
Chris@3 137 d.quantizeStep = 1;
Chris@3 138 d.valueNames.push_back("Inverse symmetric");
Chris@3 139 d.valueNames.push_back("Inverse asymmetric");
Chris@4 140 d.valueNames.push_back("Inverse complex");
Chris@4 141 d.valueNames.push_back("Forward power");
Chris@3 142 d.valueNames.push_back("Forward magnitude");
Chris@3 143 d.valueNames.push_back("Forward difference");
Chris@3 144 list.push_back(d);
Chris@3 145
Chris@0 146 d.identifier = "clamp";
Chris@0 147 d.name = "Clamp negative values in cepstrum at zero";
Chris@0 148 d.unit = "";
Chris@0 149 d.minValue = 0;
Chris@0 150 d.maxValue = 1;
Chris@0 151 d.defaultValue = 0;
Chris@0 152 d.isQuantized = true;
Chris@0 153 d.quantizeStep = 1;
Chris@3 154 d.valueNames.clear();
Chris@0 155 list.push_back(d);
Chris@0 156
Chris@0 157 return list;
Chris@0 158 }
Chris@0 159
Chris@0 160 float
Chris@0 161 SimpleCepstrum::getParameter(string identifier) const
Chris@0 162 {
Chris@0 163 if (identifier == "fmin") return m_fmin;
Chris@0 164 else if (identifier == "fmax") return m_fmax;
Chris@0 165 else if (identifier == "clamp") return (m_clamp ? 1 : 0);
Chris@3 166 else if (identifier == "method") return (int)m_method;
Chris@0 167 else return 0.f;
Chris@0 168 }
Chris@0 169
Chris@0 170 void
Chris@0 171 SimpleCepstrum::setParameter(string identifier, float value)
Chris@0 172 {
Chris@0 173 if (identifier == "fmin") m_fmin = value;
Chris@0 174 else if (identifier == "fmax") m_fmax = value;
Chris@0 175 else if (identifier == "clamp") m_clamp = (value > 0.5);
Chris@3 176 else if (identifier == "method") m_method = Method(int(value + 0.5));
Chris@0 177 }
Chris@0 178
Chris@0 179 SimpleCepstrum::ProgramList
Chris@0 180 SimpleCepstrum::getPrograms() const
Chris@0 181 {
Chris@0 182 ProgramList list;
Chris@0 183 return list;
Chris@0 184 }
Chris@0 185
Chris@0 186 string
Chris@0 187 SimpleCepstrum::getCurrentProgram() const
Chris@0 188 {
Chris@0 189 return ""; // no programs
Chris@0 190 }
Chris@0 191
Chris@0 192 void
Chris@0 193 SimpleCepstrum::selectProgram(string name)
Chris@0 194 {
Chris@0 195 }
Chris@0 196
Chris@0 197 SimpleCepstrum::OutputList
Chris@0 198 SimpleCepstrum::getOutputDescriptors() const
Chris@0 199 {
Chris@0 200 OutputList outputs;
Chris@0 201
Chris@0 202 int n = 0;
Chris@0 203
Chris@0 204 OutputDescriptor d;
Chris@2 205
Chris@0 206 d.identifier = "f0";
Chris@0 207 d.name = "Estimated fundamental frequency";
Chris@0 208 d.description = "";
Chris@0 209 d.unit = "";
Chris@0 210 d.hasFixedBinCount = true;
Chris@0 211 d.binCount = 1;
Chris@0 212 d.hasKnownExtents = true;
Chris@0 213 d.minValue = m_fmin;
Chris@0 214 d.maxValue = m_fmax;
Chris@0 215 d.isQuantized = false;
Chris@0 216 d.sampleType = OutputDescriptor::OneSamplePerStep;
Chris@0 217 d.hasDuration = false;
Chris@2 218 /*
Chris@0 219 m_f0Output = n++;
Chris@0 220 outputs.push_back(d);
Chris@2 221 */
Chris@0 222
Chris@0 223 d.identifier = "raw_cepstral_peak";
Chris@0 224 d.name = "Frequency corresponding to raw cepstral peak";
Chris@2 225 d.description = "Return the frequency whose period corresponds to the quefrency with the maximum value within the specified range of the cepstrum";
Chris@0 226 d.unit = "Hz";
Chris@0 227 m_rawOutput = n++;
Chris@0 228 outputs.push_back(d);
Chris@0 229
Chris@0 230 d.identifier = "variance";
Chris@0 231 d.name = "Variance of cepstral bins in range";
Chris@0 232 d.unit = "";
Chris@2 233 d.description = "Return the variance of bin values within the specified range of the cepstrum";
Chris@0 234 m_varOutput = n++;
Chris@0 235 outputs.push_back(d);
Chris@0 236
Chris@0 237 d.identifier = "peak";
Chris@0 238 d.name = "Peak value";
Chris@0 239 d.unit = "";
Chris@2 240 d.description = "Return the value found in the maximum-valued bin within the specified range of the cepstrum";
Chris@0 241 m_pvOutput = n++;
Chris@0 242 outputs.push_back(d);
Chris@0 243
Chris@0 244 d.identifier = "peak_to_mean";
Chris@0 245 d.name = "Peak-to-mean distance";
Chris@0 246 d.unit = "";
Chris@2 247 d.description = "Return the difference between maximum and mean bin values within the specified range of the cepstrum";
Chris@0 248 m_p2mOutput = n++;
Chris@0 249 outputs.push_back(d);
Chris@0 250
Chris@0 251 d.identifier = "cepstrum";
Chris@0 252 d.name = "Cepstrum";
Chris@0 253 d.unit = "";
Chris@2 254 d.description = "The unprocessed cepstrum bins within the specified range";
Chris@0 255
Chris@0 256 int from = int(m_inputSampleRate / m_fmax);
Chris@0 257 int to = int(m_inputSampleRate / m_fmin);
Chris@0 258 if (to >= (int)m_blockSize / 2) {
Chris@0 259 to = m_blockSize / 2 - 1;
Chris@0 260 }
Chris@0 261 d.binCount = to - from + 1;
Chris@0 262 for (int i = from; i <= to; ++i) {
Chris@0 263 float freq = m_inputSampleRate / i;
Chris@0 264 char buffer[10];
Chris@2 265 sprintf(buffer, "%.2f Hz", freq);
Chris@0 266 d.binNames.push_back(buffer);
Chris@0 267 }
Chris@0 268
Chris@0 269 d.hasKnownExtents = false;
Chris@0 270 m_cepOutput = n++;
Chris@0 271 outputs.push_back(d);
Chris@0 272
Chris@0 273 d.identifier = "am";
Chris@0 274 d.name = "Cepstrum bins relative to mean";
Chris@2 275 d.description = "The cepstrum bins within the specified range, expressed as a value relative to the mean bin value in the range, with values below the mean clamped to zero";
Chris@0 276 m_amOutput = n++;
Chris@0 277 outputs.push_back(d);
Chris@0 278
Chris@2 279 d.identifier = "env";
Chris@2 280 d.name = "Spectral envelope";
Chris@2 281 d.description = "Envelope calculated from the cepstral values below the specified minimum";
Chris@2 282 d.binCount = m_blockSize/2 + 1;
Chris@2 283 d.binNames.clear();
Chris@2 284 for (int i = 0; i < d.binCount; ++i) {
Chris@2 285 float freq = (m_inputSampleRate / m_blockSize) * i;
Chris@2 286 char buffer[10];
Chris@2 287 sprintf(buffer, "%.2f Hz", freq);
Chris@2 288 d.binNames.push_back(buffer);
Chris@2 289 }
Chris@2 290 m_envOutput = n++;
Chris@2 291 outputs.push_back(d);
Chris@2 292
Chris@2 293 d.identifier = "es";
Chris@2 294 d.name = "Spectrum without envelope";
Chris@2 295 d.description = "Magnitude of spectrum values divided by calculated envelope values, to deconvolve the envelope";
Chris@2 296 m_esOutput = n++;
Chris@2 297 outputs.push_back(d);
Chris@2 298
Chris@0 299 return outputs;
Chris@0 300 }
Chris@0 301
Chris@0 302 bool
Chris@0 303 SimpleCepstrum::initialise(size_t channels, size_t stepSize, size_t blockSize)
Chris@0 304 {
Chris@0 305 if (channels < getMinChannelCount() ||
Chris@0 306 channels > getMaxChannelCount()) return false;
Chris@0 307
Chris@0 308 // std::cerr << "SimpleCepstrum::initialise: channels = " << channels
Chris@0 309 // << ", stepSize = " << stepSize << ", blockSize = " << blockSize
Chris@0 310 // << std::endl;
Chris@0 311
Chris@0 312 m_channels = channels;
Chris@0 313 m_stepSize = stepSize;
Chris@0 314 m_blockSize = blockSize;
Chris@0 315
Chris@0 316 return true;
Chris@0 317 }
Chris@0 318
Chris@0 319 void
Chris@0 320 SimpleCepstrum::reset()
Chris@0 321 {
Chris@0 322 }
Chris@0 323
Chris@0 324 SimpleCepstrum::FeatureSet
Chris@0 325 SimpleCepstrum::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
Chris@0 326 {
Chris@1 327 FeatureSet fs;
Chris@1 328
Chris@0 329 int bs = m_blockSize;
Chris@0 330 int hs = m_blockSize/2 + 1;
Chris@0 331
Chris@0 332 double *cep = new double[bs];
Chris@3 333 double *io = new double[bs];
Chris@3 334
Chris@4 335 if (m_method != InverseComplex) {
Chris@3 336
Chris@4 337 double *logmag = new double[bs];
Chris@4 338
Chris@4 339 for (int i = 0; i < hs; ++i) {
Chris@3 340
Chris@4 341 double power =
Chris@4 342 inputBuffers[0][i*2 ] * inputBuffers[0][i*2 ] +
Chris@4 343 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1];
Chris@3 344
Chris@4 345 double lm;
Chris@3 346
Chris@4 347 if (m_method == ForwardPower) {
Chris@4 348 lm = log(power + 0.00000001);
Chris@4 349 } else {
Chris@4 350 double mag = sqrt(power);
Chris@4 351 lm = log(mag + 0.00000001);
Chris@3 352 }
Chris@4 353
Chris@4 354 switch (m_method) {
Chris@4 355 case InverseSymmetric:
Chris@4 356 logmag[i] = lm;
Chris@4 357 if (i > 0) logmag[bs - i] = lm;
Chris@4 358 break;
Chris@4 359 case InverseAsymmetric:
Chris@4 360 logmag[i] = lm;
Chris@4 361 if (i > 0) logmag[bs - i] = 0;
Chris@4 362 break;
Chris@4 363 default:
Chris@4 364 logmag[bs/2 + i - 1] = lm;
Chris@4 365 if (i < hs-1) {
Chris@4 366 logmag[bs/2 - i - 1] = lm;
Chris@4 367 }
Chris@4 368 break;
Chris@3 369 }
Chris@3 370 }
Chris@4 371
Chris@4 372 if (m_method == InverseSymmetric ||
Chris@4 373 m_method == InverseAsymmetric) {
Chris@4 374
Chris@4 375 fft(bs, true, logmag, 0, cep, io);
Chris@4 376
Chris@4 377 } else {
Chris@4 378
Chris@4 379 fft(bs, false, logmag, 0, cep, io);
Chris@4 380
Chris@4 381 if (m_method == ForwardDifference) {
Chris@4 382 for (int i = 0; i < hs; ++i) {
Chris@4 383 cep[i] = fabs(io[i]) - fabs(cep[i]);
Chris@4 384 }
Chris@4 385 } else {
Chris@4 386 for (int i = 0; i < hs; ++i) {
Chris@4 387 cep[i] = sqrt(cep[i]*cep[i] + io[i]*io[i]);
Chris@4 388 }
Chris@4 389 }
Chris@4 390 }
Chris@4 391
Chris@4 392 delete[] logmag;
Chris@4 393
Chris@4 394 } else { // InverseComplex
Chris@4 395
Chris@4 396 double *ri = new double[bs];
Chris@4 397 double *ii = new double[bs];
Chris@4 398
Chris@4 399 for (int i = 0; i < hs; ++i) {
Chris@4 400 double re = inputBuffers[0][i*2];
Chris@4 401 double im = inputBuffers[0][i*2+1];
Chris@4 402 std::complex<double> c(re, im);
Chris@4 403 std::complex<double> clog = std::log(c);
Chris@4 404 ri[i] = clog.real();
Chris@4 405 ii[i] = clog.imag();
Chris@4 406 if (i > 0) {
Chris@4 407 ri[bs - i] = ri[i];
Chris@4 408 ii[bs - i] = -ii[i];
Chris@4 409 }
Chris@4 410 }
Chris@4 411
Chris@4 412 fft(bs, true, ri, ii, cep, io);
Chris@4 413
Chris@4 414 delete[] ri;
Chris@4 415 delete[] ii;
Chris@3 416 }
Chris@0 417
Chris@0 418 if (m_clamp) {
Chris@0 419 for (int i = 0; i < bs; ++i) {
Chris@0 420 if (cep[i] < 0) cep[i] = 0;
Chris@0 421 }
Chris@0 422 }
Chris@0 423
Chris@0 424 int from = int(m_inputSampleRate / m_fmax);
Chris@0 425 int to = int(m_inputSampleRate / m_fmin);
Chris@0 426
Chris@0 427 if (to >= bs / 2) {
Chris@0 428 to = bs / 2 - 1;
Chris@0 429 }
Chris@0 430
Chris@0 431 Feature cf;
Chris@0 432 for (int i = from; i <= to; ++i) {
Chris@0 433 cf.values.push_back(cep[i]);
Chris@0 434 }
Chris@0 435 fs[m_cepOutput].push_back(cf);
Chris@0 436
Chris@0 437 float maxval = 0.f;
Chris@0 438 int maxbin = 0;
Chris@0 439
Chris@0 440 for (int i = from; i <= to; ++i) {
Chris@0 441 if (cep[i] > maxval) {
Chris@0 442 maxval = cep[i];
Chris@0 443 maxbin = i;
Chris@0 444 }
Chris@0 445 }
Chris@0 446
Chris@0 447 Feature rf;
Chris@0 448 if (maxbin > 0) {
Chris@0 449 rf.values.push_back(m_inputSampleRate / maxbin);
Chris@0 450 } else {
Chris@0 451 rf.values.push_back(0);
Chris@0 452 }
Chris@0 453 fs[m_rawOutput].push_back(rf);
Chris@0 454
Chris@0 455 float mean = 0;
Chris@0 456 for (int i = from; i <= to; ++i) {
Chris@0 457 mean += cep[i];
Chris@0 458 }
Chris@0 459 mean /= (to - from) + 1;
Chris@0 460
Chris@0 461 float variance = 0;
Chris@0 462 for (int i = from; i <= to; ++i) {
Chris@0 463 float dev = fabsf(cep[i] - mean);
Chris@0 464 variance += dev * dev;
Chris@0 465 }
Chris@0 466 variance /= (to - from) + 1;
Chris@0 467
Chris@0 468 Feature vf;
Chris@0 469 vf.values.push_back(variance);
Chris@0 470 fs[m_varOutput].push_back(vf);
Chris@0 471
Chris@0 472 Feature pf;
Chris@0 473 pf.values.push_back(maxval - mean);
Chris@0 474 fs[m_p2mOutput].push_back(pf);
Chris@0 475
Chris@0 476 Feature pv;
Chris@0 477 pv.values.push_back(maxval);
Chris@0 478 fs[m_pvOutput].push_back(pv);
Chris@0 479
Chris@0 480 Feature am;
Chris@0 481 for (int i = from; i <= to; ++i) {
Chris@0 482 if (cep[i] < mean) am.values.push_back(0);
Chris@0 483 else am.values.push_back(cep[i] - mean);
Chris@0 484 }
Chris@0 485 fs[m_amOutput].push_back(am);
Chris@0 486
Chris@2 487 // destructively wipe the higher cepstral bins in order to
Chris@2 488 // calculate the envelope
Chris@4 489 double *env = new double[bs];
Chris@2 490 cep[0] /= 2;
Chris@2 491 cep[from-1] /= 2;
Chris@2 492 for (int i = 0; i < from; ++i) {
Chris@2 493 cep[i] /= bs;
Chris@2 494 }
Chris@2 495 for (int i = from; i < bs; ++i) {
Chris@2 496 cep[i] = 0;
Chris@2 497 }
Chris@4 498 fft(bs, false, cep, 0, env, io);
Chris@2 499 for (int i = 0; i < hs; ++i) {
Chris@4 500 env[i] = exp(env[i]);
Chris@2 501 }
Chris@4 502 Feature envf;
Chris@2 503 for (int i = 0; i < hs; ++i) {
Chris@4 504 envf.values.push_back(env[i]);
Chris@2 505 }
Chris@4 506 fs[m_envOutput].push_back(envf);
Chris@2 507
Chris@2 508 Feature es;
Chris@2 509 for (int i = 0; i < hs; ++i) {
Chris@4 510 double re = inputBuffers[0][i*2 ] / env[i];
Chris@4 511 double im = inputBuffers[0][i*2+1] / env[i];
Chris@2 512 double mag = sqrt(re*re + im*im);
Chris@2 513 es.values.push_back(mag);
Chris@2 514 }
Chris@2 515 fs[m_esOutput].push_back(es);
Chris@2 516
Chris@4 517 delete[] env;
Chris@3 518 delete[] io;
Chris@0 519 delete[] cep;
Chris@0 520
Chris@0 521 return fs;
Chris@0 522 }
Chris@0 523
Chris@0 524 SimpleCepstrum::FeatureSet
Chris@0 525 SimpleCepstrum::getRemainingFeatures()
Chris@0 526 {
Chris@0 527 FeatureSet fs;
Chris@0 528 return fs;
Chris@0 529 }
Chris@0 530
Chris@0 531 void
Chris@0 532 SimpleCepstrum::fft(unsigned int n, bool inverse,
Chris@0 533 double *ri, double *ii, double *ro, double *io)
Chris@0 534 {
Chris@0 535 if (!ri || !ro || !io) return;
Chris@0 536
Chris@0 537 unsigned int bits;
Chris@0 538 unsigned int i, j, k, m;
Chris@0 539 unsigned int blockSize, blockEnd;
Chris@0 540
Chris@0 541 double tr, ti;
Chris@0 542
Chris@0 543 if (n < 2) return;
Chris@0 544 if (n & (n-1)) return;
Chris@0 545
Chris@0 546 double angle = 2.0 * M_PI;
Chris@0 547 if (inverse) angle = -angle;
Chris@0 548
Chris@0 549 for (i = 0; ; ++i) {
Chris@0 550 if (n & (1 << i)) {
Chris@0 551 bits = i;
Chris@0 552 break;
Chris@0 553 }
Chris@0 554 }
Chris@0 555
Chris@0 556 static unsigned int tableSize = 0;
Chris@0 557 static int *table = 0;
Chris@0 558
Chris@0 559 if (tableSize != n) {
Chris@0 560
Chris@0 561 delete[] table;
Chris@0 562
Chris@0 563 table = new int[n];
Chris@0 564
Chris@0 565 for (i = 0; i < n; ++i) {
Chris@0 566
Chris@0 567 m = i;
Chris@0 568
Chris@0 569 for (j = k = 0; j < bits; ++j) {
Chris@0 570 k = (k << 1) | (m & 1);
Chris@0 571 m >>= 1;
Chris@0 572 }
Chris@0 573
Chris@0 574 table[i] = k;
Chris@0 575 }
Chris@0 576
Chris@0 577 tableSize = n;
Chris@0 578 }
Chris@0 579
Chris@0 580 if (ii) {
Chris@0 581 for (i = 0; i < n; ++i) {
Chris@0 582 ro[table[i]] = ri[i];
Chris@0 583 io[table[i]] = ii[i];
Chris@0 584 }
Chris@0 585 } else {
Chris@0 586 for (i = 0; i < n; ++i) {
Chris@0 587 ro[table[i]] = ri[i];
Chris@0 588 io[table[i]] = 0.0;
Chris@0 589 }
Chris@0 590 }
Chris@0 591
Chris@0 592 blockEnd = 1;
Chris@0 593
Chris@0 594 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
Chris@0 595
Chris@0 596 double delta = angle / (double)blockSize;
Chris@0 597 double sm2 = -sin(-2 * delta);
Chris@0 598 double sm1 = -sin(-delta);
Chris@0 599 double cm2 = cos(-2 * delta);
Chris@0 600 double cm1 = cos(-delta);
Chris@0 601 double w = 2 * cm1;
Chris@0 602 double ar[3], ai[3];
Chris@0 603
Chris@0 604 for (i = 0; i < n; i += blockSize) {
Chris@0 605
Chris@0 606 ar[2] = cm2;
Chris@0 607 ar[1] = cm1;
Chris@0 608
Chris@0 609 ai[2] = sm2;
Chris@0 610 ai[1] = sm1;
Chris@0 611
Chris@0 612 for (j = i, m = 0; m < blockEnd; j++, m++) {
Chris@0 613
Chris@0 614 ar[0] = w * ar[1] - ar[2];
Chris@0 615 ar[2] = ar[1];
Chris@0 616 ar[1] = ar[0];
Chris@0 617
Chris@0 618 ai[0] = w * ai[1] - ai[2];
Chris@0 619 ai[2] = ai[1];
Chris@0 620 ai[1] = ai[0];
Chris@0 621
Chris@0 622 k = j + blockEnd;
Chris@0 623 tr = ar[0] * ro[k] - ai[0] * io[k];
Chris@0 624 ti = ar[0] * io[k] + ai[0] * ro[k];
Chris@0 625
Chris@0 626 ro[k] = ro[j] - tr;
Chris@0 627 io[k] = io[j] - ti;
Chris@0 628
Chris@0 629 ro[j] += tr;
Chris@0 630 io[j] += ti;
Chris@0 631 }
Chris@0 632 }
Chris@0 633
Chris@0 634 blockEnd = blockSize;
Chris@0 635 }
Chris@0 636 }
Chris@0 637
Chris@0 638