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 }
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 }