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, ";
195 float denom = 2.0f *
static_cast<float>(bands - 1);
200 for (
int i = 1; i < bands; i++) {
203 expf(logRatio * (2.0f *
static_cast<float>(i) - 1.0f) /
213 float nyquist =
static_cast<float>(
mSampleRate) / 2.0f;
241 const int numRawBins = N / 2 + 1;
271 for (
int i = 0; i < bands; ++i) {
272 rawBins[i] =
static_cast<float>(s.
rawBinsI[i]);
282 void initNaive(
int samples,
int bands,
float fmin,
float fmax,
int sr) {
298 const int numRawBins = fftSize / 2 + 1;
317 const int bands =
mCqCfg.bands;
320 for (
int i = 0; i < bands; ++i) {
324 rawBins[i] =
static_cast<float>(
fastMag(real, imag));
326 float r2 = float(real * real);
327 float i2 = float(imag * imag);
328 rawBins[i] =
sqrt(r2 + i2);
337 u32 a = (re >= 0) ?
static_cast<u32
>(re) :
static_cast<u32
>(-re);
338 u32 b = (im >= 0) ?
static_cast<u32
>(im) :
static_cast<u32
>(-im);
339 u32 mx = (a > b) ? a : b;
340 u32 mn = (a <= b) ? a : b;
341 static constexpr u16x16 kMinWeight(0.40625f);
342 return static_cast<u16
>(mx + (kMinWeight *
static_cast<i32
>(mn)).to_int());
351 if (
x == 0)
return 0.0f;
357 if (v >= 0x10000u) { v >>= 16; msb += 16; }
358 if (v >= 0x100u) { v >>= 8; msb += 8; }
359 if (v >= 0x10u) { v >>= 4; msb += 4; }
360 if (v >= 0x4u) { v >>= 2; msb += 2; }
361 if (v >= 0x2u) { msb += 1; }
367 t_raw = (
x >> (msb - 16)) - 65536u;
369 t_raw = (
x << (16 - msb)) - 65536u;
375 static constexpr u16x16 one(1.0f);
379 u16x16 correction = prod * kCorrection;
384 (
static_cast<u32
>(msb) << 16) + frac.
raw());
387#if FASTLED_FFT_PRECISION == FASTLED_FFT_FIXED16
390 u16x16 db = log2_val * kDbScale;
393 return 6.02060f * log2_val.
to_float();
402 for (
int i = 0; i < n; ++i) {
413 for (
int i = 0; i < n; ++i) {
414 mag[i] =
fastMag(re[i], im[i]);
422 for (
int i = 0; i < n; ++i) {
430 int binStart,
int binEnd) {
431 const int numRawBins = fftN / 2 + 1;
433 const u16x16 rawBinHz(fs /
static_cast<float>(fftN));
435 for (
int k = 0; k < numRawBins; ++k) {
436 u16x16 freq = rawBinHz *
static_cast<u32
>(k);
438 int lo = binStart, hi = binEnd - 1;
440 int mid = (lo + hi + 1) / 2;
446 lut[k] =
static_cast<u8>(lo);
453 const int numRawBins = fftN / 2 + 1;
458 static_cast<float>(fftN));
459 const u16x16 halfBin = rawBinHz >> 1;
463 (
mFmax -
mFmin) /
static_cast<float>(numLinearBins));
467 if (fminFP > halfBin) {
475 for (
int k = 0; k < numRawBins; ++k) {
476 u16x16 freq = rawBinHz *
static_cast<u32
>(k);
482 int linIdx =
static_cast<int>(
483 ((freq - fminFP) / linearBinHz).to_int());
484 if (linIdx >= numLinearBins)
485 linIdx = numLinearBins - 1;
486 lut[k] =
static_cast<u8>(linIdx);
498 for (
int n = 0; n < N; ++n) {
504 const FP two_pi(6.2831853f);
505 const FP invNm1 = FP(1) / FP(N - 1);
506 const FP phase_step = two_pi * invNm1;
509 constexpr FP bh_a0(0.35875f);
510 constexpr FP bh_a1(0.48829f);
511 constexpr FP bh_a2(0.14128f);
512 constexpr FP bh_a3(0.01168f);
514 constexpr FP half(0.5f);
515 constexpr FP one(1.0f);
518 for (
int n = 0; n < N; ++n) {
523 FP phase2 = phase + phase;
524 FP phase3 = phase2 + phase;
525 w = bh_a0 - bh_a1 * FP::cos(phase)
526 + bh_a2 * FP::cos(phase2)
527 - bh_a3 * FP::cos(phase3);
532 w = half * (one - FP::cos(phase));
536 if (raw < 0) raw = 0;
537 if (raw > 65535) raw = 65535;
538 win[n] =
alpha16(
static_cast<unsigned short>(raw));
539 phase = phase + phase_step;
547 for (
int i = 0; i < N; ++i) {
579 int numOctaves =
static_cast<int>(
floorf(
log2f(fmax / fmin)));
584 float logRatio =
logf(fmax / fmin);
586 for (
int i = 0; i < bands; i++) {
589 expf(logRatio *
static_cast<float>(i) /
590 static_cast<float>(bands - 1));
595 for (
int i = 0; i < bands; i++) {
597 static_cast<int>(
floorf(
log2f(centerFreqs[i] / fmin)));
600 if (
oct >= numOctaves)
601 oct = numOctaves - 1;
608 for (
int oct = 0;
oct < numOctaves;
oct++) {
609 int first = -1, last = -1;
610 for (
int i = 0; i < bands; i++) {
611 if (binOctave[i] ==
oct) {
635 int decimExp = numOctaves - 1 -
oct;
637 static_cast<float>(sr) /
638 static_cast<float>(1 << decimExp);
642 oi.
cfg.
fmin = centerFreqs[first];
644 : centerFreqs[first] * 2.0f;
645 oi.
cfg.
fs = effectiveFs;
662 const int numOctaves =
static_cast<int>(
mOctaves.size());
663 const int numRawBins = N / 2 + 1;
669 for (
int i = 0; i < N; i++) {
671 (i < static_cast<int>(buffer.
size())) ? buffer[i] : 0;
700 for (
int oct = numOctaves - 1;
oct >= 0;
oct--) {
705 if (
oct != numOctaves - 1) {
707 workLen = workLen / 2;
709 for (
int i = workLen; i < N; i++)
718 for (
int i = 0; i < oi.
numBins; i++) {
723 rawBins[binIdx] =
static_cast<float>(
fastMag(real, imag));
725 float r2 = float(real * real);
726 float i2 = float(imag * imag);
727 rawBins[binIdx] =
sqrt(r2 + i2);
742 void initHybrid(
int samples,
int bands,
float fmin,
float fmax,
int sr) {
743 float logRatio =
logf(fmax / fmin);
747 for (
int i = 0; i < bands; i++) {
749 fmin *
expf(logRatio *
static_cast<float>(i) /
750 static_cast<float>(bands - 1));
756 float bassMidFreq = fmin * 4.0f;
757 float midUpperFreq = fmin * 8.0f;
760 if (midUpperFreq >= fmax) midUpperFreq = fmax * 0.5f;
761 if (bassMidFreq >= midUpperFreq) bassMidFreq = midUpperFreq * 0.5f;
765 for (
int i = 0; i < bands; i++) {
766 if (centerFreqs[i] < bassMidFreq)
771 if (centerFreqs[i] < midUpperFreq)
788 float denom = 2.0f *
static_cast<float>(bands - 1);
790 for (
int i = 1; i < bands; i++) {
792 fmin *
expf(logRatio *
793 (2.0f *
static_cast<float>(i) - 1.0f) / denom);
799 float nyquist =
static_cast<float>(sr) / 2.0f;
831 static_cast<float>(sr),
843 samples,
static_cast<float>(sr),
854 for (
int i = 0; i < bands; ++i) {
869 const int numRawBins = N / 2 + 1;
902 for (
int i = 0; i < N; i++) {
904 (i < static_cast<int>(buffer.
size())) ? buffer[i] : 0;
914 int midRawBins = midFftN / 2 + 1;
958 rawBins[i] =
static_cast<float>(s.
rawBinsI[i]);
974 int binStart,
int binEnd) {
975 int bands = binEnd - binStart;
976 normFactors.
resize(binEnd);
977 for (
int i = 0; i < binEnd; ++i) {
978 normFactors[i] = 1.0f;
982 const int numRawBins = fftN / 2 + 1;
983 const u16x16 rawBinHz(fs /
static_cast<float>(fftN));
984 const u16x16 halfBin = rawBinHz >> 1;
989 if (loEdge > halfBin) {
990 kStart =
static_cast<int>(
993 int kEnd =
static_cast<int>(
995 if (kEnd > numRawBins) kEnd = numRawBins;
998 for (
int k = kStart; k < kEnd; ++k) {
999 counts[lut[k]] += 1.0f;
1002 for (
int i = binStart; i < binEnd; ++i) {
1003 normFactors[i] = (counts[i] > 0.0f) ? 1.0f / counts[i] : 1.0f;
1011 int binStart,
int binEnd,
1013 const int numRawBins = fftN / 2 + 1;
1015 const u16x16 rawBinHz(fs /
static_cast<float>(fftN));
1016 const u16x16 halfBin = rawBinHz >> 1;
1021 if (loEdge > halfBin) {
1022 kStart =
static_cast<int>(
1025 int kEnd =
static_cast<int>(
1027 if (kEnd > numRawBins) kEnd = numRawBins;
1029 for (
int k = kStart; k < kEnd; ++k) {
1030 rawBinsI[lut[k]] +=
static_cast<u32
>(mag[k]);
1038 linBins.
resize(numLinearBins);
1043 for (
int i = 0; i < numLinearBins; ++i) {
1052 for (
int i = 0; i < numLinearBins; ++i) {
1053 linBins[i] =
static_cast<float>(linBinsI[i]);
1062 int outLen = len / 2;
1063 for (
int i = 0; i < outLen; i++) {
1065 auto s = [&](
int offset) -> i32 {
1068 if (j >= len) j = len - 1;
1071 i32 val = -s(-3) + 9*s(-1) + 16*s(0) + 9*s(1) - s(3);
1145 FL_WARN(
"Impl context is not initialized");
1158 auto &audio_sample =
sample.pcm();
1160 return run(slice, out);
1165 return Impl::Result(
false,
"Impl context is not initialized");
1168 FL_WARN(
"Impl sample size mismatch");
1169 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).
static T & instance() FL_NOEXCEPT
float binBoundary(int i) const FL_NOEXCEPT
fl::vector< float > & raw_mut() FL_NOEXCEPT
void setParams(float fmin, float fmax, int sampleRate) FL_NOEXCEPT
fl::vector< float > & linear_mut() FL_NOEXCEPT
void setNormFactors(const fl::vector< float > &factors) FL_NOEXCEPT
void setLinearParams(float linearFmin, float linearFmax) FL_NOEXCEPT
static float fastDb(u32 x)
void computeLogRebinNormFactors(fl::vector< float > &normFactors, const fl::vector< u8 > &lut, int fftN, float fs, int binStart, int binEnd)
fl::vector< alpha16 > mHybridBassWindow
static void decimateBy2(kiss_fft_scalar *buf, int len)
static void applyWindow(const kiss_fft_scalar *samples, const alpha16 *win, kiss_fft_scalar *out, int N)
void initHybrid(int samples, int bands, float fmin, float fmax, int sr)
static void deinterleave(const kiss_fft_cpx *cpx, kiss_fft_scalar *re, kiss_fft_scalar *im, int n)
void buildLogBinLut(fl::vector< u8 > &lut, int fftN, float fs, int binStart, int binEnd)
fl::vector< kiss_fft_scalar > mWorkBuf
void runHybrid(span< const i16 > buffer, Bins *out)
void runLogRebin(span< const i16 > buffer, Bins *out)
fl::vector< alpha16 > mWindowBuf
void computeBinEdgesQ16()
fl::vector< u8 > mLinearBinLut
static void batchMag(const kiss_fft_scalar *re, const kiss_fft_scalar *im, u16 *mag, int n)
fl::vector< alpha16 > mHybridMidWindow
fl::vector< kiss_fft_cpx > mHybridMidFftOut
fl::vector< float > mLogBinEdges
fl::vector< u8 > mLogBinLutMid
static FftScratch & scratch()
static u16 fastMag(i32 re, i32 im)
fl::vector< kiss_fft_cpx > mHybridSmallFftOut
void initNaive(int samples, int bands, float fmin, float fmax, int sr)
void buildLinearBinLut(fl::vector< u8 > &lut, int fftN)
fl::vector< float > mHybridNormUpper
fl::vector< u16x16 > mLogBinEdgesQ16
fl::vector< u8 > mLogBinLutBass
fl::vector< OctaveInfo > mOctaves
Context(int samples, int bands, float fmin, float fmax, int sample_rate, Mode mode, Window window)
fl::vector< u8 > mLogBinLut
fl::size sampleSize() const
fl::vector< kiss_fft_cpx > mFftOut
kiss_fftr_cfg mHybridMidFft
fl::vector< float > mHybridNormBass
void logRebinRange(const u16 *mag, int fftN, float fs, int binStart, int binEnd, u32 *rawBinsI, const fl::vector< u8 > &lut)
void initOctaveWise(int samples, int bands, float fmin, float fmax, int sr)
fl::vector< float > mLogBinNormFactors
void computeLinearBins(const u16 *mag, int, Bins *out)
fl::vector< float > mHybridMergedNorm
void runNaive(span< const i16 > buffer, Bins *out)
kiss_fftr_cfg mHybridSmallFft
void run(span< const i16 > buffer, Bins *out)
fl::vector< float > mHybridNormMid
static void computeWindow(fl::vector< alpha16 > &win, int N, Window type)
void runOctaveWise(span< const i16 > buffer, Bins *out)
fl::vector< kiss_fft_scalar > re
fl::vector< kiss_fft_scalar > im
fl::vector< kiss_fft_cpx > fftOut
fl::vector< kiss_fft_scalar > windowed
fl::vector< u32 > rawBinsI
Result run(const Sample &sample, Bins *out)
fl::unique_ptr< Context > mContext
fl::size sampleSize() const
constexpr i32 raw() const FL_NOEXCEPT
const T * data() const FL_NOEXCEPT
constexpr fl::size size() const FL_NOEXCEPT
string str() const FL_NOEXCEPT
constexpr u32 raw() const FL_NOEXCEPT
static constexpr FASTLED_FORCE_INLINE u16x16 ceil(u16x16 x) FL_NOEXCEPT
constexpr float to_float() const FL_NOEXCEPT
constexpr u32 to_int() const FL_NOEXCEPT
static constexpr FASTLED_FORCE_INLINE u16x16 from_raw(u32 raw) FL_NOEXCEPT
void push_back(const T &value) FL_NOEXCEPT
void resize(fl::size n) FL_NOEXCEPT
void apply_kernels(kiss_fft_cpx fft[], kiss_fft_cpx cq[], struct sparse_arr kernels[], struct cq_kernel_cfg cfg) FL_NOEXCEPT
struct sparse_arr * generate_kernels(struct cq_kernel_cfg cfg) FL_NOEXCEPT
void free_kernels(struct sparse_arr *kernels, struct cq_kernel_cfg cfg) FL_NOEXCEPT
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) FL_NOEXCEPT
struct kiss_fftr_state * kiss_fftr_cfg
Centralized logging categories for FastLED hardware interfaces and subsystems.
void fl_fft_real_forward(kiss_fftr_cfg cfg, int N, const kiss_fft_scalar *in, kiss_fft_cpx *out) FL_NOEXCEPT
Forward real-to-complex FFT.
float expf(float value) FL_NOEXCEPT
float floorf(float value) FL_NOEXCEPT
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NOEXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type sqrt(T x) FL_NOEXCEPT
void * memset(void *s, int c, size_t n) FL_NOEXCEPT
CRGB sample(const CRGB *grid, const XYMap &xyMap, float x, float y, SampleMode mode)
Sample a pixel from a 2D CRGB grid at floating-point coordinates.
float log2f(float value) FL_NOEXCEPT
float logf(float value) FL_NOEXCEPT
Base definition for an LED controller.
constexpr T scale_signed(T v) const FL_NOEXCEPT
Scale a signed integer by this alpha (UNORM16 semantics).
Unsigned 16-bit alpha / brightness — UNORM16.
static void resolveModeEnums(Mode &mode, Window &window, int bands, int samples, float fmin, float fmax) FL_NOEXCEPT