Showing the changes to Spectacle/SpectacleBase.cpp (1 of 32 files). Show all
Unified · Split
| @@ -0,0 +1,646 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007 John Gibson | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License, version 2, as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | // Base class for the various spectacle externals, derived from | ||
| 15 | // my SPECTACLE2 RTcmix instruments. | ||
| 16 | // John Gibson <johgibso at indiana dot edu>, 11/23/07. | ||
| 17 | |||
| 18 | //#define DEBUG | ||
| 19 | //#define DEBUG_BINGROUPS | ||
| 20 | //#define CHECK_BINGROUPS | ||
| 21 | |||
| 22 | #include "SpectacleBase.h" | ||
| 23 | #include <float.h> | ||
| 24 | #include <cstddef> | ||
| 25 | |||
| 26 | #ifndef cosf | ||
| 27 | #define cosf(x) cos((x)) | ||
| 28 | #endif | ||
| 29 | #ifndef sinf | ||
| 30 | #define sinf(x) sin((x)) | ||
| 31 | #endif | ||
| 32 | #ifndef hypotf | ||
| 33 | #define hypotf(x, y) hypot((x), (y)) | ||
| 34 | #endif | ||
| 35 | #ifndef atan2f | ||
| 36 | #define atan2f(y, x) atan2((y), (x)) | ||
| 37 | #endif | ||
| 38 | |||
| 39 | const int kMaxWindowLen = kMaxFFTLen * 8; | ||
| 40 | const int kMinOverlap = 1; | ||
| 41 | const int kMaxOverlap = 64; | ||
| 42 | |||
| 43 | |||
| 44 | // ------------------------------------------------------------ SpectacleBase -- | ||
| 45 | SpectacleBase::SpectacleBase() : | ||
| 46 | _control_table_size(0), | ||
| 47 | _bin_groups(NULL), | ||
| 48 | _binmaptable(NULL), | ||
| 49 | _print_stats(false), | ||
| 50 | _reading_input(true), | ||
| 51 | _posteq(false), | ||
| 52 | _prev_bg_ignorevals(-1), | ||
| 53 | _srate(-FLT_MAX), | ||
| 54 | _minfreq(-FLT_MAX), _maxfreq(-FLT_MAX), | ||
| 55 | _anal_window(NULL), _synth_window(NULL), | ||
| 56 | _input(NULL), _output(NULL), | ||
| 57 | _outbuf(NULL), | ||
| 58 | _cursamp(0L), | ||
| 59 | _fft(NULL), | ||
| 60 | _bucket(NULL) | ||
| 61 | { | ||
| 62 | } | ||
| 63 | |||
| 64 | |||
| 65 | // ----------------------------------------------------------- ~SpectacleBase -- | ||
| 66 | SpectacleBase::~SpectacleBase() | ||
| 67 | { | ||
| 68 | delete [] _outbuf; | ||
| 69 | delete [] _input; | ||
| 70 | delete [] _output; | ||
| 71 | delete [] _anal_window; | ||
| 72 | delete [] _synth_window; | ||
| 73 | delete [] _bin_groups; | ||
| 74 | delete _bucket; | ||
| 75 | delete _fft; | ||
| 76 | } | ||
| 77 | |||
| 78 | |||
| 79 | // ------------------------------------------------------------- make_windows -- | ||
| 80 | int SpectacleBase::make_windows() | ||
| 81 | { | ||
| 82 | // Hamming window | ||
| 83 | for (int i = 0; i < _window_len; i++) | ||
| 84 | _anal_window[i] = _synth_window[i] = 0.54f - 0.46f | ||
| 85 | * cosf(2.0f * M_PI * i / (_window_len - 1)); | ||
| 86 | |||
| 87 | // When _window_len > _fftlen, also apply interpolating (sinc) windows to | ||
| 88 | // ensure that window is 0 at increments of _fftlen away from the center | ||
| 89 | // of the analysis window and of decimation away from the center of the | ||
| 90 | // synthesis window. | ||
| 91 | |||
| 92 | if (_window_len > _fftlen) { | ||
| 93 | float x = -(_window_len - 1) / 2.0; | ||
| 94 | for (int i = 0; i < _window_len; i++, x += 1.0f) | ||
| 95 | if (x != 0.0f) { | ||
| 96 | _anal_window[i] *= _fftlen * sin(M_PI * x / _fftlen) / (M_PI * x); | ||
| 97 | if (_decimation) | ||
| 98 | _synth_window[i] *= _decimation * sin(M_PI * x / _decimation) | ||
| 99 | / (M_PI * x); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | // Normalize windows for unity gain across unmodified | ||
| 104 | // analysis-synthesis procedure. | ||
| 105 | |||
| 106 | float sum = 0.0f; | ||
| 107 | for (int i = 0; i < _window_len; i++) | ||
| 108 | sum += _anal_window[i]; | ||
| 109 | |||
| 110 | for (int i = 0; i < _window_len; i++) { | ||
| 111 | float afac = 2.0f / sum; | ||
| 112 | float sfac = _window_len > _fftlen ? 1.0f / afac : afac; | ||
| 113 | _anal_window[i] *= afac; | ||
| 114 | _synth_window[i] *= sfac; | ||
| 115 | } | ||
| 116 | |||
| 117 | if (_window_len <= _fftlen && _decimation) { | ||
| 118 | sum = 0.0f; | ||
| 119 | for (int i = 0; i < _window_len; i += _decimation) | ||
| 120 | sum += _synth_window[i] * _synth_window[i]; | ||
| 121 | sum = 1.0f / sum; | ||
| 122 | for (int i = 0; i < _window_len; i++) | ||
| 123 | _synth_window[i] *= sum; | ||
| 124 | } | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | |||
| 130 | // ---------------------------------------------------------------- set_srate -- | ||
| 131 | // Must call this before anything that updates bin groups in subclass. | ||
| 132 | void SpectacleBase::set_srate(float srate) | ||
| 133 | { | ||
| 134 | if (srate != _srate) { | ||
| 135 | _srate = srate; | ||
| 136 | if (_maxfreq == _nyquist) | ||
| 137 | _maxfreq = 0.0f; | ||
| 138 | _nyquist = srate / 2; | ||
| 139 | if (_maxfreq == 0.0f) | ||
| 140 | _maxfreq = _nyquist; | ||
| 141 | _fund_anal_freq = srate / float(_fftlen); | ||
| 142 | update_bin_groups(_bin_groups, NULL, _minfreq, _maxfreq, | ||
| 143 | _control_table_size); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | |||
| 148 | // --------------------------------------------------------------------- init -- | ||
| 149 | int SpectacleBase::init(int fftlen, int windowlen, int overlap, float srate) | ||
| 150 | { | ||
| 151 | _fftlen = fftlen; | ||
| 152 | _window_len = windowlen; | ||
| 153 | _overlap = overlap; | ||
| 154 | |||
| 155 | // Make sure FFT length is a power of 2 <= kMaxFFTLen. | ||
| 156 | bool valid = false; | ||
| 157 | for (int x = 1; x <= kMaxFFTLen; x *= 2) { | ||
| 158 | if (_fftlen == x) { | ||
| 159 | valid = true; | ||
| 160 | break; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | if (!valid) { | ||
| 164 | //post("%s: FFT length must be a power of two <= %d. Setting to 1024...", | ||
| 165 | //instname(), kMaxFFTLen); | ||
| 166 | _fftlen = 1024; | ||
| 167 | } | ||
| 168 | |||
| 169 | _half_fftlen = _fftlen / 2; | ||
| 170 | |||
| 171 | // Make sure window length is a power of 2 >= FFT length. | ||
| 172 | valid = false; | ||
| 173 | for (int x = _fftlen; x <= kMaxWindowLen; x *= 2) { | ||
| 174 | if (_window_len == x) { | ||
| 175 | valid = true; | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | if (!valid) { | ||
| 180 | //post("%s: Window length must be a power of two >= FFT length (%d)\n" | ||
| 181 | //"and <= %d. Setting to 2048...", | ||
| 182 | //instname(), _fftlen, kMaxWindowLen); | ||
| 183 | _window_len = _fftlen * 2; | ||
| 184 | } | ||
| 185 | |||
| 186 | // Make sure _overlap is a power of 2 in allowed range. | ||
| 187 | valid = false; | ||
| 188 | for (int x = kMinOverlap; x <= kMaxOverlap; x *= 2) { | ||
| 189 | if (_overlap == x) { | ||
| 190 | valid = true; | ||
| 191 | break; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | if (!valid) { | ||
| 195 | //post("%s: Overlap must be a power of two between %d and %d. " | ||
| 196 | //"Setting to 2...", | ||
| 197 | //instname(), kMinOverlap, kMaxOverlap); | ||
| 198 | _overlap = 2; | ||
| 199 | } | ||
| 200 | |||
| 201 | _bin_groups = new int [_half_fftlen]; | ||
| 202 | |||
| 203 | set_srate(srate); | ||
| 204 | set_freqrange(0.0f, 0.0f); | ||
| 205 | |||
| 206 | // derive decimation from overlap | ||
| 207 | _decimation = int(_fftlen / _overlap); | ||
| 208 | |||
| 209 | _window_len_minus_decimation = _window_len - _decimation; | ||
| 210 | |||
| 211 | DPRINT2("_fftlen=%d, _decimation=%d", _fftlen, _decimation); | ||
| 212 | |||
| 213 | _input = new float [_window_len]; // interior input buffer | ||
| 214 | _output = new float [_window_len]; // interior output buffer | ||
| 215 | if (_input == NULL || _output == NULL) | ||
| 216 | return -1; | ||
| 217 | for (int i = 0; i < _window_len; i++) | ||
| 218 | _input[i] = _output[i] = 0.0f; | ||
| 219 | |||
| 220 | // Read index chases write index by _decimation; add 2 extra locations to | ||
| 221 | // keep read point from stepping on write point. Verify with asserts in | ||
| 222 | // increment_out_*_index(). | ||
| 223 | _outframes = _decimation + 2; | ||
| 224 | _out_read_index = _outframes - _decimation; | ||
| 225 | _out_write_index = 0; | ||
| 226 | _outbuf = new float [_outframes]; | ||
| 227 | if (_outbuf == NULL) | ||
| 228 | return -1; | ||
| 229 | for (int i = 0; i < _outframes; i++) | ||
| 230 | _outbuf[i] = 0.0f; | ||
| 231 | DPRINT1("_outframes: %d", _outframes); | ||
| 232 | |||
| 233 | _anal_window = new float [_window_len]; | ||
| 234 | _synth_window = new float [_window_len]; | ||
| 235 | if (_anal_window == NULL || _synth_window == NULL) | ||
| 236 | return -1; | ||
| 237 | if (make_windows() != 0) | ||
| 238 | return -1; | ||
| 239 | |||
| 240 | _bucket = new Obucket(_decimation, process_wrapper, (void *) this); | ||
| 241 | |||
| 242 | _fft = new Offt(_fftlen); | ||
| 243 | _fft_buf = _fft->getbuf(); | ||
| 244 | |||
| 245 | return 0; | ||
| 246 | } | ||
| 247 | |||
| 248 | |||
| 249 | // -------------------------------------------------------------------- clear -- | ||
| 250 | void SpectacleBase::clear() | ||
| 251 | { | ||
| 252 | for (int i = 0; i < _fftlen; i++) | ||
| 253 | _fft_buf[i] = 0.0f; | ||
| 254 | for (int i = 0; i < _window_len; i++) | ||
| 255 | _input[i] = _output[i] = 0.0f; | ||
| 256 | for (int i = 0; i < _outframes; i++) | ||
| 257 | _outbuf[i] = 0.0f; | ||
| 258 | _bucket->clear(); | ||
| 259 | } | ||
| 260 | |||
| 261 | |||
| 262 | // ------------------------------------------------------------ prepare_input -- | ||
| 263 | // <buf> contains <_decimation> samples (mono) from most recent input. | ||
| 264 | |||
| 265 | void SpectacleBase::prepare_input(const float buf[]) | ||
| 266 | { | ||
| 267 | DPRINT("prepare_input"); | ||
| 268 | |||
| 269 | // Shift samples in <_input> from right to left by <_decimation>, | ||
| 270 | // leaving a hole of <_decimation> slots at right end. | ||
| 271 | |||
| 272 | for (int i = 0; i < _window_len_minus_decimation; i++) | ||
| 273 | _input[i] = _input[i + _decimation]; | ||
| 274 | |||
| 275 | // Copy <_decimation> samples from <buf> to right end of <_input>. | ||
| 276 | |||
| 277 | for (int i = _window_len_minus_decimation, j = 0; i < _window_len; i++, j++) | ||
| 278 | _input[i] = buf[j]; | ||
| 279 | |||
| 280 | // Multiply input array by analysis window, both of length <_window_len>. | ||
| 281 | // Fold and rotate windowed real input into FFT buffer. | ||
| 282 | |||
| 283 | for (int i = 0; i < _fftlen; i++) | ||
| 284 | _fft_buf[i] = 0.0f; | ||
| 285 | int j = _cursamp % _fftlen; | ||
| 286 | for (int i = 0; i < _window_len; i++) { | ||
| 287 | _fft_buf[j] += _input[i] * _anal_window[i]; | ||
| 288 | if (++j == _fftlen) | ||
| 289 | j = 0; | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | |||
| 294 | // ----------------------------------------------------------- prepare_output -- | ||
| 295 | void SpectacleBase::prepare_output() | ||
| 296 | { | ||
| 297 | DPRINT("prepare_output"); | ||
| 298 | |||
| 299 | // overlap-add <_fft_buf> real data into <_output> | ||
| 300 | int j = _cursamp % _fftlen; | ||
| 301 | for (int i = 0; i < _window_len; i++) { | ||
| 302 | _output[i] += _fft_buf[j] * _synth_window[i]; | ||
| 303 | if (++j == _fftlen) | ||
| 304 | j = 0; | ||
| 305 | } | ||
| 306 | |||
| 307 | // transfer samples from <_output> to outer output buffer. | ||
| 308 | for (int i = 0; i < _decimation; i++) { | ||
| 309 | _outbuf[_out_write_index] = _output[i]; | ||
| 310 | increment_out_write_index(); | ||
| 311 | } | ||
| 312 | |||
| 313 | // shift samples in <_output> from right to left by <_decimation> | ||
| 314 | for (int i = 0; i < _window_len_minus_decimation; i++) | ||
| 315 | _output[i] = _output[i + _decimation]; | ||
| 316 | for (int i = _window_len_minus_decimation; i < _window_len; i++) | ||
| 317 | _output[i] = 0.0f; | ||
| 318 | } | ||
| 319 | |||
| 320 | |||
| 321 | // ---------------------------------------------------------- process_wrapper -- | ||
| 322 | // Called by Obucket whenever the input bucket is full (i.e., has _decimation | ||
| 323 | // samps). This static member wrapper function lets us call a non-static member | ||
| 324 | // function, which we can't pass directly as a callback to Obucket. | ||
| 325 | // See http://www.newty.de/fpt/callback.html for one explanation of this. | ||
| 326 | |||
| 327 | void SpectacleBase::process_wrapper(const float buf[], const int len, | ||
| 328 | void *obj) | ||
| 329 | { | ||
| 330 | SpectacleBase *myself = (SpectacleBase *) obj; | ||
| 331 | myself->process(buf, len); | ||
| 332 | } | ||
| 333 | |||
| 334 | |||
| 335 | // ------------------------------------------------------------------ process -- | ||
| 336 | void SpectacleBase::process(const float *buf, const int) | ||
| 337 | { | ||
| 338 | DPRINT("SpectacleBase::process"); | ||
| 339 | |||
| 340 | if (_reading_input) { | ||
| 341 | prepare_input(buf); | ||
| 342 | _fft->r2c(); | ||
| 343 | } | ||
| 344 | modify_analysis(_reading_input); | ||
| 345 | _fft->c2r(); | ||
| 346 | prepare_output(); | ||
| 347 | } | ||
| 348 | |||
| 349 | |||
| 350 | // ------------------------------------------------------------ set_freqrange -- | ||
| 351 | // Update the frequency range within which control tables operate. Return true | ||
| 352 | // if range has actually changed, resulting in an update_bin_groups call. | ||
| 353 | // Otherwise, return false. | ||
| 354 | |||
| 355 | bool SpectacleBase::set_freqrange(float min, float max) | ||
| 356 | { | ||
| 357 | if (min != _minfreq || max != _maxfreq) { | ||
| 358 | if (max == 0.0f) | ||
| 359 | max = _nyquist; | ||
| 360 | if (max < min) | ||
| 361 | _fswap(&min, &max); | ||
| 362 | if (max > _nyquist) | ||
| 363 | max = _nyquist; | ||
| 364 | _maxfreq = max; | ||
| 365 | _minfreq = _fclamp(0.0f, min, max); | ||
| 366 | update_bin_groups(_bin_groups, NULL, _minfreq, max, _control_table_size); | ||
| 367 | return true; | ||
| 368 | } | ||
| 369 | return false; | ||
| 370 | } | ||
| 371 | |||
| 372 | |||
| 373 | // -------------------------------------------------------- update_bin_groups -- | ||
| 374 | // Update a bin groups array. (See .h for a description of bin groups.) Note | ||
| 375 | // that this is called from set_freqrange and any subclass equivalents. | ||
| 376 | // Three methods of updating bin groups array: (1) use a binmap table, | ||
| 377 | // (2) use a one-to-one mapping of bin groups to bins, or (3) use a non-linear | ||
| 378 | // mapping that starts as one-to-one, but combines bins in ever larger | ||
| 379 | // groups as bin frequency increases. The <binmaptable> argument is an | ||
| 380 | // optional binmap table to use in place of the base class <_binmaptable>. | ||
| 381 | // If not overriding the base class table, pass NULL. | ||
| 382 | |||
| 383 | void SpectacleBase::update_bin_groups( | ||
| 384 | int bin_groups[], | ||
| 385 | int binmaptable[], | ||
| 386 | const float minfreq, | ||
| 387 | const float maxfreq, | ||
| 388 | const int control_table_size) | ||
| 389 | { | ||
| 390 | if (control_table_size == 0) // no control table | ||
| 391 | return; | ||
| 392 | |||
| 393 | const int nbins = _half_fftlen; | ||
| 394 | const int *bmtable = binmaptable ? binmaptable : _binmaptable; | ||
| 395 | |||
| 396 | // The optional <bmtable> has <control_table_size> elements, each giving | ||
| 397 | // the number of adjacent FFT bins controlled by that element in any of the | ||
| 398 | // control tables (e.g., EQ). Copy this information into the <bin_groups> | ||
| 399 | // array, which is sized to <nbins>. We ignore minfreq and maxfreq. | ||
| 400 | |||
| 401 | if (bmtable != NULL) { | ||
| 402 | #ifdef DEBUG_BINGROUPS | ||
| 403 | //post("using binmap table -----------------------------"); | ||
| 404 | for (int i = 0; i < control_table_size; i++) | ||
| 405 | //post("[%d] %d", i, bmtable[i]); | ||
| 406 | #endif | ||
| 407 | int bidx = 0; | ||
| 408 | for (int i = 0; i < control_table_size; i++) { | ||
| 409 | int bincount = bmtable[i]; | ||
| 410 | while (bincount-- > 0) { | ||
| 411 | if (bidx == nbins) | ||
| 412 | goto highcount; | ||
| 413 | bin_groups[bidx++] = i; | ||
| 414 | } | ||
| 415 | } | ||
| 416 | highcount: | ||
| 417 | if (bidx < nbins) { | ||
| 418 | const int last = control_table_size - 1; | ||
| 419 | while (bidx < nbins) | ||
| 420 | bin_groups[bidx++] = last; | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | // If there is no binmap table... | ||
| 425 | // If freq. range is full bandwidth, and control table size is >= to the | ||
| 426 | // number of bins, then use linear mapping of control table slots to bins, | ||
| 427 | // as in SPECTACLE v1. If table is larger than the number of bins, warn | ||
| 428 | // about ignoring the extra table slots. | ||
| 429 | |||
| 430 | else if (control_table_size >= nbins | ||
| 431 | && minfreq == 0 && maxfreq == _nyquist) { | ||
| 432 | for (int i = 0; i < nbins; i++) | ||
| 433 | bin_groups[i] = i; | ||
| 434 | |||
| 435 | if (control_table_size > nbins) { | ||
| 436 | const int ignorevals = control_table_size - nbins; | ||
| 437 | if (ignorevals != _prev_bg_ignorevals) { | ||
| 438 | //post("%s: Control table of size %d too large for " | ||
| 439 | //"frequency range...ignoring last %d values", | ||
| 440 | //instname(), control_table_size, ignorevals); | ||
| 441 | _prev_bg_ignorevals = ignorevals; | ||
| 442 | } | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | // Otherwise, there is either a freq. range that is not full bandwidth, or | ||
| 447 | // the control table size is less than the number of bins. If the former, | ||
| 448 | // we insure that the first array slot affects all bins whose frequencies | ||
| 449 | // are below (and possibly a little above) the min. freq., and that the last | ||
| 450 | // array slot affects all bins whose frequencies are above (and possibly a | ||
| 451 | // little below) the max. freq. If the control table is smaller than the | ||
| 452 | // number of bins within the freq. range, then we construct a one-to-one or | ||
| 453 | // one-to-many mapping of control table slots to bins, arranged so that we | ||
| 454 | // can control lower frequencies with greater resolution. | ||
| 455 | // | ||
| 456 | // Depending on the difference between the control table size and the number | ||
| 457 | // of FFT bins, we create a mapping that begins linearly and grows | ||
| 458 | // arithmetically after a certain point. The purpose is to get higher | ||
| 459 | // resolution in the lower frequencies of the range, where it matters most. | ||
| 460 | // So near the low end of the range, there is a one-to-one mapping of control | ||
| 461 | // array slots to FFT bins. Near the high end of the range, one control | ||
| 462 | // array slot affects many FFT bins. We attempt to transition smoothly | ||
| 463 | // between these two extremes. As an example, the number of FFT bins | ||
| 464 | // controlled by each slot of a control table array might look like this: | ||
| 465 | // | ||
| 466 | // number of bins spanned by each cntl slot | ||
| 467 | // (lowshelf) 1 1 1 1 1 1 1 1 1 1 1 2 3 4 5 6 7 8 9 10 14 (highshelf) | ||
| 468 | // | ||
| 469 | // The scheme isn't perfect -- e.g., the penultimate slot might control more | ||
| 470 | // than you would expect -- but it does guarantee that each control table | ||
| 471 | // slot affects at least one FFT bin. | ||
| 472 | |||
| 473 | else { | ||
| 474 | const int lowshelfbin = closest_bin(minfreq); | ||
| 475 | int highshelfbin = closest_bin(maxfreq); | ||
| 476 | bool maxfreq_is_nyquist = false; | ||
| 477 | if (highshelfbin == nbins) { | ||
| 478 | maxfreq_is_nyquist = true; | ||
| 479 | highshelfbin--; // we don't control the nyquist bin | ||
| 480 | } | ||
| 481 | int cntltablen = control_table_size; | ||
| 482 | const float endflatrange = minfreq + (cntltablen * _fund_anal_freq); | ||
| 483 | bool linear_map = true; | ||
| 484 | if (endflatrange > maxfreq - _fund_anal_freq) { | ||
| 485 | const int ignorevals = int((endflatrange - maxfreq) | ||
| 486 | / _fund_anal_freq); | ||
| 487 | if (ignorevals > 0) { | ||
| 488 | cntltablen = control_table_size - ignorevals; | ||
| 489 | if (ignorevals != _prev_bg_ignorevals) { | ||
| 490 | //post("%s: Control table of size %d too large for " | ||
| 491 | //"frequency range...ignoring last %d values", | ||
| 492 | //instname(), control_table_size, ignorevals); | ||
| 493 | _prev_bg_ignorevals = ignorevals; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | } | ||
| 497 | else | ||
| 498 | linear_map = false; | ||
| 499 | #ifdef DEBUG_BINGROUPS | ||
| 500 | //post("lowbin=%d, highbin=%d, endflatrange=%f, map=%s", | ||
| 501 | //lowshelfbin, highshelfbin, endflatrange, | ||
| 502 | //linear_map ? "linear" : "nonlinear"); | ||
| 503 | #endif | ||
| 504 | |||
| 505 | // assign low shelf group | ||
| 506 | int bidx = 0; | ||
| 507 | while (bidx <= lowshelfbin) | ||
| 508 | bin_groups[bidx++] = 0; | ||
| 509 | |||
| 510 | // assign interior groups | ||
| 511 | if (linear_map) { | ||
| 512 | const int lastbingroup = maxfreq_is_nyquist ? cntltablen - 1 | ||
| 513 | : cntltablen - 2; | ||
| 514 | int bingroup = 1; | ||
| 515 | while (bidx < highshelfbin) { | ||
| 516 | bin_groups[bidx++] = bingroup; | ||
| 517 | if (bingroup < lastbingroup) | ||
| 518 | bingroup++; | ||
| 519 | } | ||
| 520 | } | ||
| 521 | else { // higher array slots control more and more bins | ||
| 522 | const int extrabins = (highshelfbin - lowshelfbin) - cntltablen; | ||
| 523 | //if (extrabins <= 0) | ||
| 524 | //error("%s: program error - contact johgibso@gmail.com", instname()); | ||
| 525 | const int nsums = int(sqrt(2 * extrabins) + 2); | ||
| 526 | |||
| 527 | // Compute the sum of integers for n in [nsums-2, nsums); | ||
| 528 | // formula is the closed-form equation: x = n * (n + 1) / 2. | ||
| 529 | const int an = nsums - 2; | ||
| 530 | const int bn = nsums - 1; | ||
| 531 | const int a = (an * (an + 1)) / 2; | ||
| 532 | const int b = (bn * (bn + 1)) / 2; | ||
| 533 | int cntlspan = 0, binspan = 0; | ||
| 534 | if (a > extrabins) { | ||
| 535 | cntlspan = an; | ||
| 536 | binspan = a; | ||
| 537 | } | ||
| 538 | else if (b > extrabins) { | ||
| 539 | cntlspan = bn; | ||
| 540 | binspan = b; | ||
| 541 | } | ||
| 542 | //else // this should never happen | ||
| 543 | //error("%s: program error - contact johgibso@gmail.com", instname()); | ||
| 544 | |||
| 545 | #ifdef DEBUG_BINGROUPS | ||
| 546 | //post("nbins=%d, lowbin=%d, highbin=%d, tablen=%d, extrabins=%d, " | ||
| 547 | //"cntlspan=%d, binspan=%d, maxfreqisnyq=%d\n", | ||
| 548 | //nbins, lowshelfbin, highshelfbin, cntltablen, extrabins, | ||
| 549 | //cntlspan, binspan, maxfreq_is_nyquist); | ||
| 550 | #endif | ||
| 551 | |||
| 552 | // first, linear mapping | ||
| 553 | const int end = (cntltablen - cntlspan) - 1; | ||
| 554 | int bingroup = 1; | ||
| 555 | while (bingroup < end) | ||
| 556 | bin_groups[bidx++] = bingroup++; | ||
| 557 | |||
| 558 | // then map using sum-of-integers series | ||
| 559 | const int lastbingroup = maxfreq_is_nyquist ? cntltablen - 1 | ||
| 560 | : cntltablen - 2; | ||
| 561 | int incr = 1; | ||
| 562 | int count = 0; | ||
| 563 | while (bidx < highshelfbin) { | ||
| 564 | bin_groups[bidx++] = bingroup; | ||
| 565 | if (++count == incr) { | ||
| 566 | count = 0; | ||
| 567 | if (bingroup < lastbingroup) | ||
| 568 | bingroup++; | ||
| 569 | incr++; | ||
| 570 | } | ||
| 571 | } | ||
| 572 | } | ||
| 573 | |||
| 574 | // assign high shelf group | ||
| 575 | const int last = cntltablen - 1; | ||
| 576 | while (bidx < nbins) | ||
| 577 | bin_groups[bidx++] = last; | ||
| 578 | } | ||
| 579 | |||
| 580 | #ifdef DEBUG_BINGROUPS | ||
| 581 | //post("bin_groups[] -----------------------------"); | ||
| 582 | for (int i = 0; i < nbins; i++) | ||
| 583 | //post("[%d] %d (%f)", i, bin_groups[i], i * _fund_anal_freq); | ||
| 584 | #endif | ||
| 585 | #ifdef CHECK_BINGROUPS | ||
| 586 | for (int i = 1; i < nbins; i++) { | ||
| 587 | int diff = bin_groups[i] - bin_groups[i - 1]; | ||
| 588 | if (diff < 0 || diff > 1) | ||
| 589 | //error("%s: bin group (%p) index %d not 0 or 1 greater than prev entry", | ||
| 590 | instname(), bin_groups, i); | ||
| 591 | } | ||
| 592 | #endif | ||
| 593 | } | ||
| 594 | |||
| 595 | |||
| 596 | // --------------------------------------------------------- print_bin_groups -- | ||
| 597 | // print a user-readable listing of bin groups | ||
| 598 | void SpectacleBase::print_bin_groups( | ||
| 599 | int bin_groups[], | ||
| 600 | const char *type) // to identify bin groups to user | ||
| 601 | { | ||
| 602 | const int nbins = _half_fftlen; | ||
| 603 | |||
| 604 | //post("\n%s: %s table bin groups", instname(), type); | ||
| 605 | //post("------------------------------------------"); | ||
| 606 | int cntltabslot = 0; | ||
| 607 | int startbin = 0; | ||
| 608 | for (int i = 0; i < nbins; i++) { | ||
| 609 | const int thisslot = bin_groups[i]; | ||
| 610 | if (thisslot > cntltabslot) { | ||
| 611 | // print stats for previous control table slot <cntltabslot> | ||
| 612 | const int startfreq = int(_fund_anal_freq * startbin + 0.5f); | ||
| 613 | if (i - startbin > 1) { | ||
| 614 | const int endfreq = int(_fund_anal_freq * (i - 1) + 0.5f); | ||
| 615 | //post(" [%d]\t%d-%d Hz (%d bins)", | ||
| 616 | //cntltabslot, startfreq, endfreq, i - startbin); | ||
| 617 | } | ||
| 618 | else | ||
| 619 | //post(" [%d]\t%d Hz", cntltabslot, startfreq); | ||
| 620 | cntltabslot = thisslot; | ||
| 621 | startbin = i; | ||
| 622 | } | ||
| 623 | } | ||
| 624 | } | ||
| 625 | |||
| 626 | |||
| 627 | // ---------------------------------------------------------------------- run -- | ||
| 628 | void SpectacleBase::run(float in[], float out[], int nframes) | ||
| 629 | { | ||
| 630 | if (_reading_input) { | ||
| 631 | for (int i = 0; i < nframes; i++) { | ||
| 632 | _bucket->drop(in[i]); // may process <_decimation> input frames | ||
| 633 | out[i] = _outbuf[_out_read_index]; | ||
| 634 | increment_out_read_index(); | ||
| 635 | _cursamp++; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | else { | ||
| 639 | for (int i = 0; i < nframes; i++) { | ||
| 640 | _bucket->drop(0.0f); // may process <_decimation> input frames | ||
| 641 | out[i] = _outbuf[_out_read_index]; | ||
| 642 | increment_out_read_index(); | ||
| 643 | _cursamp++; | ||
| 644 | } | ||
| 645 | } | ||
| 646 | } | ||