FastLED 3.9.15
Loading...
Searching...
No Matches
spectral_equalizer.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
13// Define static constexpr arrays
16
20
24
26
28 mConfig = config;
29
30 // Reset stats
31 mStats = Stats();
32
33 // Allocate gain array
34 mGains.clear();
35 mGains.resize(mConfig.numBands, 1.0f);
36
37 // Calculate gains based on curve type
39}
40
42 switch (mConfig.curve) {
45 break;
46
49 break;
50
52 // Use custom gains if provided
53 if (mConfig.customGains.size() == mConfig.numBands) {
54 for (size i = 0; i < mConfig.numBands; ++i) {
55 mGains[i] = mConfig.customGains[i];
56 }
57 } else {
58 FL_WARN("SpectralEqualizer: custom gains size mismatch ("
59 << mConfig.customGains.size() << " != " << mConfig.numBands
60 << "), using flat gains");
62 }
63 break;
64
65 default:
67 break;
68 }
69}
70
72 // All gains = 1.0 (no equalization)
73 for (size i = 0; i < mConfig.numBands; ++i) {
74 mGains[i] = 1.0f;
75 }
76}
77
79 // Use appropriate A-weighting curve based on number of bands
80 const float* curve = nullptr;
81 size curveSize = 0;
82
83 if (mConfig.numBands == 16) {
84 curve = A_WEIGHTING_16BAND;
85 curveSize = 16;
86 } else if (mConfig.numBands == 32) {
87 curve = A_WEIGHTING_32BAND;
88 curveSize = 32;
89 } else {
90 // Unsupported band count - use flat gains
91 FL_WARN("SpectralEqualizer: A-weighting not defined for " << mConfig.numBands
92 << " bands, using flat gains");
94 return;
95 }
96
97 // Copy A-weighting coefficients to gains
98 for (size i = 0; i < mConfig.numBands && i < curveSize; ++i) {
99 mGains[i] = curve[i];
100 }
101}
102
104 if (gains.size() != mConfig.numBands) {
105 FL_WARN("SpectralEqualizer: custom gains size mismatch ("
106 << gains.size() << " != " << mConfig.numBands << ")");
107 return;
108 }
109
110 // Copy custom gains
111 mConfig.customGains.clear();
112 mConfig.customGains.reserve(gains.size());
113 for (size i = 0; i < gains.size(); ++i) {
114 mConfig.customGains.push_back(gains[i]);
115 mGains[i] = gains[i];
116 }
117
118 // Switch to custom curve
120}
121
122void SpectralEqualizer::apply(span<const float> inputBins, span<float> outputBins) const {
123 if (inputBins.size() != mConfig.numBands) {
124 FL_WARN("SpectralEqualizer: input size mismatch ("
125 << inputBins.size() << " != " << mConfig.numBands << ")");
126 return;
127 }
128
129 if (outputBins.size() < mConfig.numBands) {
130 FL_WARN("SpectralEqualizer: output buffer too small ("
131 << outputBins.size() << " < " << mConfig.numBands << ")");
132 return;
133 }
134
135 // Track input/output levels for stats
136 float inputPeak = 0.0f;
137 float outputPeak = 0.0f;
138 float inputSum = 0.0f;
139 float outputSum = 0.0f;
140
141 // Apply per-band gains
142 for (size i = 0; i < mConfig.numBands; ++i) {
143 float inputValue = inputBins[i];
144 float gain = mGains[i];
145 float outputValue = inputValue * gain;
146
147 // Apply compression if enabled
148 if (mConfig.enableCompression) {
149 outputValue = applyCompression(outputValue);
150 }
151
152 outputBins[i] = outputValue;
153
154 // Track levels
155 if (inputValue > inputPeak) {
156 inputPeak = inputValue;
157 }
158 if (outputValue > outputPeak) {
159 outputPeak = outputValue;
160 }
161 inputSum += inputValue;
162 outputSum += outputValue;
163 }
164
165 // Calculate makeup gain if enabled
166 float makeupGain = 1.0f;
167 if (mConfig.applyMakeupGain) {
168 makeupGain = calculateMakeupGain(inputBins, outputBins);
169
170 // Apply makeup gain to all output bins
171 for (size i = 0; i < mConfig.numBands; ++i) {
172 outputBins[i] *= makeupGain;
173 }
174
175 // Adjust output stats
176 outputPeak *= makeupGain;
177 outputSum *= makeupGain;
178 }
179
180 // Update mutable stats
181 mStats.applicationsCount++;
182 mStats.lastInputPeak = inputPeak;
183 mStats.lastOutputPeak = outputPeak;
184 mStats.lastMakeupGain = makeupGain;
185 mStats.avgInputLevel = inputSum / static_cast<float>(mConfig.numBands);
186 mStats.avgOutputLevel = outputSum / static_cast<float>(mConfig.numBands);
187}
188
190 // Calculate average levels
191 float inputAvg = 0.0f;
192 float outputAvg = 0.0f;
193
194 for (size i = 0; i < mConfig.numBands; ++i) {
195 inputAvg += inputBins[i];
196 outputAvg += outputBins[i];
197 }
198
199 inputAvg /= static_cast<float>(mConfig.numBands);
200 outputAvg /= static_cast<float>(mConfig.numBands);
201
202 // Avoid division by zero
203 if (outputAvg < 0.001f) {
204 return 1.0f;
205 }
206
207 // Calculate makeup gain to bring output to target level
208 // Target is relative to input average
209 float targetLevel = inputAvg * mConfig.makeupGainTarget;
210 float makeupGain = targetLevel / outputAvg;
211
212 // Clamp to reasonable range (0.1 to 10.0)
213 if (makeupGain < 0.1f) {
214 makeupGain = 0.1f;
215 }
216 if (makeupGain > 10.0f) {
217 makeupGain = 10.0f;
218 }
219
220 return makeupGain;
221}
222
224 // Simple soft-knee compression
225 if (value <= mConfig.compressionThreshold) {
226 // Below threshold - no compression
227 return value;
228 }
229
230 // Above threshold - apply compression ratio
231 float excess = value - mConfig.compressionThreshold;
232 float compressed = excess / mConfig.compressionRatio;
233 return mConfig.compressionThreshold + compressed;
234}
235
239
240} // namespace audio
241} // namespace fl
static constexpr float A_WEIGHTING_16BAND[16]
A-weighting coefficients for 16-band frequency analysis These approximate the A-weighting curve acros...
void calculateGains()
Calculate gains based on current curve.
void setCustomGains(span< const float > gains)
Set custom per-band gains (switches to Custom curve)
void configure(const SpectralEqualizerConfig &config)
Configure the spectral equalizer This calculates per-band gain multipliers based on the selected curv...
~SpectralEqualizer() FL_NOEXCEPT
float calculateMakeupGain(span< const float > inputBins, span< const float > outputBins) const
Calculate makeup gain to maintain target level.
void calculateFlatGains()
Calculate flat gains (all 1.0)
void calculateAWeightingGains()
Calculate A-weighting gains.
void apply(span< const float > inputBins, span< float > outputBins) const
Apply equalization to frequency bins.
float applyCompression(float value) const
Apply dynamic range compression per band.
static constexpr float A_WEIGHTING_32BAND[32]
A-weighting coefficients for 32-band frequency analysis.
SpectralEqualizerConfig mConfig
vector< float > mGains
Per-band gain multipliers.
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 spectral equalizer.
constexpr int type_rank< T >::value
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT