FastLED 3.10.8
Loading...
Searching...
No Matches

◆ initOctaveWise()

void fl::audio::fft::Context::initOctaveWise ( int samples,
int bands,
float fmin,
float fmax,
int sr )
inlineprivate

Definition at line 583 of file fft_impl.cpp.hpp.

584 {
585
586
587 // Use floor so the top octave covers the remaining frequency range
588 // rather than creating a tiny sliver octave with 1 bin.
589 int numOctaves = static_cast<int>(floorf(log2f(fmax / fmin)));
590 if (numOctaves < 1)
591 numOctaves = 1;
592
593 // Log-spaced center frequencies for all bins
594 float logRatio = logf(fmax / fmin);
595 FASTLED_STACK_ARRAY(float, centerFreqs, bands);
596 for (int i = 0; i < bands; i++) {
597 centerFreqs[i] =
598 fmin *
599 expf(logRatio * static_cast<float>(i) /
600 static_cast<float>(bands - 1));
601 }
602
603 // Assign each bin to an octave: oct j spans [fmin*2^j, fmin*2^(j+1))
604 FASTLED_STACK_ARRAY(int, binOctave, bands);
605 for (int i = 0; i < bands; i++) {
606 int oct =
607 static_cast<int>(floorf(log2f(centerFreqs[i] / fmin)));
608 if (oct < 0)
609 oct = 0;
610 if (oct >= numOctaves)
611 oct = numOctaves - 1;
612 binOctave[i] = oct;
613 }
614
615 // Build per-octave CQ kernel sets
616 mOctaves.resize(numOctaves);
618 for (int oct = 0; oct < numOctaves; oct++) {
619 int first = -1, last = -1;
620 for (int i = 0; i < bands; i++) {
621 if (binOctave[i] == oct) {
622 if (first < 0)
623 first = i;
624 last = i;
625 }
626 }
627
628 OctaveInfo &oi = mOctaves[oct];
629 fl::memset(&oi.cfg, 0, sizeof(oi.cfg));
630 oi.kernels = nullptr;
631 if (first < 0) {
632 oi.firstBin = 0;
633 oi.numBins = 0;
634 continue;
635 }
636
637 oi.firstBin = first;
638 oi.numBins = last - first + 1;
639 if (oi.numBins > mMaxBinsPerOctave) {
640 mMaxBinsPerOctave = oi.numBins;
641 }
642
643 // Decimation: top octave (numOctaves-1) uses original sample rate.
644 // Each lower octave halves the effective sample rate.
645 int decimExp = numOctaves - 1 - oct;
646 float effectiveFs =
647 static_cast<float>(sr) /
648 static_cast<float>(1 << decimExp);
649
650 oi.cfg.samples = samples;
651 oi.cfg.bands = oi.numBins;
652 oi.cfg.fmin = centerFreqs[first];
653 oi.cfg.fmax = (oi.numBins > 1) ? centerFreqs[last]
654 : centerFreqs[first] * 2.0f;
655 oi.cfg.fs = effectiveFs;
656 oi.cfg.min_val = FL_FFT_MIN_VAL;
657
658 oi.kernels = generate_kernels(oi.cfg);
659 }
660
661 // Pre-allocate reusable buffers
662 mWorkBuf.resize(samples);
663 mFftOut.resize(samples);
664
666 initLogRebin();
668 const float mainLobeHz = 4.0f * static_cast<float>(sr) /
669 static_cast<float>(samples);
670 // Band indices are stored as u16. `centerFreqs` above is a stack
671 // array of `bands` floats, so any band count that fits a real task
672 // stack is far below 65536.
673 FL_ASSERT(bands <= 0xFFFF, "CQ_OCTAVE band count exceeds u16 index range");
674 mCoverageFirst.resize(bands);
675 mCoverageLast.resize(bands);
676 int firstNearby = 0;
677 int lastNearby = 0;
678 for (int i = 0; i < bands; ++i) {
679 while (centerFreqs[i] - centerFreqs[firstNearby] > mainLobeHz) {
680 ++firstNearby;
681 }
682 if (lastNearby < i) lastNearby = i;
683 while (lastNearby + 1 < bands &&
684 centerFreqs[lastNearby + 1] - centerFreqs[i] <= mainLobeHz) {
685 ++lastNearby;
686 }
687 mCoverageFirst[i] = static_cast<u16>(firstNearby);
688 mCoverageLast[i] = static_cast<u16>(lastNearby);
689 }
690 // Note: CQ kernels already apply Hamming windowing in frequency domain.
691 // Adding time-domain Hanning would double-window and over-attenuate.
692 }
#define FASTLED_STACK_ARRAY(TYPE, NAME, SIZE)
Stack-allocated array with automatic zero-initialization.
Definition alloca.h:32
#define FL_ASSERT(x, MSG)
Definition assert.h:6
fl::vector< kiss_fft_scalar > mWorkBuf
void buildLinearBinLut(fl::vector< u8 > &lut, int fftN) FL_NO_EXCEPT
fl::vector< alpha16 > mWindowBuf
fl::vector< u8 > mLinearBinLut
fl::vector< u16 > mCoverageLast
void initLogRebin() FL_NO_EXCEPT
fl::vector< u16 > mCoverageFirst
fl::vector< OctaveInfo > mOctaves
fl::vector< kiss_fft_cpx > mFftOut
static void computeWindow(fl::vector< alpha16 > &win, int N, Window type) FL_NO_EXCEPT
struct sparse_arr * generate_kernels(struct cq_kernel_cfg cfg)
#define FL_FFT_MIN_VAL
float logf(float value) FL_NO_EXCEPT
Definition math.h:426
float log2f(float value) FL_NO_EXCEPT
Definition math.h:438
FASTLED_FORCE_INLINE void * memset(void *s, int c, size_t n) FL_NO_EXCEPT
Definition cstring.h:148
const oct_t oct
Definition ios.cpp.hpp:15
float floorf(float value) FL_NO_EXCEPT
Definition math.h:312
float expf(float value) FL_NO_EXCEPT
Definition math.h:406

References cq_kernel_cfg::bands, fl::audio::fft::BLACKMAN_HARRIS, buildLinearBinLut(), fl::audio::fft::Context::OctaveInfo::cfg, computeWindow(), fl::expf(), FASTLED_STACK_ARRAY, fl::audio::fft::Context::OctaveInfo::firstBin, FL_ASSERT, FL_FFT_MIN_VAL, fl::FL_NO_EXCEPT, fl::floorf(), cq_kernel_cfg::fmax, cq_kernel_cfg::fmin, cq_kernel_cfg::fs, generate_kernels(), initLogRebin(), fl::audio::fft::Context::OctaveInfo::kernels, fl::log2f(), fl::logf(), mCoverageFirst, mCoverageLast, fl::memset(), mFftOut, cq_kernel_cfg::min_val, mLinearBinLut, mMaxBinsPerOctave, mOctaves, mWindowBuf, mWorkBuf, fl::audio::fft::Context::OctaveInfo::numBins, fl::oct, and cq_kernel_cfg::samples.

Referenced by Context().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: