FastLED 3.9.15
Loading...
Searching...
No Matches
frequency_bin_mapper.cpp.hpp
Go to the documentation of this file.
1
3
5#include "fl/math/math.h"
6#include "fl/math/math.h"
8#include "fl/stl/noexcept.h"
9
10namespace fl {
11namespace audio {
12
16
20
22
24 mConfig = config;
25
26 // Reset stats
27 mStats = Stats();
28
29 // Calculate frequency boundaries for output bins
31
32 // Calculate fft::FFT bin to frequency bin mappings
34}
35
37 const size numBins = static_cast<size>(mConfig.mode);
38
39 // Allocate space for bin boundaries (numBins + 1 edges)
40 mBinFrequencies.clear();
41 mBinFrequencies.reserve(numBins + 1);
42
43 if (mConfig.useLogSpacing) {
45 } else {
47 }
48}
49
51 const size numBins = static_cast<size>(mConfig.mode);
52 const float logMin = fl::logf(mConfig.minFrequency);
53 const float logMax = fl::logf(mConfig.maxFrequency);
54 const float logStep = (logMax - logMin) / static_cast<float>(numBins);
55
56 // Calculate logarithmically-spaced bin edges
57 for (size i = 0; i <= numBins; ++i) {
58 float logFreq = logMin + static_cast<float>(i) * logStep;
59 float freq = fl::expf(logFreq);
60 mBinFrequencies.push_back(freq);
61 }
62}
63
65 const size numBins = static_cast<size>(mConfig.mode);
66 const float step = (mConfig.maxFrequency - mConfig.minFrequency) / static_cast<float>(numBins);
67
68 // Calculate linearly-spaced bin edges
69 for (size i = 0; i <= numBins; ++i) {
70 float freq = mConfig.minFrequency + static_cast<float>(i) * step;
71 mBinFrequencies.push_back(freq);
72 }
73}
74
76 const size numBins = static_cast<size>(mConfig.mode);
77
78 mBinMappings.clear();
79 mBinMappings.reserve(numBins);
80
81 // For each output frequency bin, determine which fft::FFT bins contribute
82 for (size i = 0; i < numBins; ++i) {
83 float minFreq = mBinFrequencies[i];
84 float maxFreq = mBinFrequencies[i + 1];
85
86 // Convert frequencies to fft::FFT bin indices
87 float startBinFloat = frequencyToFFTBin(minFreq);
88 float endBinFloat = frequencyToFFTBin(maxFreq);
89
90 // Round to integer fft::FFT bin indices
91 u32 startBin = static_cast<u32>(startBinFloat);
92 u32 endBin = static_cast<u32>(fl::ceilf(endBinFloat));
93
94 // Clamp to valid fft::FFT bin range
95 if (startBin >= mConfig.fftBinCount) {
96 startBin = mConfig.fftBinCount - 1;
97 }
98 if (endBin > mConfig.fftBinCount) {
99 endBin = mConfig.fftBinCount;
100 }
101
102 // Ensure at least one fft::FFT bin per output bin
103 if (endBin <= startBin) {
104 endBin = startBin + 1;
105 }
106
107 BinMapping mapping;
108 mapping.startBin = startBin;
109 mapping.endBin = endBin;
110 mBinMappings.push_back(mapping);
111 }
112}
113
114float FrequencyBinMapper::frequencyToFFTBin(float frequency) const {
115 // fft::FFT bin index = (frequency / sampleRate) * fftSize
116 // fftSize = fftBinCount * 2 (fft::FFT produces fftSize/2 bins)
117 const float fftSize = static_cast<float>(mConfig.fftBinCount) * 2.0f;
118 return (frequency / static_cast<float>(mConfig.sampleRate)) * fftSize;
119}
120
122 const size numBins = static_cast<size>(mConfig.mode);
123
124 // Validate output buffer size
125 if (outputBins.size() < numBins) {
126 FL_WARN("FrequencyBinMapper: output buffer too small (" << outputBins.size()
127 << " < " << numBins << ")");
128 return;
129 }
130
131 // Track maximum magnitude for stats
132 float maxMag = 0.0f;
133 u32 fftBinsUsed = 0;
134
135 // Map fft::FFT bins to frequency bins by averaging
136 for (size i = 0; i < numBins; ++i) {
137 const BinMapping& mapping = mBinMappings[i];
138
139 float sum = 0.0f;
140 u32 count = 0;
141
142 // Average fft::FFT bins in this range
143 for (u32 j = mapping.startBin; j < mapping.endBin && j < fftBins.size(); ++j) {
144 sum += fftBins[j];
145 ++count;
146 ++fftBinsUsed;
147
148 if (fftBins[j] > maxMag) {
149 maxMag = fftBins[j];
150 }
151 }
152
153 // Calculate average
154 if (count > 0) {
155 outputBins[i] = sum / static_cast<float>(count);
156 } else {
157 outputBins[i] = 0.0f;
158 }
159 }
160
161 // Update stats (mutable in const method for statistics)
162 const_cast<FrequencyBinMapper*>(this)->mStats.binMappingCount++;
163 const_cast<FrequencyBinMapper*>(this)->mStats.lastFFTBinsUsed = fftBinsUsed;
164 const_cast<FrequencyBinMapper*>(this)->mStats.maxMagnitude = maxMag;
165}
166
168 if (frequencyBins.size() < BASS_BIN_END) {
169 return 0.0f;
170 }
171
172 // Average bins 0-1 (bass range)
173 float sum = 0.0f;
174 for (size i = BASS_BIN_START; i < BASS_BIN_END; ++i) {
175 sum += frequencyBins[i];
176 }
177 return sum / static_cast<float>(BASS_BIN_END - BASS_BIN_START);
178}
179
181 if (frequencyBins.size() < MID_BIN_END) {
182 return 0.0f;
183 }
184
185 // Average bins 6-7 (mid range)
186 float sum = 0.0f;
187 for (size i = MID_BIN_START; i < MID_BIN_END; ++i) {
188 sum += frequencyBins[i];
189 }
190 return sum / static_cast<float>(MID_BIN_END - MID_BIN_START);
191}
192
194 if (frequencyBins.size() < TREBLE_BIN_END) {
195 return 0.0f;
196 }
197
198 // Average bins 14-15 (treble range)
199 float sum = 0.0f;
200 for (size i = TREBLE_BIN_START; i < TREBLE_BIN_END; ++i) {
201 sum += frequencyBins[i];
202 }
203 return sum / static_cast<float>(TREBLE_BIN_END - TREBLE_BIN_START);
204}
205
207 FrequencyRange range = {0.0f, 0.0f};
208
209 if (binIndex >= mBinFrequencies.size() - 1) {
210 return range;
211 }
212
213 range.minFreq = mBinFrequencies[binIndex];
214 range.maxFreq = mBinFrequencies[binIndex + 1];
215 return range;
216}
217
218} // namespace audio
219} // namespace fl
FrequencyRange getBinFrequencyRange(size binIndex) const
static constexpr size TREBLE_BIN_START
void calculateBinMappings()
Calculate FFT bin to frequency bin mappings Pre-calculates which FFT bins contribute to each frequenc...
float frequencyToFFTBin(float frequency) const
Convert frequency (Hz) to FFT bin index.
static constexpr size BASS_BIN_START
Bass/mid/treble bin indices (for 16-bin mode) These are pre-calculated based on the bin count.
float getBassEnergy(span< const float > frequencyBins) const
Get bass energy (average of bins 0-1 in 16-bin mode)
vector< float > mBinFrequencies
Pre-calculated frequency boundaries for each output bin Size: numBins + 1 (includes both lower and up...
float getMidEnergy(span< const float > frequencyBins) const
Get mid energy (average of bins 6-7 in 16-bin mode)
void mapBins(span< const float > fftBins, span< float > outputBins) const
Map FFT bins to frequency channels.
FrequencyBinMapperConfig mConfig
void calculateLinearFrequencies()
Calculate linearly-spaced frequency boundaries.
void calculateLogFrequencies()
Calculate logarithmically-spaced frequency boundaries.
float getTrebleEnergy(span< const float > frequencyBins) const
Get treble energy (average of bins 14-15 in 16-bin mode)
void calculateBinBoundaries()
Calculate frequency bin boundaries (linear or logarithmic spacing)
void configure(const FrequencyBinMapperConfig &config)
Configure the frequency bin mapper This calculates bin boundaries and FFT-to-frequency bin mappings.
Get frequency boundaries for a specific output bin.
Mapping from output bins to FFT bin ranges Each entry contains (startBin, endBin) for averaging.
Get statistics (for debugging/monitoring)
constexpr fl::size size() const FL_NOEXCEPT
Definition span.h:458
#define FL_WARN(X)
Definition log.h:276
Configuration for frequency bin mapping.
float expf(float value) FL_NOEXCEPT
Definition math.h:398
float ceilf(float value) FL_NOEXCEPT
Definition math.h:310
constexpr enable_if< is_fixed_point< T >::value, T >::type step(T edge, T x) FL_NOEXCEPT
float logf(float value) FL_NOEXCEPT
Definition math.h:418
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT