int SPECTEQ2::subinit(double p[], int n_args) { int eqtablen; _eqtable = (double *) getPFieldTable(8, &eqtablen); if (!_eqtable) _eqconst = p[8]; else _control_table_size = eqtablen; int len; double *binmaptable = (double *) getPFieldTable(11, &len); if (binmaptable) { if (len != eqtablen) die(instname(), "The bin-mapping table (p11) must be the same size as " "the EQ table (p8)."); set_binmap_table(binmaptable); if (p[9] != 0.0 || (p[10] != 0.0 || p[10] != _nyquist)) warn(instname(), "Use of the bin-mapping table ignores the freq. " "range set in p9-10."); } set_ringdur(0.0f); return 0; }
int Spectacle::init(int fftlen, int windowlen, int overlap, float srate, float maxdeltime) { if (SpectacleBase::init(fftlen, windowlen, overlap, srate) != 0) return -1; // Compute maximum delay lag and create delay lines for FFT real and // imaginary values. Remember that these delays function at the decimation // rate, not at the audio rate, so the memory footprint is not as large // as you would expect -- about 44100 samples per second at fftlen=1024, // overlap=2 and SR=44100. _maxdeltime = maxdeltime; _maxdelsamps = long(maxdeltime * get_srate() / float(_decimation) + 0.5); for (int i = 0; i < _half_fftlen; i++) { Odelay *delay = new Odelay(_maxdelsamps); if (delay == NULL) goto bad_alloc; _real_delay[i] = delay; delay = new Odelay(_maxdelsamps); if (delay == NULL) goto bad_alloc; _imag_delay[i] = delay; } _delay_bin_groups = new int [_half_fftlen]; set_delay_freqrange(0.0f, 0.0f); return 0; bad_alloc: error("%s: Not enough memory for delay lines.", instname()); return -1; }
/* ------------------------------------------------------------------ init -- */ WindowType SPECTACLE_BASE :: getWindowType(double pval) { int intval = int(pval); WindowType type = HAMMING; switch (intval) { case 0: type = HAMMING; break; case 1: type = HANNING; break; case 2: type = RECTANGLE; break; case 3: type = TRIANGLE; break; case 4: type = BLACKMAN; break; case 5: type = KAISER; break; default: die(instname(), "Invalid window type %d\n.", intval); break; } return type; }
// -------------------------------------------------------------------- usage -- int SPECTEQ2::usage() { return die(NULL, "Usage: %s(start, inskip, indur, amp, fftlen, windowlen, windowtype, " "overlap, eqtable, minfreq, maxfreq, binmaptable, bypass, inchan, pan)", instname()); }
// --------------------------------------------------------- print_bin_groups -- // print a user-readable listing of bin groups void SpectacleBase::print_bin_groups( int bin_groups[], const char *type) // to identify bin groups to user { const int nbins = _half_fftlen; post("\n%s: %s table bin groups", instname(), type); post("------------------------------------------"); int cntltabslot = 0; int startbin = 0; for (int i = 0; i < nbins; i++) { const int thisslot = bin_groups[i]; if (thisslot > cntltabslot) { // print stats for previous control table slot <cntltabslot> const int startfreq = int(_fund_anal_freq * startbin + 0.5f); if (i - startbin > 1) { const int endfreq = int(_fund_anal_freq * (i - 1) + 0.5f); post(" [%d]\t%d-%d Hz (%d bins)", cntltabslot, startfreq, endfreq, i - startbin); } else post(" [%d]\t%d Hz", cntltabslot, startfreq); cntltabslot = thisslot; startbin = i; } } }
/* ------------------------------------------------------------------ init -- */ WindowType SPECTACLE_BASE :: getWindowType(double pval) { int intval = int(pval); WindowType type = Hamming; switch (intval) { case 0: type = Hamming; break; case 1: type = Hanning; break; case 2: type = Rectangle; break; case 3: type = Triangle; break; case 4: type = Blackman; break; case 5: type = Kaiser; break; default: die(instname(), "Invalid window type %d\n.", intval); break; } return type; }
// -------------------------------------------------------------------- usage -- int SPECTACLE2::usage() { return die(NULL, "Usage: %s(start, inskip, indur, amp, inamp, ringdur, fftlen, " "windowlen, windowtype, overlap, eqtable, delaytimetable, feedbacktable, " "eqminfreq, eqmaxfreq, delayminfreq, delaymaxfreq, binmaptable, wetpct, " "inchan, pan)", instname()); }
/* -------------------------------------------------------------- pre_init -- */ int FOLLOWBUTTER :: pre_init(double p[], int n_args) { nyquist = SR * 0.5; mincf = p[8]; maxcf = p[9]; nfilts = n_args > 10 ? (int) p[10] : 1; if (mincf < 1.0 || mincf > maxcf) return die(instname(), "The minimum cf (p8) must be greater than 1 but " "not greater than the maximum cf."); if (maxcf > nyquist) return die(instname(), "The maximum cf (p9) must be less than the " "Nyquist frequency."); if (nfilts < 1 || nfilts > MAXFILTS) return die(instname(), "Steepness (p10) must be an integer between 1 " "and %d.", MAXFILTS); return 0; }
/* ------------------------------------------------------------- post_init -- */ int FOLLOWBUTTER :: post_init(double p[], int n_args) { type = getFiltType(true); if (type == FiltInvalid) return die(instname(), "Filter type must be \"lowpass\", \"highpass\", " "\"bandpass\", or \"bandreject\"."); for (int i = 0; i < nfilts; i++) filt[i] = new Butter(SR); if (type == BandPass || type == BandReject) { if (n_args < 13) { // no p12 filter bandwidth PField, must use gen table double *function = floc(2); if (function == NULL) return die(instname(), "Either use the filter bandwidth pfield " "(p12) or make an old-style gen function in slot 2."); int len = fsize(2); bwtable = new TableL(SR, getdur(), function, len); } } return 0; }
// ------------------------------------------------------------- subconfigure -- int SPECTACLE2::subconfigure() { _eq_bin_groups = new int [_half_fftlen + 1]; for (int i = 0; i <= _half_fftlen; i++) { Odelay *delay = new Odelay(_maxdelsamps); if (delay == NULL) goto bad_alloc; _mag_delay[i] = delay; delay = new Odelay(_maxdelsamps); if (delay == NULL) goto bad_alloc; _phase_delay[i] = delay; } return 0; bad_alloc: return die(instname(), "Not enough memory for delay lines."); }
/* -------------------------------------------------------------- pre_init -- */ int SPECTACLE :: pre_init(double p[], int n_args) { wetdry = n_args > 9 ? p[9] : 1.0; /* default is 1 */ inchan = n_args > 10 ? (int) p[10] : 0; /* default is chan 0 */ pctleft = n_args > 11 ? p[11] : 0.5; /* default is center */ if (wetdry < 0.0 || wetdry > 1.0) return die(instname(), "Wet/dry must be between 0 and 1."); eqtable = floc(3); if (eqtable) { int len = fsize(3); eqtable = resample_functable(eqtable, len, half_fft_len); } else return die(instname(), "You haven't made the EQ function (table 3)."); deltimetable = floc(4); if (deltimetable) { int len = fsize(4); deltimetable = resample_functable(deltimetable, len, half_fft_len); } else return die(instname(), "You haven't made the delay time function (table 4)."); /* Compute maximum delay lag and create delay lines for FFT magnitude and phase values. Make ringdur at least as long as the longest delay time. Remember that these delays function at the decimation rate, not at audio rate. */ long maxdelsamps = (long) (MAXDELTIME * SR / decimation + 0.5); float maxtime = 0.0; for (int i = 0; i < half_fft_len; i++) { float deltime = deltimetable[i]; if (deltime < 0.0 || deltime > MAXDELTIME) return die(instname(), "Delay times must be >= 0 and <= %g.", MAXDELTIME); float samps = deltime * SR / (float) decimation; assert(samps <= maxdelsamps); mag_delay[i] = new DLineN(maxdelsamps); phase_delay[i] = new DLineN(maxdelsamps); if (deltime > maxtime) maxtime = deltime; // DPRINT2("%d: %g\n", i, samps); } if (ringdur < maxtime) ringdur = maxtime; /* but will still cut off any trailing feedback */ DPRINT3("decimation=%d, maxdelsamps=%ld, ringdur=%g\n", decimation, maxdelsamps, ringdur); feedbacktable = floc(5); if (feedbacktable) { int len = fsize(5); feedbacktable = resample_functable(feedbacktable, len, half_fft_len); } else return die(instname(), "You haven't made the delay feedback function (table 5)."); return 0; }
void SpectacleBase::update_bin_groups( int bin_groups[], int binmaptable[], const float minfreq, const float maxfreq, const int control_table_size) { if (control_table_size == 0) // no control table return; const int nbins = _half_fftlen; const int *bmtable = binmaptable ? binmaptable : _binmaptable; // The optional <bmtable> has <control_table_size> elements, each giving // the number of adjacent FFT bins controlled by that element in any of the // control tables (e.g., EQ). Copy this information into the <bin_groups> // array, which is sized to <nbins>. We ignore minfreq and maxfreq. if (bmtable != NULL) { #ifdef DEBUG_BINGROUPS post("using binmap table -----------------------------"); for (int i = 0; i < control_table_size; i++) post("[%d] %d", i, bmtable[i]); #endif int bidx = 0; for (int i = 0; i < control_table_size; i++) { int bincount = bmtable[i]; while (bincount-- > 0) { if (bidx == nbins) goto highcount; bin_groups[bidx++] = i; } } highcount: if (bidx < nbins) { const int last = control_table_size - 1; while (bidx < nbins) bin_groups[bidx++] = last; } } // If there is no binmap table... // If freq. range is full bandwidth, and control table size is >= to the // number of bins, then use linear mapping of control table slots to bins, // as in SPECTACLE v1. If table is larger than the number of bins, warn // about ignoring the extra table slots. else if (control_table_size >= nbins && minfreq == 0 && maxfreq == _nyquist) { for (int i = 0; i < nbins; i++) bin_groups[i] = i; if (control_table_size > nbins) { const int ignorevals = control_table_size - nbins; if (ignorevals != _prev_bg_ignorevals) { post("%s: Control table of size %d too large for " "frequency range...ignoring last %d values", instname(), control_table_size, ignorevals); _prev_bg_ignorevals = ignorevals; } } } // Otherwise, there is either a freq. range that is not full bandwidth, or // the control table size is less than the number of bins. If the former, // we insure that the first array slot affects all bins whose frequencies // are below (and possibly a little above) the min. freq., and that the last // array slot affects all bins whose frequencies are above (and possibly a // little below) the max. freq. If the control table is smaller than the // number of bins within the freq. range, then we construct a one-to-one or // one-to-many mapping of control table slots to bins, arranged so that we // can control lower frequencies with greater resolution. // // Depending on the difference between the control table size and the number // of FFT bins, we create a mapping that begins linearly and grows // arithmetically after a certain point. The purpose is to get higher // resolution in the lower frequencies of the range, where it matters most. // So near the low end of the range, there is a one-to-one mapping of control // array slots to FFT bins. Near the high end of the range, one control // array slot affects many FFT bins. We attempt to transition smoothly // between these two extremes. As an example, the number of FFT bins // controlled by each slot of a control table array might look like this: // // number of bins spanned by each cntl slot // (lowshelf) 1 1 1 1 1 1 1 1 1 1 1 2 3 4 5 6 7 8 9 10 14 (highshelf) // // The scheme isn't perfect -- e.g., the penultimate slot might control more // than you would expect -- but it does guarantee that each control table // slot affects at least one FFT bin. else { const int lowshelfbin = closest_bin(minfreq); int highshelfbin = closest_bin(maxfreq); bool maxfreq_is_nyquist = false; if (highshelfbin == nbins) { maxfreq_is_nyquist = true; highshelfbin--; // we don't control the nyquist bin } int cntltablen = control_table_size; const float endflatrange = minfreq + (cntltablen * _fund_anal_freq); bool linear_map = true; if (endflatrange > maxfreq - _fund_anal_freq) { const int ignorevals = int((endflatrange - maxfreq) / _fund_anal_freq); if (ignorevals > 0) { cntltablen = control_table_size - ignorevals; if (ignorevals != _prev_bg_ignorevals) { post("%s: Control table of size %d too large for " "frequency range...ignoring last %d values", instname(), control_table_size, ignorevals); _prev_bg_ignorevals = ignorevals; } } } else linear_map = false; #ifdef DEBUG_BINGROUPS post("lowbin=%d, highbin=%d, endflatrange=%f, map=%s", lowshelfbin, highshelfbin, endflatrange, linear_map ? "linear" : "nonlinear"); #endif // assign low shelf group int bidx = 0; while (bidx <= lowshelfbin) bin_groups[bidx++] = 0; // assign interior groups if (linear_map) { const int lastbingroup = maxfreq_is_nyquist ? cntltablen - 1 : cntltablen - 2; int bingroup = 1; while (bidx < highshelfbin) { bin_groups[bidx++] = bingroup; if (bingroup < lastbingroup) bingroup++; } } else { // higher array slots control more and more bins const int extrabins = (highshelfbin - lowshelfbin) - cntltablen; if (extrabins <= 0) error("%s: program error - contact [email protected]", instname()); const int nsums = int(sqrt(2 * extrabins) + 2); // Compute the sum of integers for n in [nsums-2, nsums); // formula is the closed-form equation: x = n * (n + 1) / 2. const int an = nsums - 2; const int bn = nsums - 1; const int a = (an * (an + 1)) / 2; const int b = (bn * (bn + 1)) / 2; int cntlspan = 0, binspan = 0; if (a > extrabins) { cntlspan = an; binspan = a; } else if (b > extrabins) { cntlspan = bn; binspan = b; } else // this should never happen error("%s: program error - contact [email protected]", instname()); #ifdef DEBUG_BINGROUPS post("nbins=%d, lowbin=%d, highbin=%d, tablen=%d, extrabins=%d, " "cntlspan=%d, binspan=%d, maxfreqisnyq=%d\n", nbins, lowshelfbin, highshelfbin, cntltablen, extrabins, cntlspan, binspan, maxfreq_is_nyquist); #endif // first, linear mapping const int end = (cntltablen - cntlspan) - 1; int bingroup = 1; while (bingroup < end) bin_groups[bidx++] = bingroup++; // then map using sum-of-integers series const int lastbingroup = maxfreq_is_nyquist ? cntltablen - 1 : cntltablen - 2; int incr = 1; int count = 0; while (bidx < highshelfbin) { bin_groups[bidx++] = bingroup; if (++count == incr) { count = 0; if (bingroup < lastbingroup) bingroup++; incr++; } } } // assign high shelf group const int last = cntltablen - 1; while (bidx < nbins) bin_groups[bidx++] = last; } #ifdef DEBUG_BINGROUPS post("bin_groups[] -----------------------------"); for (int i = 0; i < nbins; i++) post("[%d] %d (%f)", i, bin_groups[i], i * _fund_anal_freq); #endif #ifdef CHECK_BINGROUPS for (int i = 1; i < nbins; i++) { int diff = bin_groups[i] - bin_groups[i - 1]; if (diff < 0 || diff > 1) error("%s: bin group (%p) index %d not 0 or 1 greater than prev entry", instname(), bin_groups, i); } #endif }
/* Make balanced pair of analysis and synthesis windows. */ int SPECTACLE_BASE :: make_windows() { switch (window_type) { case HAMMING: for (int i = 0; i < window_len; i++) anal_window[i] = synth_window[i] = 0.54 - 0.46 * cos(TWO_PI * i / (window_len - 1)); break; case HANNING: return die(instname(), "Hanning window not implemented."); break; case RECTANGLE: //FIXME: is this right? for (int i = 0; i < window_len; i++) anal_window[i] = synth_window[i] = 1.0; break; case TRIANGLE: return die(instname(), "Triangle window not implemented."); break; case BLACKMAN: return die(instname(), "Blackman window not implemented."); break; case KAISER: return die(instname(), "Kaiser window not implemented."); break; } /* When window_len > fft_len, also apply interpolating (sinc) windows to ensure that window are 0 at increments of fft_len away from the center of the analysis window and of decimation away from the center of the synthesis window. */ if (window_len > fft_len) { float x; /* Take care to create symmetrical windows. */ x = -(window_len - 1) / 2.; for (int i = 0; i < window_len; i++, x += 1.) if (x != 0.) { anal_window[i] *= fft_len * sin(PI * x / fft_len) / (PI * x); if (decimation) synth_window[i] *= decimation * sin(PI * x / decimation) / (PI * x); } } /* Normalize windows for unity gain across unmodified analysis-synthesis procedure. */ float sum = 0.0; for (int i = 0; i < window_len; i++) sum += anal_window[i]; for (int i = 0; i < window_len; i++) { float afac = 2. / sum; float sfac = window_len > fft_len ? 1. / afac : afac; anal_window[i] *= afac; synth_window[i] *= sfac; } if (window_len <= fft_len && decimation) { sum = 0.0; for (int i = 0; i < window_len; i += decimation) sum += synth_window[i] * synth_window[i]; sum = 1.0 / sum; for (int i = 0; i < window_len; i++) synth_window[i] *= sum; } return 0; }
int SPECTACLE_BASE :: init(double p[], int n_args) { float outskip = p[0]; float inskip = p[1]; inputdur = p[2]; amp = p[3]; ringdur = p[4]; fft_len = (int) p[5]; window_len = (int) p[6]; window_type = getWindowType(p[7]); float overlap = p[8]; /* Make sure FFT length is a power of 2 <= MAXFFTLEN and <= RTBUFSAMPS. */ bool valid = false; for (int x = 1; x <= MAXFFTLEN; x *= 2) { if (fft_len == x) { valid = true; break; } } if (!valid || fft_len > MAXFFTLEN) return die(instname(), "FFT length must be a power of two <= %d", MAXFFTLEN); // FIXME: now this isn't a problem; instead, decimation can't be larger // than RTBUFSAMPS. But must couch errmsg in terms of overlap and fft length, // not decimation... #if 0 if (fft_len > RTBUFSAMPS) return die(instname(), "FFT length must be a power of two less than or equal\n" "to the output buffer size set in rtsetparams (currently %d).", RTBUFSAMPS); #endif half_fft_len = fft_len / 2; fund_anal_freq = SR / (float) fft_len; /* Make sure window length is a power of 2 >= FFT length. */ valid = false; for (int x = fft_len; x <= MAXWINDOWLEN; x *= 2) { if (window_len == x) { valid = true; break; } } if (!valid) return die(instname(), "Window length must be a power of two >= FFT length\n" "(currently %d) and <= %d.", fft_len, MAXWINDOWLEN); /* Make sure overlap is a power of 2 in our safety range. */ valid = false; //FIXME: need to adjust MINOVERLAP so that iterations is never 0 in run() // This might depend upon window_len?? for (float x = MINOVERLAP; x <= MAXOVERLAP; x *= 2.0) { if (overlap == x) { valid = true; break; } } if (!valid) return die(instname(), "Overlap must be a power of two between %g and %g.", MINOVERLAP, MAXOVERLAP); int_overlap = (int) overlap; /* derive decimation from overlap */ decimation = (int) (fft_len / overlap); DPRINT2("fft_len=%d, decimation=%d\n", fft_len, decimation); if (pre_init(p, n_args) != 0) /* can modify ringdur */ return DONT_SCHEDULE; iamparray = floc(1); if (iamparray) { int lenamp = fsize(1); tableset(SR, inputdur, lenamp, iamptabs); } else rtcmix_advise(instname(), "Setting input amplitude curve to all 1's."); oamparray = floc(2); if (oamparray) { int lenamp = fsize(2); tableset(SR, inputdur + ringdur, lenamp, oamptabs); } else rtcmix_advise(instname(), "Setting output amplitude curve to all 1's."); if (rtsetinput(inskip, this) == -1) return DONT_SCHEDULE; if (inchan >= inputChannels()) return die(instname(), "You asked for channel %d of a %d-channel file.", inchan, inputChannels()); /* <latency> is the delay before the FFT looks at actual input rather than zero-padding. Need to let inst run long enough to compensate for this. */ window_len_minus_decimation = window_len - decimation; latency = window_len + window_len_minus_decimation; float latency_dur = latency * (1.0 / SR); if (rtsetoutput(outskip, latency_dur + inputdur + ringdur, this) == -1) return DONT_SCHEDULE; total_insamps = (int)(inputdur * SR); /* without latency_dur */ input_end_frame = total_insamps + latency; DPRINT1("input_end_frame=%d\n", input_end_frame); input = new float [window_len]; /* input buffer */ output = new float [window_len]; /* output buffer */ anal_window = new float [window_len]; /* analysis window */ synth_window = new float [window_len]; /* synthesis window */ fft_buf = new float [fft_len]; /* FFT buffer */ anal_chans = new float [fft_len + 2]; /* analysis channels */ if (make_windows() != 0) return DONT_SCHEDULE; /* Delay dry output by window_len - decimation to sync with wet sig. */ drybuf = new float [decimation]; dry_delay = new DLineN(window_len); dry_delay->setDelay((float) window_len_minus_decimation); /* Init iamp and oamp to starting amplitudes. */ iamp = (iamparray == NULL) ? 1.0 : iamparray[0]; oamp = (oamparray == NULL) ? amp : oamparray[0]; skip = (int) (SR / (float) resetval); if (post_init(p, n_args) != 0) return DONT_SCHEDULE; return nSamps(); }
// --------------------------------------------------------------------- init -- int SPECTACLE2_BASE::init(double p[], int n_args) { _nargs = n_args; #ifdef NOTYET _print_stats = Option::printStats(); #else _print_stats = true; #endif if (getargs(p, n_args) < 0) // subclass gets args return DONT_SCHEDULE; // NB: subclass init will have already grabbed all pfields except these... const float outskip = p[0]; const float inskip = p[1]; _inputdur = p[2]; _oamp = p[3]; // Make sure FFT length is a power of 2 <= kMaxFFTLen. bool valid = false; for (int x = 1; x <= kMaxFFTLen; x *= 2) { if (_fftlen == x) { valid = true; break; } } if (!valid) return die(instname(), "FFT length must be a power of two <= %d", kMaxFFTLen); _half_fftlen = _fftlen / 2; _fund_anal_freq = SR / float(_fftlen); // Make sure window length is a power of 2 >= FFT length. valid = false; for (int x = _fftlen; x <= kMaxWindowLen; x *= 2) { if (_window_len == x) { valid = true; break; } } if (!valid) return die(instname(), "Window length must be a power of two >= FFT length (%d)\n" "and <= %d.", _fftlen, kMaxWindowLen); // Make sure _overlap is a power of 2 in allowed range. valid = false; for (int x = kMinOverlap; x <= kMaxOverlap; x *= 2) { if (_overlap == x) { valid = true; break; } } if (!valid) return die(instname(), "Overlap must be a power of two between %d and %d.", kMinOverlap, kMaxOverlap); // derive decimation from overlap _decimation = int(_fftlen / _overlap); // create this now, because subinit will need it _bin_groups = new int [_half_fftlen + 1]; // subclass init -- must follow FFT and bin groups init; sets _ringdur if (subinit(p, n_args) < 0) return DONT_SCHEDULE; if (rtsetinput(inskip, this) == -1) return DONT_SCHEDULE; if (_inchan >= inputChannels()) return die(instname(), "You asked for channel %d of a %d-channel input.", _inchan, inputChannels()); // Latency is the delay before the FFT looks at actual input rather than // zero-padding. Need to let inst run long enough to compensate for this, // as well as to span the user's requested ring-down duration. _window_len_minus_decimation = _window_len - _decimation; _latency = _window_len_minus_decimation; const float latency_dur = _latency / SR; if (rtsetoutput(outskip, latency_dur + _inputdur + _ringdur, this) == -1) return DONT_SCHEDULE; _input_frames = int(_inputdur * SR + 0.5); // without latency_dur _input_end_frame = _input_frames + _latency; DPRINT2("_fftlen=%d, _decimation=%d\n", _fftlen, _decimation); DPRINT3("_latency=%d, _input_frames=%d, ring frames = %d\n", _latency, _input_frames, int(_ringdur * SR + 0.5)); return nSamps(); }
void SPECTACLE2_BASE::update_bin_groups( int bin_groups[], const float minfreq, const float maxfreq, const int control_table_size, const char *type) // to identify bin groups to user { if (control_table_size == 0) // no control table return; const int nbins = _half_fftlen + 1; // including DC and Nyquist // The optional <_binmaptable> has <control_table_size> elements, each giving // the number of adjacent FFT bins controlled by that element in any of the // control tables (e.g., EQ). Copy this information into the <bin_groups> // array, which is sized to <nbins>. We ignore minfreq and maxfreq. if (_binmaptable != NULL) { int bidx = 0; for (int i = 0; i < control_table_size; i++) { int bincount = int(_binmaptable[i]); while (bincount-- > 0) { if (bidx == nbins) goto highcount; bin_groups[bidx++] = i; } } highcount: if (bidx < nbins) { const int last = control_table_size - 1; while (bidx < nbins) bin_groups[bidx++] = last; } } // If there is no binmap table... // If freq. range is full bandwidth, and control table size is >= to the // number of bins, then use linear mapping of control table slots to bins, // as in SPECTACLE v1. If table is larger than the number of bins, warn // about ignoring the extra table slots. else if (minfreq == 0 && maxfreq == _nyquist && control_table_size >= _half_fftlen) { for (int i = 0; i < _half_fftlen; i++) bin_groups[i] = i; // Let last table element control last two bins (incl. Nyquist), so that // we can ask user to size arrays to fftlen/2, rather than fftlen/2 + 1. bin_groups[_half_fftlen] = _half_fftlen - 1; if (control_table_size > _half_fftlen) { const int ignorevals = control_table_size - _half_fftlen; if (ignorevals != _prev_bg_ignorevals) { warn(instname(), "Control table of size %d too large for " "frequency range...ignoring last %d values", control_table_size, ignorevals); _prev_bg_ignorevals = ignorevals; } } } // Otherwise, there is either a freq. range that is not full bandwidth, or // the control table size is less than the number of bins. If the former, // we insure that the first array slot affects all bins whose frequencies // are below (and possibly a little above) the min. freq., and that the last // array slot affects all bins whose frequencies are above (and possibly a // little below) the max. freq. If the control table is smaller than the // number of bins within the freq. range, then we construct a one-to-one or // one-to-many mapping of control table slots to bins, arranged so that we // can control lower frequencies with greater resolution. // // Depending on the difference between the control table size and the number // of FFT bins, we create a mapping that begins linearly and grows // arithmetically after a certain point. The purpose is to get higher // resolution in the lower frequencies of the range, where it matters most. // So near the low end of the range, there is a one-to-one mapping of control // array slots to FFT bins. Near the high end of the range, one control // array slot affects many FFT bins. We attempt to transition smoothly // between these two extremes. As an example, the number of FFT bins // controlled by each slot of a control table array might look like this: // // number of bins spanned by each cntl slot // (lowshelf) 1 1 1 1 1 1 1 1 1 1 1 2 3 4 5 6 7 8 9 10 14 (highshelf) // // The scheme isn't perfect -- e.g., the penultimate slot might control more // than you would expect -- but it does guarantee that each control table // slot affects at least one FFT bin. else { const int lowshelfbin = closest_bin(minfreq); const int highshelfbin = closest_bin(maxfreq); int cntltablen = control_table_size; const float endflatrange = minfreq + (cntltablen * _fund_anal_freq); bool linear_map = true; if (endflatrange > maxfreq - _fund_anal_freq) { const int ignorevals = int((endflatrange - maxfreq) / _fund_anal_freq); if (ignorevals > 0) { cntltablen = control_table_size - ignorevals; if (ignorevals != _prev_bg_ignorevals) { warn(instname(), "Control table of size %d too large for " "frequency range...ignoring last %d values", control_table_size, ignorevals); _prev_bg_ignorevals = ignorevals; } } } else linear_map = false; #ifdef DEBUG_BINGROUPS printf("lowbin=%d, highbin=%d, endflatrange=%f, map=%s\n", lowshelfbin, highshelfbin, endflatrange, linear_map ? "linear" : "nonlinear"); #endif // assign low shelf group int bidx = 0; while (bidx <= lowshelfbin) bin_groups[bidx++] = 0; // assign interior groups if (linear_map) { int bingroup = 1; while (bidx < highshelfbin) { bin_groups[bidx++] = bingroup; if (bingroup < cntltablen - 2) bingroup++; } } else { // higher array slots control more and more bins const int extrabins = (highshelfbin - lowshelfbin) - cntltablen; assert(extrabins > 0); const int nsums = int(sqrt(2 * extrabins) + 2); // Compute the sum of integers for n in [nsums-2, nsums); // formula is the closed-form equation: x = n * (n + 1) / 2. const int an = nsums - 2; const int bn = nsums - 1; const int a = (an * (an + 1)) / 2; const int b = (bn * (bn + 1)) / 2; int cntlspan = 0, binspan = 0; if (a > extrabins) { cntlspan = an; binspan = a; } else if (b > extrabins) { cntlspan = bn; binspan = b; } else assert(0); // this should never happen, but if it does, we'll know #ifdef DEBUG_BINGROUPS printf("nbins=%d, lowbin=%d, highbin=%d, tablen=%d, extrabins=%d, " "cntlspan=%d, binspan=%d\n\n", nbins, lowshelfbin, highshelfbin, cntltablen, extrabins, cntlspan, binspan); #endif // first, linear mapping const int end = (cntltablen - cntlspan) - 1; int bingroup = 1; while (bingroup < end) bin_groups[bidx++] = bingroup++; // then map using sum-of-integers series int incr = 1; int count = 0; while (bidx < highshelfbin) { bin_groups[bidx++] = bingroup; if (++count == incr) { count = 0; if (bingroup < cntltablen - 2) bingroup++; incr++; } } } // assign high shelf group const int last = cntltablen - 1; while (bidx < nbins) bin_groups[bidx++] = last; } // print a user-readable listing of bin groups if (_print_stats) { printf("\n%s: %s table bin groups\n", instname(), type); printf("------------------------------------------\n"); int cntltabslot = 0; int startbin = 0; for (int i = 0; i < nbins; i++) { const int thisslot = bin_groups[i]; if (thisslot > cntltabslot) { // print stats for previous control table slot <cntltabslot> const int startfreq = int(_fund_anal_freq * startbin + 0.5f); if (i - startbin > 1) { const int endfreq = int(_fund_anal_freq * (i - 1) + 0.5f); printf(" [%d]\t%d-%d Hz (%d bins)\n", cntltabslot, startfreq, endfreq, i - startbin); } else printf(" [%d]\t%d Hz\n", cntltabslot, startfreq); cntltabslot = thisslot; startbin = i; } } // print stats for last control table slot const int startfreq = int(_fund_anal_freq * startbin + 0.5f); if (nbins - startbin > 1) printf(" [%d]\t%d-%d Hz (%d bins)\n\n", cntltabslot, startfreq, int(_nyquist), nbins - startbin); else printf(" [%d]\t%d Hz\n\n", cntltabslot, startfreq); fflush(stdout); } #ifdef DEBUG_BINGROUPS printf("bin_groups[] -----------------------------\n"); for (int i = 0; i < nbins; i++) printf("[%d] %d (%f)\n", i, bin_groups[i], i * _fund_anal_freq); #endif #ifdef CHECK_BINGROUPS for (int i = 1; i < nbins; i++) { int diff = bin_groups[i] - bin_groups[i - 1]; if (diff < 0 || diff > 1) printf("bin group (%p) index %d not 0 or 1 greater than prev entry\n", bin_groups, i); } #endif }
/* -------------------------------------------------------------- pre_init -- */ int TVSPECTACLE :: pre_init(double p[], int n_args) { wetdry = n_args > 9 ? p[9] : 1.0; /* default is 1 */ inchan = n_args > 10 ? (int) p[10] : 0; /* default is chan 0 */ pctleft = n_args > 11 ? p[11] : 0.5; /* default is center */ if (wetdry < 0.0 || wetdry > 1.0) return die(instname(), "Wet/dry must be between 0 and 1."); eqtableA = floc(3); if (eqtableA) { int len = fsize(3); eqtableA = resample_functable(eqtableA, len, half_fft_len); } else return die(instname(), "You haven't made EQ function A (table 3)."); eqtableB = floc(6); if (eqtableB) { int len = fsize(6); eqtableB = resample_functable(eqtableB, len, half_fft_len); } else return die(instname(), "You haven't made EQ function B (table 6)."); deltimetableA = floc(4); if (deltimetableA) { int len = fsize(4); deltimetableA = resample_functable(deltimetableA, len, half_fft_len); } else return die(instname(), "You haven't made delay time function A (table 4)."); deltimetableB = floc(7); if (deltimetableB) { int len = fsize(7); deltimetableB = resample_functable(deltimetableB, len, half_fft_len); } else return die(instname(), "You haven't made delay time function B (table 7)."); /* Compute maximum delay lag and create delay lines for FFT magnitude and phase values. Make ringdur at least as long as the longest delay time. Remember that these delays function at the decimation rate, not at audio rate. */ maxdelsamps = (long) (MAXDELTIME * SR / decimation + 0.5); float maxtime = 0.0; for (int i = 0; i < half_fft_len; i++) { /* Check delay time table A. */ float deltime = deltimetableA[i]; if (deltime < 0.0 || deltime > MAXDELTIME) return die(instname(), "Delay times must be >= 0 and <= %g. (The value in " "table A at %d is %g.)", MAXDELTIME, i, deltime); float samps = deltime * SR / (float) decimation; assert(samps <= maxdelsamps); if (deltime > maxtime) maxtime = deltime; /* Check delay time table B. */ deltime = deltimetableB[i]; if (deltime < 0.0 || deltime > MAXDELTIME) return die(instname(), "Delay times must be >= 0 and <= %g. (The value in " "table B at %d is %g.)", MAXDELTIME, i, deltime); samps = deltime * SR / (float) decimation; assert(samps <= maxdelsamps); if (deltime > maxtime) maxtime = deltime; mag_delay[i] = new DLineN(maxdelsamps); phase_delay[i] = new DLineN(maxdelsamps); } if (ringdur < maxtime) ringdur = maxtime; /* but will still cut off any trailing feedback */ DPRINT3("decimation=%d, maxdelsamps=%ld, ringdur=%g\n", decimation, maxdelsamps, ringdur); feedbacktableA = floc(5); if (feedbacktableA) { int len = fsize(5); feedbacktableA = resample_functable(feedbacktableA, len, half_fft_len); } else return die(instname(), "You haven't made delay feedback function A (table 5)."); feedbacktableB = floc(8); if (feedbacktableB) { int len = fsize(8); feedbacktableB = resample_functable(feedbacktableB, len, half_fft_len); } else return die(instname(), "You haven't made delay feedback function B (table 8)."); eqcurve = floc(9); if (eqcurve) { int len = fsize(9); if (check_curve(eqcurve, len, 0.0, 1.0)) rtcmix_warn(instname(), "EQ curve values must be between 0 and 1.\n" "Fixing..."); tableset(SR, inputdur + ringdur, len, eqcurvetabs); eq_curve_weight = eqcurve[0]; } else { rtcmix_advise(instname(), "Setting EQ table curve to all 0's (no table 9)."); eq_curve_weight = 0.0; } deltimecurve = floc(10); if (deltimecurve) { int len = fsize(10); if (check_curve(deltimecurve, len, 0.0, 1.0)) rtcmix_warn(instname(), "Delay time curve values must be between 0 and 1.\n" "Fixing..."); tableset(SR, inputdur + ringdur, len, deltimecurvetabs); deltime_curve_weight = deltimecurve[0]; } else { rtcmix_advise(instname(), "Setting EQ table curve to all 0's (no table 10)."); deltime_curve_weight = 0.0; } feedbackcurve = floc(11); if (feedbackcurve) { int len = fsize(11); if (check_curve(feedbackcurve, len, 0.0, 1.0)) rtcmix_warn(instname(), "Feedback curve values must be between 0 and 1.\n" "Fixing..."); tableset(SR, inputdur + ringdur, len, feedbackcurvetabs); feedback_curve_weight = feedbackcurve[0]; } else { rtcmix_advise(instname(), "Setting EQ table curve to all 0's (no table 11)."); feedback_curve_weight = 0.0; } //printf("deltimecurvetabs: %f, %f\n", deltimecurvetabs[0], deltimecurvetabs[1]); return 0; }
int SPECTACLE2::subinit(double p[], int n_args) { _eqtable = (double *) getPFieldTable(10, &_eqtablen); if (!_eqtable) _eqconst = p[10]; int deltimetablen; _deltimetable = (double *) getPFieldTable(11, &deltimetablen); if (!_deltimetable) _deltimeconst = p[11]; // read later in this function int feedbacktablen; _feedbacktable = (double *) getPFieldTable(12, &feedbacktablen); if (!_feedbacktable) _feedbackconst = p[12]; // Delay and feedback tables, if they exist, must be the same size. int cntltablen = 0; if (_deltimetable) cntltablen = deltimetablen; if (_feedbacktable) { cntltablen = feedbacktablen; if (_deltimetable && (feedbacktablen != deltimetablen)) return die(instname(), "Delay time and feedback tables must be the " "same size."); } _control_table_size = cntltablen; int binmaptablen; double *binmaptable = (double *) getPFieldTable(17, &binmaptablen); if (binmaptable) { if (binmaptablen != _eqtablen || binmaptablen != _control_table_size) die(instname(), "The bin-mapping table (p17) must be the same size as " "the EQ and delay tables (p10-12)."); set_binmap_table(binmaptable); if (p[13] != 0.0 || (p[14] != 0.0 || p[14] != _nyquist) || p[15] != 0.0 || (p[16] != 0.0 || p[16] != _nyquist)) rtcmix_warn(instname(), "Use of the bin-mapping table ignores the freq. " "ranges set in p13-16."); } // Init delay minfreq and maxfreq, so that bin groups will be ready for use // below. This calls update_bin_groups, which reads _control_table_size. set_freqrange(p[15], p[16]); // Compute maximum delay lag and create delay lines for FFT magnitude // and phase values. Make ringdur at least as long as the longest // delay time. Remember that these delays function at the decimation // rate, not at the audio rate, so the memory footprint is not as large // as you would expect -- about 44100 samples per second at fftlen=1024, // overlap=2 and SR=44100. // Set max delay time and bounds-check initial state of delay time array. // Also increase ringdur to accommodate longest delay time, if necessary. // Note that if user updates delay time table while running, values are // pinned to max delay time without notification. _maxdelsamps = long(kMaxDelayTime * SR / float(_decimation) + 0.5); float maxtime = 0.0f; float deltime = _deltimeconst; for (int i = 0; i <= _half_fftlen; i++) { if (_deltimetable) deltime = _deltimetable[_bin_groups[i]]; if (deltime < 0.0f || deltime > kMaxDelayTime) return die(instname(), "Delay times must be between 0 and %g seconds.", kMaxDelayTime); if (deltime > maxtime) maxtime = deltime; } float ringdur = p[5]; if (ringdur < maxtime) ringdur = maxtime; // but will still cut off any trailing feedback set_ringdur(ringdur); DPRINT3("decimation=%d, _maxdelsamps=%ld, ringdur=%g\n", _decimation, _maxdelsamps, ringdur); return 0; }
// --------------------------------------------------------------------- init -- int SpectacleBase::init(int fftlen, int windowlen, int overlap, float srate) { _fftlen = fftlen; _window_len = windowlen; _overlap = overlap; // Make sure FFT length is a power of 2 <= kMaxFFTLen. bool valid = false; for (int x = 1; x <= kMaxFFTLen; x *= 2) { if (_fftlen == x) { valid = true; break; } } if (!valid) { post("%s: FFT length must be a power of two <= %d. Setting to 1024...", instname(), kMaxFFTLen); _fftlen = 1024; } _half_fftlen = _fftlen / 2; // Make sure window length is a power of 2 >= FFT length. valid = false; for (int x = _fftlen; x <= kMaxWindowLen; x *= 2) { if (_window_len == x) { valid = true; break; } } if (!valid) { post("%s: Window length must be a power of two >= FFT length (%d)\n" "and <= %d. Setting to 2048...", instname(), _fftlen, kMaxWindowLen); _window_len = _fftlen * 2; } // Make sure _overlap is a power of 2 in allowed range. valid = false; for (int x = kMinOverlap; x <= kMaxOverlap; x *= 2) { if (_overlap == x) { valid = true; break; } } if (!valid) { post("%s: Overlap must be a power of two between %d and %d. " "Setting to 2...", instname(), kMinOverlap, kMaxOverlap); _overlap = 2; } _bin_groups = new int [_half_fftlen]; set_srate(srate); set_freqrange(0.0f, 0.0f); // derive decimation from overlap _decimation = int(_fftlen / _overlap); _window_len_minus_decimation = _window_len - _decimation; DPRINT2("_fftlen=%d, _decimation=%d", _fftlen, _decimation); _input = new float [_window_len]; // interior input buffer _output = new float [_window_len]; // interior output buffer if (_input == NULL || _output == NULL) return -1; for (int i = 0; i < _window_len; i++) _input[i] = _output[i] = 0.0f; // Read index chases write index by _decimation; add 2 extra locations to // keep read point from stepping on write point. Verify with asserts in // increment_out_*_index(). _outframes = _decimation + 2; _out_read_index = _outframes - _decimation; _out_write_index = 0; _outbuf = new float [_outframes]; if (_outbuf == NULL) return -1; for (int i = 0; i < _outframes; i++) _outbuf[i] = 0.0f; DPRINT1("_outframes: %d", _outframes); _anal_window = new float [_window_len]; _synth_window = new float [_window_len]; if (_anal_window == NULL || _synth_window == NULL) return -1; if (make_windows() != 0) return -1; _bucket = new Obucket(_decimation, process_wrapper, (void *) this); _fft = new Offt(_fftlen); _fft_buf = _fft->getbuf(); return 0; }