FastLED 3.9.15
Loading...
Searching...
No Matches
fft.cpp.hpp
Go to the documentation of this file.
1
2#include "fl/audio/fft/fft.h"
6#include "fl/stl/int.h"
7#include "fl/stl/shared_ptr.h" // For shared_ptr
8#include "fl/stl/singleton.h"
9#include "fl/stl/mutex.h"
10#include "fl/stl/noexcept.h"
11
12namespace fl {
13namespace audio {
14namespace fft {
15
16// Recycles fl::vector<float> buffers to avoid repeated allocation.
17// Vectors returned to the pool retain their capacity for reuse.
19 public:
20 fl::vector<float> acquire(fl::size capacity) {
21 for (fl::size i = 0; i < mPool.size(); ++i) {
22 if (mPool[i].capacity() >= capacity) {
24 mPool[i] = fl::move(mPool.back());
25 mPool.pop_back();
26 v.clear();
27 return v;
28 }
29 }
31 v.reserve(capacity);
32 return v;
33 }
34
36 if (v.capacity() > 0 && mPool.size() < kMaxPoolSize) {
37 v.clear();
38 mPool.push_back(fl::move(v));
39 }
40 }
41
43 if (!v.empty()) {
44 release(fl::move(v));
45 }
46 }
47
48 private:
49 static const fl::size kMaxPoolSize = 32;
51};
52
54 mBinsRaw.clear();
55 mBinsLinear.clear();
56 mNormFactors.clear();
57 mNormalizedDirty = true;
58 mDbDirty = true;
59}
60
61fl::size Bins::bands() const { return mBands; }
62
64
66 if (!mDbDirty) {
67 return mBinsDb;
68 }
69 if (mBinsDb.capacity() == 0) {
71 }
72 mBinsDb.resize(mBinsRaw.size());
73 for (fl::size i = 0; i < mBinsRaw.size(); ++i) {
74 float x = mBinsRaw[i];
75 mBinsDb[i] = (x > 0.0f) ? 20.0f * fl::log10f(x) : 0.0f;
76 }
77 mDbDirty = false;
78 return mBinsDb;
79}
80
82 if (!mNormalizedDirty) {
83 return mBinsRawNormalized;
84 }
85 if (mBinsRawNormalized.capacity() == 0) {
87 }
88 mBinsRawNormalized.resize(mBinsRaw.size());
89 for (fl::size i = 0; i < mBinsRaw.size(); ++i) {
90 float norm = (i < mNormFactors.size()) ? mNormFactors[i] : 1.0f;
91 mBinsRawNormalized[i] = mBinsRaw[i] * norm;
92 }
93 mNormalizedDirty = false;
94 return mBinsRawNormalized;
95}
96
98float Bins::linearFmin() const { return mLinearFmin; }
99float Bins::linearFmax() const { return mLinearFmax; }
100float Bins::fmin() const { return mFmin; }
101float Bins::fmax() const { return mFmax; }
102int Bins::sampleRate() const { return mSampleRate; }
103
104float Bins::binToFreq(int i) const {
105 int nbands = static_cast<int>(mBinsRaw.size());
106 if (nbands <= 1) return mFmin;
107 float m = fl::logf(mFmax / mFmin);
108 return mFmin * fl::expf(m * static_cast<float>(i) / static_cast<float>(nbands - 1));
109}
110
111int Bins::freqToBin(float freq) const {
112 int nbands = static_cast<int>(mBinsRaw.size());
113 if (nbands <= 1) return 0;
114 if (freq <= mFmin) return 0;
115 if (freq >= mFmax) return nbands - 1;
116 float m = fl::logf(mFmax / mFmin);
117 float bin = fl::logf(freq / mFmin) / m * static_cast<float>(nbands - 1);
118 int result = static_cast<int>(bin + 0.5f);
119 if (result < 0) return 0;
120 if (result >= nbands) return nbands - 1;
121 return result;
122}
123
124float Bins::binBoundary(int i) const {
125 float f_i = binToFreq(i);
126 float f_next = binToFreq(i + 1);
127 return fl::sqrtf(f_i * f_next);
128}
129
131 if (mBinsRaw.capacity() == 0) {
133 }
134 mNormalizedDirty = true;
135 mDbDirty = true;
136 return mBinsRaw;
137}
138
140 if (mBinsLinear.capacity() == 0) {
142 }
143 return mBinsLinear;
144}
145
146void Bins::setParams(float fmin, float fmax, int sampleRate) {
147 mFmin = fmin;
148 mFmax = fmax;
150}
151
156
158 if (mNormFactors.capacity() == 0) {
160 }
161 mNormFactors.resize(factors.size());
162 for (fl::size i = 0; i < factors.size(); ++i) {
163 mNormFactors[i] = factors[i];
164 }
165 mNormalizedDirty = true;
166}
167
171
172Bins::Bins(fl::size n)
173 : mBands(n) {}
174
176 auto& p = pool();
177 p.releaseIfNotEmpty(fl::move(mBinsRaw));
178 p.releaseIfNotEmpty(fl::move(mBinsLinear));
179 p.releaseIfNotEmpty(fl::move(mNormFactors));
180 p.releaseIfNotEmpty(fl::move(mBinsDb));
181 p.releaseIfNotEmpty(fl::move(mBinsRawNormalized));
182}
183
184} // namespace fft
185} // namespace audio
186
187// Hash specialization must be in fl:: namespace where Hash is defined
188template <> struct Hash<audio::fft::Args> {
189 fl::u32 operator()(const audio::fft::Args &key) const FL_NOEXCEPT {
190 // Hash fields individually to avoid padding-byte issues
191 fl::u32 h = 0;
192 h ^= MurmurHash3_x86_32(&key.samples, sizeof(key.samples));
193 h ^= MurmurHash3_x86_32(&key.bands, sizeof(key.bands)) * 2654435761u;
194 h ^= MurmurHash3_x86_32(&key.fmin, sizeof(key.fmin)) * 2246822519u;
195 h ^= MurmurHash3_x86_32(&key.fmax, sizeof(key.fmax)) * 3266489917u;
196 h ^= MurmurHash3_x86_32(&key.sample_rate, sizeof(key.sample_rate)) * 668265263u;
197 h ^= static_cast<fl::u32>(key.mode) * 374761393u;
198 h ^= static_cast<fl::u32>(key.window) * 2246822519u;
199 return h;
200 }
201};
202
203namespace audio {
204namespace fft {
205
207 static constexpr fl::size kDefaultMaxSize = 10;
209
211
213 fl::lock_guard<fl::mutex> lock(mMutex);
214 fl::shared_ptr<Impl> *val = mMap.find_value(args);
215 if (val) {
216 return *val;
217 }
219 mMap[args] = fft;
220 return fft;
221 }
222
223 void clear() {
224 fl::lock_guard<fl::mutex> lock(mMutex);
225 mMap.clear();
226 }
227
228 fl::size size() {
229 fl::lock_guard<fl::mutex> lock(mMutex);
230 return mMap.size();
231 }
232
233 void setMaxSize(fl::size max_size) {
234 fl::lock_guard<fl::mutex> lock(mMutex);
235 mMap.setMaxSize(max_size);
236 }
237
238private:
241};
242
244 // Global LRU cache with max 10 entries — shared by all FFT instances.
245 // This avoids regenerating expensive CQ kernels when AudioContext is
246 // recreated (each Impl with 128 CQ bins takes ~850ms to initialize).
248}
249
250void FFT::run(const span<const fl::i16> &sample, Bins *out,
251 const Args &args) {
252 Args args2 = args;
253 args2.samples = sample.size();
254 // Fetch cached impl (thread-safe), then run FFT outside the lock.
256 impl->run(sample, out);
257}
258
260
261fl::size FFT::size() const { return globalCache().size(); }
262
264
266 int samples, float fmin, float fmax) {
267 // Resolve mode first
268 if (mode == Mode::AUTO) {
269 if (bands <= 32) {
271 } else {
272 // Check kernel conditioning: N_window = N * fmin / fmax.
273 // When >= 2, CQ_NAIVE (single FFT + kernels) works well.
274 // When < 2, kernels degenerate and we need octave-wise CQT.
275 int winMin = static_cast<int>(
276 static_cast<float>(samples) * fmin / fmax);
277 mode = (winMin >= 2) ? Mode::CQ_NAIVE : Mode::CQ_OCTAVE;
278 }
279 }
280
281 // Resolve window based on resolved mode
282 if (window == Window::AUTO) {
283 switch (mode) {
284 case Mode::LOG_REBIN:
285 case Mode::CQ_HYBRID:
286 // These paths apply time-domain windowing before fft::FFT.
287 // BLACKMAN_HARRIS: -92 dB sidelobe rejection (vs -31 dB HANNING).
288 // Sidelobes produce only mag=0-1 quantization noise, ensuring
289 // distant output bins have near-zero energy (≤2) for pure tones.
290 // Main lobe is 8 FFT bins wide (~690 Hz at 512/44100), so
291 // output bins narrower than ~130 Hz may see main lobe leakage
292 // at distance 3+ — a fundamental resolution limit.
294 break;
295 case Mode::CQ_NAIVE:
296 case Mode::CQ_OCTAVE:
297 // CQ kernels already apply Hamming windowing in frequency domain.
298 // No time-domain window is applied on these paths, so this is
299 // cosmetic. HANNING is the lighter/cheaper default.
301 break;
302 default:
304 break;
305 }
306 }
307}
308
309bool Args::operator==(const Args &other) const {
312
313 return samples == other.samples && bands == other.bands &&
314 fmin == other.fmin && fmax == other.fmax &&
315 sample_rate == other.sample_rate && mode == other.mode &&
316 window == other.window;
317
319}
320
321} // namespace fft
322} // namespace audio
323} // namespace fl
fl::UIAudio audio("Audio Input")
AudioAnalyzeFFT1024 fft
static T & instance() FL_NOEXCEPT
Definition singleton.h:41
float binBoundary(int i) const FL_NOEXCEPT
Definition fft.cpp.hpp:124
fl::vector< float > & raw_mut() FL_NOEXCEPT
Definition fft.cpp.hpp:130
void setParams(float fmin, float fmax, int sampleRate) FL_NOEXCEPT
Definition fft.cpp.hpp:146
fl::vector< float > & linear_mut() FL_NOEXCEPT
Definition fft.cpp.hpp:139
static FloatVectorPool & pool() FL_NOEXCEPT
Definition fft.cpp.hpp:168
fl::vector< float > mBinsRawNormalized
Definition fft.h:114
~Bins() FL_NOEXCEPT
Definition fft.cpp.hpp:175
fl::vector< float > mBinsLinear
Definition fft.h:111
fl::vector< float > mNormFactors
Definition fft.h:112
void setNormFactors(const fl::vector< float > &factors) FL_NOEXCEPT
Definition fft.cpp.hpp:157
float linearFmin() const FL_NOEXCEPT
Definition fft.cpp.hpp:98
fl::span< const float > db() const FL_NOEXCEPT
Definition fft.cpp.hpp:65
fl::span< const float > raw() const FL_NOEXCEPT
Definition fft.cpp.hpp:63
int sampleRate() const FL_NOEXCEPT
Definition fft.cpp.hpp:102
float fmin() const FL_NOEXCEPT
Definition fft.cpp.hpp:100
fl::vector< float > mBinsDb
Definition fft.h:113
int freqToBin(float freq) const FL_NOEXCEPT
Definition fft.cpp.hpp:111
fl::vector< float > mBinsRaw
Definition fft.h:110
fl::size mBands
Definition fft.h:109
fl::size bands() const FL_NOEXCEPT
Definition fft.cpp.hpp:61
float mLinearFmin
Definition fft.h:120
bool mNormalizedDirty
Definition fft.h:116
float mLinearFmax
Definition fft.h:121
float linearFmax() const FL_NOEXCEPT
Definition fft.cpp.hpp:99
float fmax() const FL_NOEXCEPT
Definition fft.cpp.hpp:101
void clear() FL_NOEXCEPT
Definition fft.cpp.hpp:53
fl::span< const float > rawNormalized() const FL_NOEXCEPT
Definition fft.cpp.hpp:81
Bins(fl::size n) FL_NOEXCEPT
Definition fft.cpp.hpp:172
float binToFreq(int i) const FL_NOEXCEPT
Definition fft.cpp.hpp:104
fl::span< const float > linear() const FL_NOEXCEPT
Definition fft.cpp.hpp:97
void setLinearParams(float linearFmin, float linearFmax) FL_NOEXCEPT
Definition fft.cpp.hpp:152
fl::size size() const FL_NOEXCEPT
Definition fft.cpp.hpp:261
void run(const span< const i16 > &sample, Bins *out, const Args &args=Args()) FL_NOEXCEPT
void clear() FL_NOEXCEPT
Definition fft.cpp.hpp:259
static ImplCache & globalCache() FL_NOEXCEPT
Definition fft.cpp.hpp:243
static void setFFTCacheSize(fl::size size) FL_NOEXCEPT
Definition fft.cpp.hpp:263
fl::vector< fl::vector< float > > mPool
Definition fft.cpp.hpp:50
void releaseIfNotEmpty(fl::vector< float > &&v)
Definition fft.cpp.hpp:42
void release(fl::vector< float > &&v)
Definition fft.cpp.hpp:35
fl::vector< float > acquire(fl::size capacity)
Definition fft.cpp.hpp:20
static const fl::size kMaxPoolSize
Definition fft.cpp.hpp:49
fl::size size() const FL_NOEXCEPT
void reserve(fl::size n) FL_NOEXCEPT
Definition vector.h:591
void clear() FL_NOEXCEPT
Definition vector.h:634
Platform-independent mutex interface.
constexpr remove_reference< T >::type && move(T &&t) FL_NOEXCEPT
Definition s16x16x4.h:28
float sqrtf(float value) FL_NOEXCEPT
Definition math.h:453
float expf(float value) FL_NOEXCEPT
Definition math.h:398
float log10f(float value) FL_NOEXCEPT
Definition math.h:424
bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2) FL_NOEXCEPT
Definition algorithm.h:96
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.
Definition sample.cpp.hpp:9
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
Definition shared_ptr.h:414
static u32 MurmurHash3_x86_32(const void *key, fl::size len, u32 seed=0)
Definition hash.h:22
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
fl::platforms::mutex mutex
Definition mutex.h:20
float logf(float value) FL_NOEXCEPT
Definition math.h:418
Base definition for an LED controller.
Definition crgb.hpp:179
corkscrew_args args
Definition old.h:149
#define FL_DISABLE_WARNING(warning)
#define FL_DISABLE_WARNING_PUSH
#define FL_DISABLE_WARNING_POP
#define FL_NOEXCEPT
fl::u32 operator()(const audio::fft::Args &key) const FL_NOEXCEPT
Definition fft.cpp.hpp:189
bool operator==(const Args &other) const FL_NOEXCEPT
Definition fft.cpp.hpp:309
Args(int samples=DefaultSamples(), int bands=DefaultBands(), float fmin=DefaultMinFrequency(), float fmax=DefaultMaxFrequency(), int sample_rate=DefaultSampleRate(), Mode mode=Mode::AUTO, Window window=Window::AUTO) FL_NOEXCEPT
Definition fft.h:139
static void resolveModeEnums(Mode &mode, Window &window, int bands, int samples, float fmin, float fmax) FL_NOEXCEPT
Definition fft.cpp.hpp:265
void setMaxSize(fl::size max_size)
Definition fft.cpp.hpp:233
static constexpr fl::size kDefaultMaxSize
Definition fft.cpp.hpp:207
fl::shared_ptr< Impl > get_or_create(const Args &args)
Definition fft.cpp.hpp:212
HashMapLru< Args, fl::shared_ptr< Impl > > LruMap
Definition fft.cpp.hpp:208