annotate LowFreq.cpp @ 11:ccb271dd0b84 tip

More sensible defaults
author Chris Cannam
date Wed, 12 Mar 2014 13:23:29 +0000
parents 0a56b08b373f
children
rev   line source
Chris@7 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@7 2 /*
Chris@7 3 Low-frequency spectrogram
Chris@7 4 Copyright (c) 2014 Queen Mary, University of London
Chris@7 5
Chris@7 6 Permission is hereby granted, free of charge, to any person
Chris@7 7 obtaining a copy of this software and associated documentation
Chris@7 8 files (the "Software"), to deal in the Software without
Chris@7 9 restriction, including without limitation the rights to use, copy,
Chris@7 10 modify, merge, publish, distribute, sublicense, and/or sell copies
Chris@7 11 of the Software, and to permit persons to whom the Software is
Chris@7 12 furnished to do so, subject to the following conditions:
Chris@7 13
Chris@7 14 The above copyright notice and this permission notice shall be
Chris@7 15 included in all copies or substantial portions of the Software.
Chris@7 16
Chris@7 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@7 18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@7 19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Chris@7 20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
Chris@7 21 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@7 22 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@7 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@7 24
Chris@7 25 Except as contained in this notice, the names of the Centre for
Chris@7 26 Digital Music; Queen Mary, University of London; and Chris Cannam
Chris@7 27 shall not be used in advertising or otherwise to promote the sale,
Chris@7 28 use or other dealings in this Software without prior written
Chris@7 29 authorization.
Chris@7 30 */
Chris@0 31
Chris@0 32 #include "LowFreq.h"
Chris@0 33
Chris@2 34 #include "dsp/rateconversion/Resampler.h"
Chris@2 35 #include "dsp/transforms/FFT.h"
Chris@2 36
Chris@2 37 #include <cmath>
Chris@4 38 #include <cstdio>
Chris@2 39
Chris@2 40 using std::cerr;
Chris@6 41 using std::cout;
Chris@2 42 using std::endl;
Chris@2 43 using std::vector;
Chris@2 44
Chris@5 45 static float minFmin = 0;
Chris@5 46 static float maxFmin = 500;
Chris@11 47 static float defaultFmin = 0.5;
Chris@5 48
Chris@5 49 static float minFmax = 0.1;
Chris@5 50 static float maxFmax = 500;
Chris@11 51 static float defaultFmax = 40;
Chris@2 52
Chris@2 53 static int minN = 1;
Chris@7 54 static int maxN = 2048;
Chris@11 55 static int defaultN = 79;
Chris@0 56
Chris@0 57 LowFreq::LowFreq(float inputSampleRate) :
Chris@0 58 Plugin(inputSampleRate),
Chris@5 59 m_fmin(defaultFmin),
Chris@5 60 m_fmax(defaultFmax),
Chris@2 61 m_n(defaultN),
Chris@2 62 m_blockSize(0),
Chris@8 63 m_roundedInputRate(round(inputSampleRate)),
Chris@9 64 m_drop(0),
Chris@9 65 m_pad(0),
Chris@2 66 m_resampler(0),
Chris@4 67 m_fft(0),
Chris@4 68 m_window(0)
Chris@0 69 {
Chris@7 70 m_nonIntegralInputRate = false;
Chris@7 71 if (fabs(double(m_inputSampleRate) - round(m_inputSampleRate)) > 1e-6) {
Chris@7 72 m_nonIntegralInputRate = true;
Chris@7 73 }
Chris@7 74
Chris@7 75 if (inputSampleRate / 4 < maxFmax) {
Chris@7 76 maxFmax = inputSampleRate / 4;
Chris@7 77 }
Chris@0 78 }
Chris@0 79
Chris@0 80 LowFreq::~LowFreq()
Chris@0 81 {
Chris@2 82 delete m_resampler;
Chris@2 83 delete m_fft;
Chris@4 84 delete m_window;
Chris@0 85 }
Chris@0 86
Chris@0 87 string
Chris@0 88 LowFreq::getIdentifier() const
Chris@0 89 {
Chris@0 90 return "lowfreq";
Chris@0 91 }
Chris@0 92
Chris@0 93 string
Chris@0 94 LowFreq::getName() const
Chris@0 95 {
Chris@0 96 return "Low-frequency Spectrogram";
Chris@0 97 }
Chris@0 98
Chris@0 99 string
Chris@0 100 LowFreq::getDescription() const
Chris@0 101 {
Chris@0 102 //!!! Return something helpful here!
Chris@0 103 return "";
Chris@0 104 }
Chris@0 105
Chris@0 106 string
Chris@0 107 LowFreq::getMaker() const
Chris@0 108 {
Chris@0 109 return "Queen Mary, University of London";
Chris@0 110 }
Chris@0 111
Chris@0 112 int
Chris@0 113 LowFreq::getPluginVersion() const
Chris@0 114 {
Chris@0 115 return 1;
Chris@0 116 }
Chris@0 117
Chris@0 118 string
Chris@0 119 LowFreq::getCopyright() const
Chris@0 120 {
Chris@0 121 return "GPL";
Chris@0 122 }
Chris@0 123
Chris@0 124 LowFreq::InputDomain
Chris@0 125 LowFreq::getInputDomain() const
Chris@0 126 {
Chris@0 127 return TimeDomain;
Chris@0 128 }
Chris@0 129
Chris@0 130 size_t
Chris@0 131 LowFreq::getPreferredBlockSize() const
Chris@0 132 {
Chris@2 133 return 1024;
Chris@0 134 }
Chris@0 135
Chris@0 136 size_t
Chris@0 137 LowFreq::getPreferredStepSize() const
Chris@0 138 {
Chris@2 139 return 1024;
Chris@0 140 }
Chris@0 141
Chris@0 142 size_t
Chris@0 143 LowFreq::getMinChannelCount() const
Chris@0 144 {
Chris@0 145 return 1;
Chris@0 146 }
Chris@0 147
Chris@0 148 size_t
Chris@0 149 LowFreq::getMaxChannelCount() const
Chris@0 150 {
Chris@0 151 return 1;
Chris@0 152 }
Chris@0 153
Chris@0 154 LowFreq::ParameterList
Chris@0 155 LowFreq::getParameterDescriptors() const
Chris@0 156 {
Chris@0 157 ParameterList list;
Chris@0 158
Chris@0 159 ParameterDescriptor d;
Chris@5 160 d.identifier = "fmin";
Chris@5 161 d.name = "Minimum frequency";
Chris@5 162 d.description = "Low bound for frequencies to include in the spectrogram. This will be rounded up to the next highest bin frequency based on the highest frequency and number of bins.";
Chris@5 163 d.unit = "Hz";
Chris@5 164 d.minValue = minFmin;
Chris@5 165 d.maxValue = maxFmin;
Chris@5 166 d.defaultValue = defaultFmin;
Chris@5 167 d.isQuantized = false;
Chris@5 168 list.push_back(d);
Chris@5 169
Chris@5 170 d.identifier = "fmax";
Chris@5 171 d.name = "Maximum frequency";
Chris@5 172 d.description = "Highest frequency component to include in the spectrogram.";
Chris@5 173 d.unit = "Hz";
Chris@5 174 d.minValue = minFmax;
Chris@5 175 d.maxValue = maxFmax;
Chris@5 176 d.defaultValue = defaultFmax;
Chris@0 177 d.isQuantized = false;
Chris@0 178 list.push_back(d);
Chris@0 179
Chris@2 180 d.identifier = "n";
Chris@2 181 d.name = "Number of bins";
Chris@5 182 d.description = "Number of spectrogram bins to return.";
Chris@2 183 d.unit = "";
Chris@2 184 d.minValue = minN;
Chris@2 185 d.maxValue = maxN;
Chris@2 186 d.defaultValue = defaultN;
Chris@2 187 d.isQuantized = true;
Chris@2 188 d.quantizeStep = 1;
Chris@0 189 list.push_back(d);
Chris@0 190
Chris@0 191 return list;
Chris@0 192 }
Chris@0 193
Chris@0 194 float
Chris@0 195 LowFreq::getParameter(string identifier) const
Chris@0 196 {
Chris@5 197 if (identifier == "fmin") {
Chris@5 198 return m_fmin;
Chris@5 199 } else if (identifier == "fmax") {
Chris@5 200 return m_fmax;
Chris@2 201 } else if (identifier == "n") {
Chris@2 202 return m_n;
Chris@0 203 }
Chris@0 204 return 0;
Chris@0 205 }
Chris@0 206
Chris@0 207 void
Chris@0 208 LowFreq::setParameter(string identifier, float value)
Chris@0 209 {
Chris@5 210 if (identifier == "fmin") {
Chris@5 211 m_fmin = value;
Chris@5 212 } else if (identifier == "fmax") {
Chris@5 213 m_fmax = value;
Chris@2 214 } else if (identifier == "n") {
Chris@2 215 m_n = int(value + 0.01);
Chris@0 216 }
Chris@0 217 }
Chris@0 218
Chris@0 219 LowFreq::ProgramList
Chris@0 220 LowFreq::getPrograms() const
Chris@0 221 {
Chris@0 222 ProgramList list;
Chris@0 223 return list;
Chris@0 224 }
Chris@0 225
Chris@0 226 string
Chris@0 227 LowFreq::getCurrentProgram() const
Chris@0 228 {
Chris@0 229 return ""; // no programs
Chris@0 230 }
Chris@0 231
Chris@0 232 void
Chris@0 233 LowFreq::selectProgram(string name)
Chris@0 234 {
Chris@0 235 }
Chris@0 236
Chris@0 237 LowFreq::OutputList
Chris@0 238 LowFreq::getOutputDescriptors() const
Chris@0 239 {
Chris@0 240 OutputList list;
Chris@0 241
Chris@0 242 OutputDescriptor d;
Chris@0 243 d.identifier = "spectrogram";
Chris@0 244 d.name = "Spectrogram";
Chris@0 245 d.description = "";
Chris@0 246 d.unit = "";
Chris@0 247 d.hasFixedBinCount = true;
Chris@6 248 d.binCount = m_n;
Chris@4 249
Chris@0 250 d.hasKnownExtents = false;
Chris@0 251 d.isQuantized = false;
Chris@4 252 d.sampleType = OutputDescriptor::FixedSampleRate;
Chris@7 253 d.sampleRate = getOutputSampleRate();
Chris@4 254
Chris@10 255 // cerr << "output descriptor effective sample rate = " << d.sampleRate << endl;
Chris@10 256 // cerr << "input sample rate = " << m_inputSampleRate << endl;
Chris@10 257 // cerr << "input rate / output rate = " << m_inputSampleRate / d.sampleRate << endl;
Chris@4 258
Chris@4 259 char namebuf[50];
Chris@6 260 for (int i = 0; i < m_n; ++i) {
Chris@5 261 sprintf(namebuf, "%.3gHz", double(getOutputBinFrequency(i)));
Chris@4 262 d.binNames.push_back(namebuf);
Chris@4 263 }
Chris@4 264
Chris@0 265 d.hasDuration = false;
Chris@0 266 list.push_back(d);
Chris@0 267
Chris@0 268 return list;
Chris@0 269 }
Chris@0 270
Chris@0 271 bool
Chris@0 272 LowFreq::initialise(size_t channels, size_t stepSize, size_t blockSize)
Chris@0 273 {
Chris@0 274 if (channels < getMinChannelCount() ||
Chris@2 275 channels > getMaxChannelCount()) {
Chris@2 276 cerr << "LowFreq::initialise: ERROR: channels " << channels
Chris@2 277 << " out of acceptable range " << getMinChannelCount()
Chris@2 278 << " -> " << getMaxChannelCount() << endl;
Chris@2 279 return false;
Chris@2 280 }
Chris@0 281
Chris@2 282 if (stepSize != blockSize) {
Chris@2 283 // We don't actually care what the block size is, but there
Chris@2 284 // must be no overlap
Chris@2 285 cerr << "LowFreq::initialise: ERROR: step size and block size must be "
Chris@2 286 << "equal (" << stepSize << " != " << blockSize << ")" << endl;
Chris@2 287 return false;
Chris@2 288 }
Chris@2 289
Chris@2 290 if (m_n < minN || m_n > maxN) {
Chris@2 291 cerr << "LowFreq::initialise: ERROR: bin count " << m_n
Chris@2 292 << " out of acceptable range " << minN
Chris@2 293 << " -> " << maxN << endl;
Chris@2 294 return false;
Chris@2 295 }
Chris@2 296
Chris@5 297 if (m_fmin < minFmin || m_fmin > maxFmin) {
Chris@5 298 cerr << "LowFreq::initialise: ERROR: lowest frequency " << m_fmin
Chris@5 299 << " out of acceptable range " << minFmin
Chris@5 300 << " -> " << maxFmin << endl;
Chris@5 301 return false;
Chris@5 302 }
Chris@5 303
Chris@5 304 if (m_fmax < minFmax || m_fmax > maxFmax) {
Chris@5 305 cerr << "LowFreq::initialise: ERROR: highest frequency " << m_fmax
Chris@5 306 << " out of acceptable range " << minFmax
Chris@5 307 << " -> " << maxFmax << endl;
Chris@2 308 return false;
Chris@2 309 }
Chris@2 310
Chris@7 311 if (m_nonIntegralInputRate) {
Chris@2 312 cerr << "LowFreq::initialise: WARNING: input sample rate "
Chris@2 313 << m_inputSampleRate << " is non-integral, output frequencies "
Chris@2 314 << "will be skewed by rounding it to nearest integer" << endl;
Chris@2 315 }
Chris@2 316
Chris@2 317 m_blockSize = blockSize;
Chris@2 318
Chris@2 319 reset();
Chris@0 320
Chris@0 321 return true;
Chris@0 322 }
Chris@0 323
Chris@0 324 void
Chris@0 325 LowFreq::reset()
Chris@0 326 {
Chris@2 327 delete m_resampler;
Chris@2 328 delete m_fft;
Chris@4 329 delete m_window;
Chris@2 330
Chris@5 331 // We want to return a number of bins n between frequencies fmin
Chris@5 332 // and fmax.
Chris@5 333
Chris@10 334 // The bin frequency for bin i of an n-point fft is fs/n*i (where
Chris@6 335 // fs is the sample rate), with the top half aliasing the bottom
Chris@6 336 // half. To get a max bin frequency of fmax, in theory we can
Chris@6 337 // downsample to a sample rate of fmax*2 and then run an m-point
Chris@6 338 // FFT, where m is sufficient to give us n bins remaining between
Chris@6 339 // fmin and fmax. We get m by calculating fmax / ((fmax-fmin)/n).
Chris@5 340
Chris@5 341 // However, our resampler introduces artifacts close to its cutoff
Chris@5 342 // frequency within the filter transition band so we probably
Chris@6 343 // shouldn't aim to resample to fmax*2. Instead we resample to
Chris@6 344 // fmax*4 and carry out an FFT of twice the length. (We could
Chris@6 345 // resample closer to fmax, but the sums make my brain hurt.)
Chris@2 346
Chris@8 347 m_resampler = new Resampler(m_roundedInputRate,
Chris@4 348 getTargetSampleRate());
Chris@9 349
Chris@5 350 m_fft = new FFT(getFFTSize());
Chris@5 351 m_window = new Window<double>(HanningWindow, getFFTSize());
Chris@4 352 m_buffer = std::vector<double>();
Chris@2 353
Chris@10 354 /*
Chris@5 355 cerr << "LowFreq::reset: freq range " << m_fmin << " to " << m_fmax << endl;
Chris@4 356 cerr << "LowFreq::reset: block size " << m_blockSize
Chris@4 357 << ", input sample rate " << m_inputSampleRate << endl;
Chris@4 358 cerr << "LowFreq::reset: target rate " << getTargetSampleRate()
Chris@5 359 << ", target step " << getTargetStepSize()
Chris@4 360 << ", resampler latency " << m_resampler->getLatency()
Chris@5 361 << ", fft size " << getFFTSize() << endl;
Chris@10 362 */
Chris@5 363
Chris@9 364 // Resampler's declared latency is its output latency. We need to
Chris@9 365 // drop that number of samples from the start of its output...
Chris@9 366 m_drop = m_resampler->getLatency();
Chris@9 367
Chris@9 368 // ...and add enough padding at the end of its input to ensure at
Chris@9 369 // least that number of samples come back.
Chris@9 370 m_pad = int(ceil((double(m_drop) * m_roundedInputRate) /
Chris@9 371 getTargetSampleRate()));
Chris@9 372
Chris@9 373 // We also need to make sure that the FFT frames are properly
Chris@9 374 // aligned. That is, the first returned column is expected to show
Chris@9 375 // an FFT frame centred on time zero. So we need to pre-pad the
Chris@9 376 // buffer with half the FFT length.
Chris@9 377 for (int i = 0; i < getFFTSize()/2; ++i) {
Chris@9 378 m_buffer.push_back(0.0);
Chris@9 379 }
Chris@5 380 }
Chris@5 381
Chris@5 382 int
Chris@5 383 LowFreq::getTargetSampleRate() const
Chris@5 384 {
Chris@6 385 int tfs = int(ceil(m_fmax)) * 4;
Chris@10 386 // cerr << "LowFreq::getTargetSampleRate: for range " << m_fmin << " -> " << m_fmax
Chris@10 387 // << " target rate is " << tfs << endl;
Chris@6 388 return tfs;
Chris@5 389 }
Chris@5 390
Chris@7 391 float
Chris@7 392 LowFreq::getOutputSampleRate() const
Chris@7 393 {
Chris@7 394 return float(getTargetSampleRate()) / float(getTargetStepSize());
Chris@7 395 }
Chris@7 396
Chris@7 397 static int gcd(int a, int b)
Chris@7 398 {
Chris@7 399 int c = a % b;
Chris@7 400 if (c == 0) {
Chris@7 401 return b;
Chris@7 402 } else {
Chris@7 403 return gcd(b, c);
Chris@7 404 }
Chris@7 405 }
Chris@7 406
Chris@5 407 int
Chris@5 408 LowFreq::getTargetStepSize() const
Chris@5 409 {
Chris@7 410 // We need to make sure that m_inputSampleRate / output sample
Chris@7 411 // rate is an integer, otherwise hosts like SV will end up with
Chris@7 412 // rounding error when counting columns with non-integral width in
Chris@7 413 // terms of the input file sample rate.
Chris@7 414 //
Chris@8 415 // This is tricky if we allow the user to configure the step size
Chris@8 416 // (e.g. by specifying a frame overlap for the fft) because the
Chris@8 417 // output sample rate is target rate / target step size, and we
Chris@8 418 // can't easily mess with the target rate, so we need some control
Chris@8 419 // over the step size.
Chris@7 420 //
Chris@8 421 // The simplest way to do this is always use the smallest possible
Chris@8 422 // step size that allows for an integral number of samples (at the
Chris@8 423 // input sample rate) per output frame. That is, targetRate / g,
Chris@8 424 // where g is the gcd of m_inputSampleRate and targetRate.
Chris@7 425
Chris@8 426 int g = gcd(m_roundedInputRate, getTargetSampleRate());
Chris@8 427 int step = getTargetSampleRate() / g;
Chris@8 428 if (step < 1) step = 1;
Chris@6 429 return step;
Chris@5 430 }
Chris@5 431
Chris@5 432 int
Chris@5 433 LowFreq::getFFTSize() const
Chris@5 434 {
Chris@8 435 // See note in reset() above
Chris@6 436 int fftSize = 4 * int(ceil(m_fmax / ((m_fmax - m_fmin) / m_n)));
Chris@10 437 // cerr << "LowFreq::getFFTSize: for range " << m_fmin << " -> " << m_fmax
Chris@10 438 // << " and n = " << m_n << ", fft size is " << fftSize << endl;
Chris@5 439 return fftSize;
Chris@5 440 }
Chris@5 441
Chris@5 442 int
Chris@5 443 LowFreq::getFirstOutputBin() const
Chris@5 444 {
Chris@10 445 // Frequency of fft bin i is fs/n*i. We want the first bin whose
Chris@10 446 // frequency is at least m_fmin.
Chris@10 447 int first = int(ceil((m_fmin * getFFTSize()) / getTargetSampleRate()));
Chris@10 448 // cerr << "LowFreq::getFirstOutputBin: for range " << m_fmin << " -> " << m_fmax
Chris@10 449 // << " and n = " << m_n << ", bin is " << first << endl;
Chris@5 450 return first;
Chris@5 451 }
Chris@5 452
Chris@5 453 float
Chris@5 454 LowFreq::getOutputBinFrequency(int i) const
Chris@5 455 {
Chris@10 456 // That is, frequency of the i'th output bin that we actually
Chris@10 457 // return -- the (i + getFirstOutputBin())'th bin in the fft
Chris@5 458 return (float(getTargetSampleRate()) / getFFTSize())
Chris@5 459 * (i + getFirstOutputBin());
Chris@0 460 }
Chris@0 461
Chris@0 462 LowFreq::FeatureSet
Chris@0 463 LowFreq::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
Chris@0 464 {
Chris@2 465 double *data = new double[m_blockSize];
Chris@2 466 for (int i = 0; i < m_blockSize; ++i) {
Chris@2 467 data[i] = inputBuffers[0][i];
Chris@2 468 }
Chris@2 469
Chris@2 470 vector<double> resampled = m_resampler->process(data, m_blockSize);
Chris@2 471 m_buffer.insert(m_buffer.end(), resampled.begin(), resampled.end());
Chris@2 472
Chris@9 473 if (m_drop > 0) {
Chris@9 474 int dropHere = m_drop;
Chris@9 475 if (dropHere > int(m_buffer.size())) {
Chris@9 476 dropHere = int(m_buffer.size());
Chris@9 477 }
Chris@9 478 advanceBy(dropHere);
Chris@9 479 m_drop -= dropHere;
Chris@9 480 }
Chris@9 481
Chris@2 482 delete[] data;
Chris@2 483
Chris@2 484 FeatureSet fs;
Chris@2 485
Chris@5 486 while (int(m_buffer.size()) >= getFFTSize()) {
Chris@2 487 Feature f = processColumn();
Chris@2 488 fs[0].push_back(f);
Chris@4 489 advance();
Chris@2 490 }
Chris@2 491
Chris@2 492 return fs;
Chris@0 493 }
Chris@0 494
Chris@0 495 LowFreq::FeatureSet
Chris@0 496 LowFreq::getRemainingFeatures()
Chris@0 497 {
Chris@2 498 FeatureSet fs;
Chris@2 499
Chris@9 500 double *padding = new double[m_pad];
Chris@9 501 for (int i = 0; i < m_pad; ++i) {
Chris@9 502 padding[i] = 0.0;
Chris@9 503 }
Chris@9 504 vector<double> lastBit = m_resampler->process(padding, m_pad);
Chris@9 505 m_buffer.insert(m_buffer.end(), lastBit.begin(), lastBit.end());
Chris@9 506
Chris@9 507 for (int i = 0; i < getFFTSize() * 2 - getTargetStepSize(); ++i) {
Chris@9 508 m_buffer.push_back(0.0);
Chris@4 509 }
Chris@4 510
Chris@5 511 while (int(m_buffer.size()) >= getFFTSize()) {
Chris@2 512 Feature f = processColumn();
Chris@2 513 fs[0].push_back(f);
Chris@4 514 advance();
Chris@2 515 }
Chris@2 516
Chris@2 517 return fs;
Chris@0 518 }
Chris@0 519
Chris@2 520 LowFreq::Feature
Chris@2 521 LowFreq::processColumn()
Chris@2 522 {
Chris@2 523 Feature f;
Chris@5 524
Chris@5 525 int sz = getFFTSize();
Chris@2 526
Chris@5 527 double *realOut = new double[sz];
Chris@5 528 double *imagOut = new double[sz];
Chris@2 529
Chris@5 530 double *windowed = new double[sz];
Chris@4 531 m_window->cut(m_buffer.data(), windowed);
Chris@3 532
Chris@4 533 m_fft->process(false, windowed, 0, realOut, imagOut);
Chris@2 534
Chris@6 535 int base = getFirstOutputBin();
Chris@6 536 for (int i = 0; i < m_n; ++i) {
Chris@6 537 int ix = base + i;
Chris@6 538 float mag = (realOut[ix] * realOut[ix] + imagOut[ix] * imagOut[ix]);
Chris@6 539 f.values.push_back(mag);
Chris@2 540 }
Chris@2 541
Chris@4 542 return f;
Chris@4 543 }
Chris@2 544
Chris@4 545 void
Chris@4 546 LowFreq::advance()
Chris@4 547 {
Chris@9 548 advanceBy(getTargetStepSize());
Chris@9 549 }
Chris@9 550
Chris@9 551 void
Chris@9 552 LowFreq::advanceBy(int n)
Chris@9 553 {
Chris@9 554 std::vector<double> advanced(m_buffer.data() + n,
Chris@2 555 m_buffer.data() + m_buffer.size());
Chris@2 556
Chris@2 557 m_buffer = advanced;
Chris@2 558 }
Chris@2 559