1#ifndef FASTLED_INTERNAL
2#define FASTLED_INTERNAL
30#ifndef FL_AUDIO_SAMPLE_RATE
31#define FL_AUDIO_SAMPLE_RATE 44100
34#ifndef FL_FFT_SAMPLE_RATE
35#define FL_FFT_SAMPLE_RATE FL_AUDIO_SAMPLE_RATE
39#define FL_FFT_SAMPLES 512
43#define FL_FFT_BANDS 16
47#define FL_FFT_MIN_VAL 5000
50#ifndef FL_FFT_PRINT_HEADER
51#define FL_FFT_PRINT_HEADER 1
62 Context(
int samples,
int bands,
float fmin,
float fmax,
int sample_rate,
73 FL_WARN(
"Failed to allocate Impl context");
82 initNaive(samples, bands, fmin, fmax, sample_rate);
85 initHybrid(samples, bands, fmin, fmax, sample_rate);
91 FL_WARN(
"Mode::AUTO should have been resolved");
103 for (
int i = 0; i < static_cast<int>(
mOctaves.size()); i++) {
133 FL_WARN(
"Mode::AUTO should have been resolved");
146 ss <<
"Impl Frequency Bands (CQ log-spaced): ";
151 ss << f_low <<
"Hz-" << f_high <<
"Hz, ";
205 float denom = 2.0f *
static_cast<float>(bands - 1);
210 for (
int i = 1; i < bands; i++) {
213 expf(logRatio * (2.0f *
static_cast<float>(i) - 1.0f) /
223 float nyquist =
static_cast<float>(
mSampleRate) / 2.0f;
251 const int numRawBins = N / 2 + 1;
281 for (
int i = 0; i < bands; ++i) {
282 rawBins[i] =
static_cast<float>(s.
rawBinsI[i]);
308 const int numRawBins = fftSize / 2 + 1;
327 const int bands =
mCqCfg.bands;
330 for (
int i = 0; i < bands; ++i) {
334 rawBins[i] =
static_cast<float>(
fastMag(real, imag));
336 float r2 = float(real * real);
337 float i2 = float(imag * imag);
338 rawBins[i] =
sqrt(r2 + i2);
347 u32 a = (re >= 0) ?
static_cast<u32
>(re) :
static_cast<u32
>(-re);
348 u32 b = (im >= 0) ?
static_cast<u32
>(im) :
static_cast<u32
>(-im);
349 u32 mx = (a > b) ? a : b;
350 u32 mn = (a <= b) ? a : b;
351 static constexpr u16x16 kMinWeight(0.40625f);
352 return static_cast<u16
>(mx + (kMinWeight *
static_cast<i32
>(mn)).to_int());
361 if (
x == 0)
return 0.0f;
367 if (v >= 0x10000u) { v >>= 16; msb += 16; }
368 if (v >= 0x100u) { v >>= 8; msb += 8; }
369 if (v >= 0x10u) { v >>= 4; msb += 4; }
370 if (v >= 0x4u) { v >>= 2; msb += 2; }
371 if (v >= 0x2u) { msb += 1; }
377 t_raw = (
x >> (msb - 16)) - 65536u;
379 t_raw = (
x << (16 - msb)) - 65536u;
385 static constexpr u16x16 one(1.0f);
389 u16x16 correction = prod * kCorrection;
394 (
static_cast<u32
>(msb) << 16) + frac.
raw());
397#if FASTLED_FFT_PRECISION == FASTLED_FFT_FIXED16
400 u16x16 db = log2_val * kDbScale;
403 return 6.02060f * log2_val.
to_float();
412 for (
int i = 0; i < n; ++i) {
423 for (
int i = 0; i < n; ++i) {
424 mag[i] =
fastMag(re[i], im[i]);
432 for (
int i = 0; i < n; ++i) {
441 const int numRawBins = fftN / 2 + 1;
442 lut.resize(numRawBins);
443 const u16x16 rawBinHz(fs /
static_cast<float>(fftN));
445 for (
int k = 0; k < numRawBins; ++k) {
446 u16x16 freq = rawBinHz *
static_cast<u32
>(k);
448 int lo = binStart, hi = binEnd - 1;
450 int mid = (lo + hi + 1) / 2;
456 lut[k] =
static_cast<u8>(lo);
463 const int numRawBins = fftN / 2 + 1;
465 lut.resize(numRawBins);
468 static_cast<float>(fftN));
469 const u16x16 halfBin = rawBinHz >> 1;
473 (
mFmax -
mFmin) /
static_cast<float>(numLinearBins));
477 if (fminFP > halfBin) {
485 for (
int k = 0; k < numRawBins; ++k) {
486 u16x16 freq = rawBinHz *
static_cast<u32
>(k);
492 int linIdx =
static_cast<int>(
493 ((freq - fminFP) / linearBinHz).to_int());
494 if (linIdx >= numLinearBins)
495 linIdx = numLinearBins - 1;
496 lut[k] =
static_cast<u8>(linIdx);
508 for (
int n = 0; n < N; ++n) {
514 const FP two_pi(6.2831853f);
515 const FP invNm1 = FP(1) / FP(N - 1);
516 const FP phase_step = two_pi * invNm1;
519 constexpr FP bh_a0(0.35875f);
520 constexpr FP bh_a1(0.48829f);
521 constexpr FP bh_a2(0.14128f);
522 constexpr FP bh_a3(0.01168f);
524 constexpr FP half(0.5f);
525 constexpr FP one(1.0f);
528 for (
int n = 0; n < N; ++n) {
533 FP phase2 = phase + phase;
534 FP phase3 = phase2 + phase;
535 w = bh_a0 - bh_a1 * FP::cos(phase)
536 + bh_a2 * FP::cos(phase2)
537 - bh_a3 * FP::cos(phase3);
542 w = half * (one - FP::cos(phase));
546 if (raw < 0) raw = 0;
547 if (raw > 65535) raw = 65535;
548 win[n] =
alpha16(
static_cast<unsigned short>(raw));
549 phase = phase + phase_step;
557 for (
int i = 0; i < N; ++i) {
559 win[i].scale_signed(
static_cast<int>(samples[i])));
589 int numOctaves =
static_cast<int>(
floorf(
log2f(fmax / fmin)));
594 float logRatio =
logf(fmax / fmin);
596 for (
int i = 0; i < bands; i++) {
599 expf(logRatio *
static_cast<float>(i) /
600 static_cast<float>(bands - 1));
605 for (
int i = 0; i < bands; i++) {
607 static_cast<int>(
floorf(
log2f(centerFreqs[i] / fmin)));
610 if (
oct >= numOctaves)
611 oct = numOctaves - 1;
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) {
645 int decimExp = numOctaves - 1 -
oct;
647 static_cast<float>(sr) /
648 static_cast<float>(1 << decimExp);
652 oi.
cfg.
fmin = centerFreqs[first];
654 : centerFreqs[first] * 2.0f;
655 oi.
cfg.
fs = effectiveFs;
668 const float mainLobeHz = 4.0f *
static_cast<float>(sr) /
669 static_cast<float>(samples);
673 FL_ASSERT(bands <= 0xFFFF,
"CQ_OCTAVE band count exceeds u16 index range");
678 for (
int i = 0; i < bands; ++i) {
679 while (centerFreqs[i] - centerFreqs[firstNearby] > mainLobeHz) {
682 if (lastNearby < i) lastNearby = i;
683 while (lastNearby + 1 < bands &&
684 centerFreqs[lastNearby + 1] - centerFreqs[i] <= mainLobeHz) {
696 const int numOctaves =
static_cast<int>(
mOctaves.size());
697 const int numRawBins = N / 2 + 1;
703 for (
int i = 0; i < N; i++) {
705 (i < static_cast<int>(buffer.size())) ? buffer[i] : 0;
734 for (
int oct = numOctaves - 1;
oct >= 0;
oct--) {
739 if (
oct != numOctaves - 1) {
741 workLen = workLen / 2;
743 for (
int i = workLen; i < N; i++)
751 for (
int i = 0; i < oi.
numBins; i++) {
756 rawBins[binIdx] =
static_cast<float>(
fastMag(real, imag));
758 float r2 = float(real * real);
759 float i2 = float(imag * imag);
760 rawBins[binIdx] =
sqrt(r2 + i2);
782 static constexpr float kCoverageGain = 0.02f;
788 s.
cqSnapshot[i] =
static_cast<u32
>(rawBins[i]);
798 while (tail > head &&
805 while (head < tail &&
815 rawBins[i] =
static_cast<float>(s.
rawBinsI[i]) * kCoverageGain;
819 static constexpr float kDetectedCqMagnitude = 20.0f;
820 float coverage =
static_cast<float>(s.
rawBinsI[i]) * kCoverageGain;
821 if (localCq < kDetectedCqMagnitude && coverage > rawBins[i]) {
822 rawBins[i] = coverage;
838 float logRatio =
logf(fmax / fmin);
842 for (
int i = 0; i < bands; i++) {
844 fmin *
expf(logRatio *
static_cast<float>(i) /
845 static_cast<float>(bands - 1));
851 float bassMidFreq = fmin * 4.0f;
852 float midUpperFreq = fmin * 8.0f;
855 if (midUpperFreq >= fmax) midUpperFreq = fmax * 0.5f;
856 if (bassMidFreq >= midUpperFreq) bassMidFreq = midUpperFreq * 0.5f;
860 for (
int i = 0; i < bands; i++) {
861 if (centerFreqs[i] < bassMidFreq)
866 if (centerFreqs[i] < midUpperFreq)
883 float denom = 2.0f *
static_cast<float>(bands - 1);
885 for (
int i = 1; i < bands; i++) {
887 fmin *
expf(logRatio *
888 (2.0f *
static_cast<float>(i) - 1.0f) / denom);
894 float nyquist =
static_cast<float>(sr) / 2.0f;
926 static_cast<float>(sr),
938 samples,
static_cast<float>(sr),
949 for (
int i = 0; i < bands; ++i) {
964 const int numRawBins = N / 2 + 1;
997 for (
int i = 0; i < N; i++) {
999 (i < static_cast<int>(buffer.size())) ? buffer[i] : 0;
1009 int midRawBins = midFftN / 2 + 1;
1053 rawBins[i] =
static_cast<float>(s.
rawBinsI[i]);
1070 int bands = binEnd - binStart;
1071 normFactors.resize(binEnd);
1072 for (
int i = 0; i < binEnd; ++i) {
1073 normFactors[i] = 1.0f;
1077 const int numRawBins = fftN / 2 + 1;
1078 const u16x16 rawBinHz(fs /
static_cast<float>(fftN));
1079 const u16x16 halfBin = rawBinHz >> 1;
1084 if (loEdge > halfBin) {
1085 kStart =
static_cast<int>(
1088 int kEnd =
static_cast<int>(
1090 if (kEnd > numRawBins) kEnd = numRawBins;
1093 for (
int k = kStart; k < kEnd; ++k) {
1094 counts[lut[k]] += 1.0f;
1097 for (
int i = binStart; i < binEnd; ++i) {
1098 normFactors[i] = (counts[i] > 0.0f) ? 1.0f / counts[i] : 1.0f;
1106 int binStart,
int binEnd,
1108 const int numRawBins = fftN / 2 + 1;
1110 const u16x16 rawBinHz(fs /
static_cast<float>(fftN));
1111 const u16x16 halfBin = rawBinHz >> 1;
1116 if (loEdge > halfBin) {
1117 kStart =
static_cast<int>(
1120 int kEnd =
static_cast<int>(
1122 if (kEnd > numRawBins) kEnd = numRawBins;
1124 for (
int k = kStart; k < kEnd; ++k) {
1125 rawBinsI[lut[k]] +=
static_cast<u32
>(mag[k]);
1133 linBins.
resize(numLinearBins);
1138 for (
int i = 0; i < numLinearBins; ++i) {
1147 for (
int i = 0; i < numLinearBins; ++i) {
1148 linBins[i] =
static_cast<float>(linBinsI[i]);
1157 int outLen = len / 2;
1158 for (
int i = 0; i < outLen; i++) {
1160 auto s = [&](
int offset) -> i32 {
1163 if (j >= len) j = len - 1;
1166 i32 val = -s(-3) + 9*s(-1) + 16*s(0) + 9*s(1) - s(3);
1242 FL_WARN(
"Impl context is not initialized");
1255 auto &audio_sample =
sample.pcm();
1257 return run(slice, out);
1262 return Impl::Result(
false,
"Impl context is not initialized");
1265 FL_WARN(
"Impl sample size mismatch");
1266 return Impl::Result(
false,
"Impl sample size mismatch");
fl::UIAudio audio("Audio Input")
#define FASTLED_STACK_ARRAY(TYPE, NAME, SIZE)
Stack-allocated array with automatic zero-initialization.
Unsigned alpha types with UNORM semantics (GPU industry standard).
#define FL_ASSERT(x, MSG)
static T & instance() FL_NO_EXCEPT
void setParams(float fmin, float fmax, int sampleRate) FL_NO_EXCEPT
fl::vector< float > & raw_mut() FL_NO_EXCEPT
float binBoundary(int i) const FL_NO_EXCEPT
static float fastDb(u32 x) FL_NO_EXCEPT
fl::vector< alpha16 > mHybridBassWindow
static void batchMag(const kiss_fft_scalar *re, const kiss_fft_scalar *im, u16 *mag, int n) FL_NO_EXCEPT
void run(span< const i16 > buffer, Bins *out) FL_NO_EXCEPT
void runOctaveWise(span< const i16 > buffer, Bins *out) FL_NO_EXCEPT
void computeLinearBins(const u16 *mag, int, Bins *out) FL_NO_EXCEPT
void logRebinRange(const u16 *mag, int fftN, float fs, int binStart, int binEnd, u32 *rawBinsI, const fl::vector< u8 > &lut) FL_NO_EXCEPT
fl::vector< kiss_fft_scalar > mWorkBuf
void buildLinearBinLut(fl::vector< u8 > &lut, int fftN) FL_NO_EXCEPT
void buildLogBinLut(fl::vector< u8 > &lut, int fftN, float fs, int binStart, int binEnd) FL_NO_EXCEPT
fl::vector< alpha16 > mWindowBuf
fl::size sampleSize() const FL_NO_EXCEPT
void runHybrid(span< const i16 > buffer, Bins *out) FL_NO_EXCEPT
fl::vector< u8 > mLinearBinLut
void initWindow() FL_NO_EXCEPT
fl::vector< u16 > mCoverageLast
fl::vector< alpha16 > mHybridMidWindow
fl::vector< kiss_fft_cpx > mHybridMidFftOut
fl::vector< float > mLogBinEdges
static u16 fastMag(i32 re, i32 im) FL_NO_EXCEPT
fl::vector< u8 > mLogBinLutMid
void initLogRebin() FL_NO_EXCEPT
fl::vector< kiss_fft_cpx > mHybridSmallFftOut
void initNaive(int samples, int bands, float fmin, float fmax, int sr) FL_NO_EXCEPT
static FftScratch & scratch() FL_NO_EXCEPT
static void deinterleave(const kiss_fft_cpx *cpx, kiss_fft_scalar *re, kiss_fft_scalar *im, int n) FL_NO_EXCEPT
fl::vector< float > mHybridNormUpper
fl::vector< u16x16 > mLogBinEdgesQ16
fl::vector< u16 > mCoverageFirst
fl::vector< u8 > mLogBinLutBass
void runLogRebin(span< const i16 > buffer, Bins *out) FL_NO_EXCEPT
fl::vector< OctaveInfo > mOctaves
Context(int samples, int bands, float fmin, float fmax, int sample_rate, Mode mode, Window window) FL_NO_EXCEPT
static void applyWindow(const kiss_fft_scalar *samples, const alpha16 *win, kiss_fft_scalar *out, int N) FL_NO_EXCEPT
fl::vector< u8 > mLogBinLut
fl::vector< kiss_fft_cpx > mFftOut
static void computeWindow(fl::vector< alpha16 > &win, int N, Window type) FL_NO_EXCEPT
kiss_fftr_cfg mHybridMidFft
static void decimateBy2(kiss_fft_scalar *buf, int len) FL_NO_EXCEPT
fl::string info() const FL_NO_EXCEPT
void computeBinEdgesQ16() FL_NO_EXCEPT
void initOctaveWise(int samples, int bands, float fmin, float fmax, int sr) FL_NO_EXCEPT
fl::vector< float > mHybridNormBass
fl::vector< float > mLogBinNormFactors
fl::vector< float > mHybridMergedNorm
void computeLogRebinNormFactors(fl::vector< float > &normFactors, const fl::vector< u8 > &lut, int fftN, float fs, int binStart, int binEnd) FL_NO_EXCEPT
void initHybrid(int samples, int bands, float fmin, float fmax, int sr) FL_NO_EXCEPT
void runNaive(span< const i16 > buffer, Bins *out) FL_NO_EXCEPT
kiss_fftr_cfg mHybridSmallFft
fl::vector< float > mHybridNormMid
fl::vector< kiss_fft_scalar > re
fl::vector< u16 > coverageMaxQueue
fl::vector< kiss_fft_scalar > im
fl::vector< kiss_fft_cpx > fftOut
fl::vector< float > cqSnapshot
fl::vector< kiss_fft_scalar > windowed
fl::vector< u32 > rawBinsI
Impl(const Args &args) FL_NO_EXCEPT
fl::size sampleSize() const FL_NO_EXCEPT
Result run(const Sample &sample, Bins *out) FL_NO_EXCEPT
fl::unique_ptr< Context > mContext
fl::string info() const FL_NO_EXCEPT
constexpr i32 raw() const FL_NO_EXCEPT
string str() const FL_NO_EXCEPT
constexpr u32 raw() const FL_NO_EXCEPT
static constexpr FASTLED_FORCE_INLINE u16x16 from_raw(u32 raw) FL_NO_EXCEPT
constexpr float to_float() const FL_NO_EXCEPT
constexpr u32 to_int() const FL_NO_EXCEPT
static constexpr FASTLED_FORCE_INLINE u16x16 ceil(u16x16 x) FL_NO_EXCEPT
void resize(fl::size n) FL_NO_EXCEPT
void push_back(const T &value) FL_NO_EXCEPT
void free_kernels(struct sparse_arr *kernels, struct cq_kernel_cfg cfg)
void apply_kernels(kiss_fft_cpx fft[], kiss_fft_cpx cq[], struct sparse_arr kernels[], struct cq_kernel_cfg cfg)
struct sparse_arr * generate_kernels(struct cq_kernel_cfg cfg)
struct sparse_arr * cq_kernels_t
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
Internal FastLED header for implementation files.
kiss_fftr_cfg kiss_fftr_alloc(int nfft, int inverse_fft, void *mem, size_t *lenmem)
struct kiss_fftr_state * kiss_fftr_cfg
Centralized logging categories for FastLED hardware interfaces and subsystems.
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NO_EXCEPT
bool coverageFloorApplies(u32 fftMag, u32 cqMag, u32 localCqMax) FL_NO_EXCEPT
CQ_OCTAVE coverage supplement decision (FastLED#4301/#4540), integer form used in FIXED16: the full-r...
void fl_fft_real_forward(kiss_fftr_cfg cfg, int N, const kiss_fft_scalar *in, kiss_fft_cpx *out) FL_NO_EXCEPT
Forward real-to-complex FFT.
float logf(float value) FL_NO_EXCEPT
u8 u8 fract8 blur_amount FL_NO_EXCEPT
CRGB sample(const CRGB *grid, const XYMap &xyMap, float x, float y, SampleMode mode) FL_NO_EXCEPT
Sample a pixel from a 2D CRGB grid at floating-point coordinates.
float log2f(float value) FL_NO_EXCEPT
FASTLED_FORCE_INLINE void * memset(void *s, int c, size_t n) FL_NO_EXCEPT
float floorf(float value) FL_NO_EXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type sqrt(T x) FL_NO_EXCEPT
float expf(float value) FL_NO_EXCEPT
Base definition for an LED controller.
Unsigned 16-bit alpha / brightness — UNORM16.
static void resolveModeEnums(Mode &mode, Window &window, int bands, int samples, float fmin, float fmax) FL_NO_EXCEPT