1#ifndef FASTLED_INTERNAL
2#define FASTLED_INTERNAL
29#ifndef FL_AUDIO_SAMPLE_RATE
30#define FL_AUDIO_SAMPLE_RATE 44100
33#ifndef FL_FFT_SAMPLE_RATE
34#define FL_FFT_SAMPLE_RATE FL_AUDIO_SAMPLE_RATE
38#define FL_FFT_SAMPLES 512
42#define FL_FFT_BANDS 16
46#define FL_FFT_MIN_VAL 5000
49#ifndef FL_FFT_PRINT_HEADER
50#define FL_FFT_PRINT_HEADER 1
61 Context(
int samples,
int bands,
float fmin,
float fmax,
int sample_rate,
72 FL_WARN(
"Failed to allocate Impl context");
81 initNaive(samples, bands, fmin, fmax, sample_rate);
84 initHybrid(samples, bands, fmin, fmax, sample_rate);
90 FL_WARN(
"Mode::AUTO should have been resolved");
102 for (
int i = 0; i < static_cast<int>(
mOctaves.size()); i++) {
132 FL_WARN(
"Mode::AUTO should have been resolved");
145 ss <<
"Impl Frequency Bands (CQ log-spaced): ";
150 ss << f_low <<
"Hz-" << f_high <<
"Hz, ";
204 float denom = 2.0f *
static_cast<float>(bands - 1);
209 for (
int i = 1; i < bands; i++) {
212 expf(logRatio * (2.0f *
static_cast<float>(i) - 1.0f) /
222 float nyquist =
static_cast<float>(
mSampleRate) / 2.0f;
250 const int numRawBins = N / 2 + 1;
280 for (
int i = 0; i < bands; ++i) {
281 rawBins[i] =
static_cast<float>(s.
rawBinsI[i]);
307 const int numRawBins = fftSize / 2 + 1;
326 const int bands =
mCqCfg.bands;
329 for (
int i = 0; i < bands; ++i) {
333 rawBins[i] =
static_cast<float>(
fastMag(real, imag));
335 float r2 = float(real * real);
336 float i2 = float(imag * imag);
337 rawBins[i] =
sqrt(r2 + i2);
346 u32 a = (re >= 0) ?
static_cast<u32
>(re) :
static_cast<u32
>(-re);
347 u32 b = (im >= 0) ?
static_cast<u32
>(im) :
static_cast<u32
>(-im);
348 u32 mx = (a > b) ? a : b;
349 u32 mn = (a <= b) ? a : b;
350 static constexpr u16x16 kMinWeight(0.40625f);
351 return static_cast<u16
>(mx + (kMinWeight *
static_cast<i32
>(mn)).to_int());
360 if (
x == 0)
return 0.0f;
366 if (v >= 0x10000u) { v >>= 16; msb += 16; }
367 if (v >= 0x100u) { v >>= 8; msb += 8; }
368 if (v >= 0x10u) { v >>= 4; msb += 4; }
369 if (v >= 0x4u) { v >>= 2; msb += 2; }
370 if (v >= 0x2u) { msb += 1; }
376 t_raw = (
x >> (msb - 16)) - 65536u;
378 t_raw = (
x << (16 - msb)) - 65536u;
384 static constexpr u16x16 one(1.0f);
388 u16x16 correction = prod * kCorrection;
393 (
static_cast<u32
>(msb) << 16) + frac.
raw());
396#if FASTLED_FFT_PRECISION == FASTLED_FFT_FIXED16
399 u16x16 db = log2_val * kDbScale;
402 return 6.02060f * log2_val.
to_float();
411 for (
int i = 0; i < n; ++i) {
422 for (
int i = 0; i < n; ++i) {
423 mag[i] =
fastMag(re[i], im[i]);
431 for (
int i = 0; i < n; ++i) {
440 const int numRawBins = fftN / 2 + 1;
441 lut.resize(numRawBins);
442 const u16x16 rawBinHz(fs /
static_cast<float>(fftN));
444 for (
int k = 0; k < numRawBins; ++k) {
445 u16x16 freq = rawBinHz *
static_cast<u32
>(k);
447 int lo = binStart, hi = binEnd - 1;
449 int mid = (lo + hi + 1) / 2;
455 lut[k] =
static_cast<u8>(lo);
462 const int numRawBins = fftN / 2 + 1;
464 lut.resize(numRawBins);
467 static_cast<float>(fftN));
468 const u16x16 halfBin = rawBinHz >> 1;
472 (
mFmax -
mFmin) /
static_cast<float>(numLinearBins));
476 if (fminFP > halfBin) {
484 for (
int k = 0; k < numRawBins; ++k) {
485 u16x16 freq = rawBinHz *
static_cast<u32
>(k);
491 int linIdx =
static_cast<int>(
492 ((freq - fminFP) / linearBinHz).to_int());
493 if (linIdx >= numLinearBins)
494 linIdx = numLinearBins - 1;
495 lut[k] =
static_cast<u8>(linIdx);
507 for (
int n = 0; n < N; ++n) {
513 const FP two_pi(6.2831853f);
514 const FP invNm1 = FP(1) / FP(N - 1);
515 const FP phase_step = two_pi * invNm1;
518 constexpr FP bh_a0(0.35875f);
519 constexpr FP bh_a1(0.48829f);
520 constexpr FP bh_a2(0.14128f);
521 constexpr FP bh_a3(0.01168f);
523 constexpr FP half(0.5f);
524 constexpr FP one(1.0f);
527 for (
int n = 0; n < N; ++n) {
532 FP phase2 = phase + phase;
533 FP phase3 = phase2 + phase;
534 w = bh_a0 - bh_a1 * FP::cos(phase)
535 + bh_a2 * FP::cos(phase2)
536 - bh_a3 * FP::cos(phase3);
541 w = half * (one - FP::cos(phase));
545 if (raw < 0) raw = 0;
546 if (raw > 65535) raw = 65535;
547 win[n] =
alpha16(
static_cast<unsigned short>(raw));
548 phase = phase + phase_step;
556 for (
int i = 0; i < N; ++i) {
558 win[i].scale_signed(
static_cast<int>(samples[i])));
588 int numOctaves =
static_cast<int>(
floorf(
log2f(fmax / fmin)));
593 float logRatio =
logf(fmax / fmin);
595 for (
int i = 0; i < bands; i++) {
598 expf(logRatio *
static_cast<float>(i) /
599 static_cast<float>(bands - 1));
604 for (
int i = 0; i < bands; i++) {
606 static_cast<int>(
floorf(
log2f(centerFreqs[i] / fmin)));
609 if (
oct >= numOctaves)
610 oct = numOctaves - 1;
617 for (
int oct = 0;
oct < numOctaves;
oct++) {
618 int first = -1, last = -1;
619 for (
int i = 0; i < bands; i++) {
620 if (binOctave[i] ==
oct) {
644 int decimExp = numOctaves - 1 -
oct;
646 static_cast<float>(sr) /
647 static_cast<float>(1 << decimExp);
651 oi.
cfg.
fmin = centerFreqs[first];
653 : centerFreqs[first] * 2.0f;
654 oi.
cfg.
fs = effectiveFs;
667 const float mainLobeHz = 4.0f *
static_cast<float>(sr) /
668 static_cast<float>(samples);
672 FL_ASSERT(bands <= 0xFFFF,
"CQ_OCTAVE band count exceeds u16 index range");
677 for (
int i = 0; i < bands; ++i) {
678 while (centerFreqs[i] - centerFreqs[firstNearby] > mainLobeHz) {
681 if (lastNearby < i) lastNearby = i;
682 while (lastNearby + 1 < bands &&
683 centerFreqs[lastNearby + 1] - centerFreqs[i] <= mainLobeHz) {
695 const int numOctaves =
static_cast<int>(
mOctaves.size());
696 const int numRawBins = N / 2 + 1;
702 for (
int i = 0; i < N; i++) {
704 (i < static_cast<int>(buffer.size())) ? buffer[i] : 0;
733 for (
int oct = numOctaves - 1;
oct >= 0;
oct--) {
738 if (
oct != numOctaves - 1) {
740 workLen = workLen / 2;
742 for (
int i = workLen; i < N; i++)
750 for (
int i = 0; i < oi.
numBins; i++) {
755 rawBins[binIdx] =
static_cast<float>(
fastMag(real, imag));
757 float r2 = float(real * real);
758 float i2 = float(imag * imag);
759 rawBins[binIdx] =
sqrt(r2 + i2);
781 static constexpr float kCoverageGain = 0.02f;
787 s.
cqSnapshot[i] =
static_cast<u32
>(rawBins[i]);
797 while (tail > head &&
804 while (head < tail &&
814 rawBins[i] =
static_cast<float>(s.
rawBinsI[i]) * kCoverageGain;
818 static constexpr float kDetectedCqMagnitude = 20.0f;
819 float coverage =
static_cast<float>(s.
rawBinsI[i]) * kCoverageGain;
820 if (localCq < kDetectedCqMagnitude && coverage > rawBins[i]) {
821 rawBins[i] = coverage;
837 float logRatio =
logf(fmax / fmin);
841 for (
int i = 0; i < bands; i++) {
843 fmin *
expf(logRatio *
static_cast<float>(i) /
844 static_cast<float>(bands - 1));
850 float bassMidFreq = fmin * 4.0f;
851 float midUpperFreq = fmin * 8.0f;
854 if (midUpperFreq >= fmax) midUpperFreq = fmax * 0.5f;
855 if (bassMidFreq >= midUpperFreq) bassMidFreq = midUpperFreq * 0.5f;
859 for (
int i = 0; i < bands; i++) {
860 if (centerFreqs[i] < bassMidFreq)
865 if (centerFreqs[i] < midUpperFreq)
882 float denom = 2.0f *
static_cast<float>(bands - 1);
884 for (
int i = 1; i < bands; i++) {
886 fmin *
expf(logRatio *
887 (2.0f *
static_cast<float>(i) - 1.0f) / denom);
893 float nyquist =
static_cast<float>(sr) / 2.0f;
925 static_cast<float>(sr),
937 samples,
static_cast<float>(sr),
948 for (
int i = 0; i < bands; ++i) {
963 const int numRawBins = N / 2 + 1;
996 for (
int i = 0; i < N; i++) {
998 (i < static_cast<int>(buffer.size())) ? buffer[i] : 0;
1008 int midRawBins = midFftN / 2 + 1;
1050 rawBins[i] =
static_cast<float>(s.
rawBinsI[i]);
1067 int bands = binEnd - binStart;
1068 normFactors.resize(binEnd);
1069 for (
int i = 0; i < binEnd; ++i) {
1070 normFactors[i] = 1.0f;
1074 const int numRawBins = fftN / 2 + 1;
1075 const u16x16 rawBinHz(fs /
static_cast<float>(fftN));
1076 const u16x16 halfBin = rawBinHz >> 1;
1081 if (loEdge > halfBin) {
1082 kStart =
static_cast<int>(
1085 int kEnd =
static_cast<int>(
1087 if (kEnd > numRawBins) kEnd = numRawBins;
1090 for (
int k = kStart; k < kEnd; ++k) {
1091 counts[lut[k]] += 1.0f;
1094 for (
int i = binStart; i < binEnd; ++i) {
1095 normFactors[i] = (counts[i] > 0.0f) ? 1.0f / counts[i] : 1.0f;
1103 int binStart,
int binEnd,
1105 const int numRawBins = fftN / 2 + 1;
1107 const u16x16 rawBinHz(fs /
static_cast<float>(fftN));
1108 const u16x16 halfBin = rawBinHz >> 1;
1113 if (loEdge > halfBin) {
1114 kStart =
static_cast<int>(
1117 int kEnd =
static_cast<int>(
1119 if (kEnd > numRawBins) kEnd = numRawBins;
1121 for (
int k = kStart; k < kEnd; ++k) {
1122 rawBinsI[lut[k]] +=
static_cast<u32
>(mag[k]);
1130 linBins.
resize(numLinearBins);
1135 for (
int i = 0; i < numLinearBins; ++i) {
1144 for (
int i = 0; i < numLinearBins; ++i) {
1145 linBins[i] =
static_cast<float>(linBinsI[i]);
1154 int outLen = len / 2;
1155 for (
int i = 0; i < outLen; i++) {
1157 auto s = [&](
int offset) -> i32 {
1160 if (j >= len) j = len - 1;
1163 i32 val = -s(-3) + 9*s(-1) + 16*s(0) + 9*s(1) - s(3);
1239 FL_WARN(
"Impl context is not initialized");
1252 auto &audio_sample =
sample.pcm();
1254 return run(slice, out);
1259 return Impl::Result(
false,
"Impl context is not initialized");
1262 FL_WARN(
"Impl sample size mismatch");
1263 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.
void kiss_fftr(kiss_fftr_cfg st, const kiss_fft_scalar *timedata, kiss_fft_cpx *freqdata)
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...
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