FastLED 3.10.8
Loading...
Searching...
No Matches
audio_reactive.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/audio/fft/fft.h"
4#include "fl/stl/int.h"
5#include "fl/audio/audio.h" // IWYU pragma: keep
12#include "fl/stl/array.h"
13#include "fl/stl/span.h"
14#include "fl/stl/shared_ptr.h"
15#include "fl/stl/unique_ptr.h"
17#include "fl/gfx/colorutils.h" // IWYU pragma: keep
18#include "fl/gfx/crgb.h" // for CRGB // IWYU pragma: keep
19#include "fl/stl/vector.h"
20#include "fl/stl/noexcept.h"
21
22namespace fl {
23namespace audio {
24
25class Processor;
26
27// Forward declarations for enhanced beat detection
30namespace detector {
31class MusicalBeat;
32class MultiBandBeat;
33}
34
35// Audio data structure - matches original WLED output with extensions
36struct Data {
37 float volume = 0.0f; // Overall volume level (0.0-1.0, adaptive normalized)
38 float volumeRaw = 0.0f; // Raw volume without smoothing (0.0-1.0)
39 float peak = 0.0f; // Peak level (0.0-1.0)
40 bool beatDetected = false; // Beat detection flag
41 float frequencyBins[16] = {0}; // 16 frequency bins (matches WLED NUM_GEQ_CHANNELS)
42 float dominantFrequency = 0.0f; // Major peak frequency (Hz)
43 float magnitude = 0.0f; // fft::FFT magnitude of dominant frequency
44 fl::u32 timestamp = 0; // millis() when data was captured
45
46 // Enhanced beat detection fields
47 bool bassBeatDetected = false; // Bass-specific beat detection
48 bool midBeatDetected = false; // Mid-range beat detection
49 bool trebleBeatDetected = false; // Treble beat detection
50 float spectralFlux = 0.0f; // Current spectral flux value
51 float bassEnergy = 0.0f; // Energy in bass frequencies (0-1)
52 float midEnergy = 0.0f; // Energy in mid frequencies (6-7)
53 float trebleEnergy = 0.0f; // Energy in treble frequencies (14-15)
54};
55
57 fl::u8 gain = 128; // Input gain (0-255)
58 fl::u8 sensitivity = 128; // AGC sensitivity
59 bool noiseGate = true; // Noise gate
60 fl::u8 attack = 50; // Attack time (ms) - how fast to respond to increases
61 fl::u8 decay = 200; // Decay time (ms) - how slow to respond to decreases
62 u16 sampleRate = 22050; // Sample rate (Hz)
63 fl::u8 scalingMode = 3; // 0=none, 1=log, 2=linear, 3=sqrt
64
65 // Enhanced beat detection configuration
66 bool enableSpectralFlux = true; // Enable spectral flux-based beat detection
67 bool enableMultiBand = true; // Enable multi-band beat detection
68 float spectralFluxThreshold = 0.1f; // Threshold for spectral flux detection
69 float bassThreshold = 0.15f; // Threshold for bass beat detection
70 float midThreshold = 0.12f; // Threshold for mid beat detection
71 float trebleThreshold = 0.08f; // Threshold for treble beat detection
72
73 // Signal conditioning configuration
74 bool enableSignalConditioning = true; // Enable DC removal, spike filter, noise gate
75 bool enableNoiseFloorTracking = true; // Enable noise floor tracking
76
77 // Frequency bin mapping configuration
78 bool enableLogBinSpacing = true; // Use logarithmic frequency bin spacing (vs linear)
79
80 // Spectral equalizer configuration (optional middleware)
81 bool enableSpectralEqualizer = false; // Enable spectral EQ (A-weighting, custom curves)
82
83 // Musical beat detection configuration (Phase 3 middleware)
84 bool enableMusicalBeatDetection = false; // Enable advanced beat tracking with BPM
85 bool enableMultiBandBeats = false; // Enable per-band beat detection
86 float musicalBeatMinBPM = 60.0f; // Minimum BPM for musical beats
87 float musicalBeatMaxBPM = 180.0f; // Maximum BPM for musical beats
88 float musicalBeatConfidence = 0.5f; // Minimum confidence for beat validation
89};
90
91class Reactive {
92public:
95
96 // Setup
97 void begin(const ReactiveConfig& config = ReactiveConfig{}) FL_NO_EXCEPT;
98 void setConfig(const ReactiveConfig& config) FL_NO_EXCEPT;
99
100 // Process audio sample - this does all the work immediately
102
103 // Optional: update smoothing without new sample data
104 void update(fl::u32 currentTimeMs) FL_NO_EXCEPT;
105
106 // Data access
107 const Data& getData() const FL_NO_EXCEPT;
108 const Data& getSmoothedData() const FL_NO_EXCEPT;
109
110 // Convenience accessors
111 float getVolume() const FL_NO_EXCEPT;
112 float getBass() const FL_NO_EXCEPT; // Average of bins 0-1
113 float getMid() const FL_NO_EXCEPT; // Average of bins 6-7
114 float getTreble() const FL_NO_EXCEPT; // Average of bins 14-15
115 bool isBeat() const FL_NO_EXCEPT;
116
117 // Enhanced beat detection accessors
118 bool isBassBeat() const FL_NO_EXCEPT;
119 bool isMidBeat() const FL_NO_EXCEPT;
120 bool isTrebleBeat() const FL_NO_EXCEPT;
121 float getSpectralFlux() const FL_NO_EXCEPT;
122 float getBassEnergy() const FL_NO_EXCEPT;
123 float getMidEnergy() const FL_NO_EXCEPT;
124 float getTrebleEnergy() const FL_NO_EXCEPT;
125
126 // ----- Polling Getters (float 0.0-1.0, bool, or integer) -----
127 // These forward to an internal Processor for detector-based analysis.
128
129 // Vocal Detection
131
132 // Beat Detection (detector-based)
134 float getBPM() FL_NO_EXCEPT;
135
136 // Energy Analysis
139
140 // Frequency Bands
144
145 // Silence Detection
146 bool isSilent() FL_NO_EXCEPT;
148
149 // Transient Detection
151
152 // Dynamics Analysis
153 float getDynamicTrend() FL_NO_EXCEPT; // -1.0 to 1.0
156
157 // Pitch Detection
159 float getPitchHz() FL_NO_EXCEPT;
160
161 // Tempo Analysis
164
165 // Buildup Detection
168
169 // Drop Detection
171
172 // Percussion Detection
173 bool isKick() FL_NO_EXCEPT;
174 bool isSnare() FL_NO_EXCEPT;
175 bool isHiHat() FL_NO_EXCEPT;
176 bool isTom() FL_NO_EXCEPT;
177
178 // Note Detection
182
183 // Downbeat Detection
187
188 // Backbeat Detection
191
192 // Chord Detection
194
195 // Key Detection
197
198 // Mood Analysis
200 float getMoodValence() FL_NO_EXCEPT; // -1.0 to 1.0
201
202 // Gain control - delegates to internal Processor
203 void setGain(float gain) FL_NO_EXCEPT;
204 float getGain() const FL_NO_EXCEPT;
205
206 // Effect helpers
208 CRGB volumeToColor(const CRGBPalette16& palette) const FL_NO_EXCEPT;
209 fl::u8 frequencyToScale255(fl::u8 binIndex) const FL_NO_EXCEPT;
210
211 // Signal conditioning stats
214
215 // Spectral equalizer stats (optional middleware - must be enabled first)
218
219private:
220 // Internal processing methods
224 void detectBeat(fl::u32 currentTimeMs) FL_NO_EXCEPT;
227 void applyGain() FL_NO_EXCEPT;
228
229 // Enhanced beat detection methods
230 void detectEnhancedBeats(fl::u32 currentTimeMs) FL_NO_EXCEPT;
236
237 // Helper methods
238 float mapFrequencyBin(int fromBin, int toBin) FL_NO_EXCEPT;
239 float computeRMS(const fl::vector<fl::i16>& samples) FL_NO_EXCEPT;
240
241 // Configuration
243
244 // FFT processing (via shared Context for caching)
247
248 // Audio data
251
252 // Processing state
254 static constexpr fl::u32 BEAT_COOLDOWN = 100; // 100ms minimum between beats
255
256 // Volume tracking for beat detection
257 float mPreviousVolume = 0.0f;
258 float mVolumeThreshold = 0.04f;
259
260 // Pink noise compensation: √(f_center / f_ref) normalized so geometric mean ≈ 1.0.
261 // Compensates for 1/f spectral tilt of natural audio (music, speech).
262 // Computed dynamically from bin centers in begin().
263 float mPinkNoiseGains[16] = {};
264 bool mPinkNoiseComputed = false;
265
266 // Signal conditioning components
270
271 // Enhanced beat detection components
274
275 // Musical beat detection components (Phase 3 middleware)
279
280 // Enhanced beat detection state
282
283 // Silence-gate envelopes for spectral metrics (FastLED#2253).
284 // During silence (Context::isSilent() true), these fast-decay the
285 // dominantFrequency / magnitude / spectralFlux outputs toward zero so
286 // the FFT noise floor does not lock onto arbitrary bins. During audio
287 // they are pass-through. Tau = 0.2 s — spectral metrics are brittle
288 // and should snap to zero quickly.
292
293 // Internal Processor for detector-based polling getters
296};
297
298// Spectral flux-based onset detection for enhanced beat detection
300public:
303
304 void reset() FL_NO_EXCEPT;
305 bool detectOnset(span<const float, 16> currentBins) FL_NO_EXCEPT;
306 float calculateSpectralFlux(span<const float, 16> currentBins, span<const float, 16> previousBins) FL_NO_EXCEPT;
307 void setThreshold(float threshold) FL_NO_EXCEPT;
308 float getThreshold() const FL_NO_EXCEPT;
309
310private:
313
314#if SKETCH_HAS_LARGE_MEMORY
315 fl::array<float, 32> mFluxHistory; // For advanced smoothing
316 fl::size mHistoryIndex;
317 float calculateAdaptiveThreshold() FL_NO_EXCEPT;
318#endif
319};
320
321// Multi-band beat detection for different frequency ranges
325
326 void reset() FL_NO_EXCEPT;
327 void detectBeats(span<const float, 16> frequencyBins, Data& audioData) FL_NO_EXCEPT;
328 void setThresholds(float bassThresh, float midThresh, float trebleThresh) FL_NO_EXCEPT;
329
330private:
331#if SKETCH_HAS_LARGE_MEMORY
332 SpectralFluxDetector bass; // 20-200 Hz (bins 0-1)
333 SpectralFluxDetector mid; // 200-2000 Hz (bins 6-7)
334 SpectralFluxDetector treble; // 2000-20000 Hz (bins 14-15)
335#else
336 SpectralFluxDetector combined; // Single detector for memory-constrained
337#endif
338
339 // Energy tracking for band-specific thresholds
346};
347
348// Perceptual audio weighting for psychoacoustic processing
350public:
353
354 void applyAWeighting(Data& data) const FL_NO_EXCEPT;
355 void applyLoudnessCompensation(Data& data, float referenceLevel) const FL_NO_EXCEPT;
356
357private:
358 // A-weighting coefficients for 16-bin frequency analysis
359 static constexpr float A_WEIGHTING_COEFFS[16] = {
360 0.5f, 0.6f, 0.8f, 1.0f, 1.2f, 1.3f, 1.4f, 1.4f,
361 1.3f, 1.2f, 1.0f, 0.8f, 0.6f, 0.4f, 0.2f, 0.1f
362 };
363
364#if SKETCH_HAS_LARGE_MEMORY
365 fl::array<float, 16> mLoudnessHistory; // For dynamic compensation
366 fl::size mHistoryIndex;
367#endif
368};
369
370} // namespace audio
371} // namespace fl
static bool isSilent
UINumberField palette("Palette", 0, 0, 2)
bool detectBeat(float energy)
Definition advanced.h:123
bool isBeat
Definition advanced.h:89
A fixed-size array implementation similar to std::array.
Definition array.h:27
FrequencyBinMapper maps FFT output bins to perceptually-spaced frequency channels.
NoiseFloorTracker maintains an adaptive estimate of the background noise floor for audio signals,...
void applyAWeighting(Data &data) const FL_NO_EXCEPT
static constexpr float A_WEIGHTING_COEFFS[16]
~PerceptualWeighting() FL_NO_EXCEPT
void applyLoudnessCompensation(Data &data, float referenceLevel) const FL_NO_EXCEPT
float getBassLevel() FL_NO_EXCEPT
bool isTrebleBeat() const FL_NO_EXCEPT
float getMoodValence() FL_NO_EXCEPT
float getVocalConfidence() FL_NO_EXCEPT
void processSample(const Sample &sample) FL_NO_EXCEPT
void mapFFTBinsToFrequencyChannels() FL_NO_EXCEPT
float getTrebleLevel() FL_NO_EXCEPT
bool isTom() FL_NO_EXCEPT
float getTreble() const FL_NO_EXCEPT
float getBass() const FL_NO_EXCEPT
float getTrebleEnergy() const FL_NO_EXCEPT
float getMeasurePhase() FL_NO_EXCEPT
const Data & getData() const FL_NO_EXCEPT
bool isHiHat() FL_NO_EXCEPT
void calculateBandEnergies() FL_NO_EXCEPT
void updateSpectralFlux() FL_NO_EXCEPT
void setConfig(const ReactiveConfig &config) FL_NO_EXCEPT
u8 getCurrentNote() FL_NO_EXCEPT
float getBackbeatConfidence() FL_NO_EXCEPT
float getPitchHz() FL_NO_EXCEPT
bool isBassBeat() const FL_NO_EXCEPT
void applyLoudnessCompensation() FL_NO_EXCEPT
void setGain(float gain) FL_NO_EXCEPT
fl::unique_ptr< SpectralFluxDetector > mSpectralFluxDetector
bool isDiminuendo() FL_NO_EXCEPT
void applySpectralEqualization() FL_NO_EXCEPT
SilenceEnvelope mDominantFrequencyEnvelope
fl::unique_ptr< SpectralEqualizer > mSpectralEqualizer
bool isCrescendo() FL_NO_EXCEPT
CRGB volumeToColor(const CRGBPalette16 &palette) const FL_NO_EXCEPT
fl::unique_ptr< detector::MultiBandBeat > mMultiBandBeatDetector
const SpectralEqualizer::Stats & getSpectralEqualizerStats() const FL_NO_EXCEPT
shared_ptr< Context > mContext
fl::unique_ptr< Processor > mAudioProcessor
float getMoodArousal() FL_NO_EXCEPT
void applyAWeighting() FL_NO_EXCEPT
u32 getSilenceDuration() FL_NO_EXCEPT
float getBuildupIntensity() FL_NO_EXCEPT
SilenceEnvelope mMagnitudeEnvelope
float getBassEnergy() const FL_NO_EXCEPT
float getPitchConfidence() FL_NO_EXCEPT
float getBPM() FL_NO_EXCEPT
void applyGain() FL_NO_EXCEPT
float getMid() const FL_NO_EXCEPT
ReactiveConfig mConfig
float getTempoBPM() FL_NO_EXCEPT
float getBuildupProgress() FL_NO_EXCEPT
void updateVolumeAndPeak(const Sample &sample) FL_NO_EXCEPT
const NoiseFloorTracker::Stats & getNoiseFloorStats() const FL_NO_EXCEPT
float getNoteConfidence() FL_NO_EXCEPT
u8 getCurrentBeatNumber() FL_NO_EXCEPT
SignalConditioner mSignalConditioner
float computeRMS(const fl::vector< fl::i16 > &samples) FL_NO_EXCEPT
float getDownbeatConfidence() FL_NO_EXCEPT
NoiseFloorTracker mNoiseFloorTracker
bool isSpectralEqualizerEnabled() const FL_NO_EXCEPT
~Reactive() FL_NO_EXCEPT
const SignalConditioner::Stats & getSignalConditionerStats() const FL_NO_EXCEPT
bool isSnare() FL_NO_EXCEPT
const Data & getSmoothedData() const FL_NO_EXCEPT
float getNoteVelocity() FL_NO_EXCEPT
float getKeyConfidence() FL_NO_EXCEPT
fl::unique_ptr< detector::MusicalBeat > mMusicalBeatDetector
float getBeatConfidence() FL_NO_EXCEPT
float getPeakLevel() FL_NO_EXCEPT
float getMidEnergy() const FL_NO_EXCEPT
void applyScaling() FL_NO_EXCEPT
float getTempoConfidence() FL_NO_EXCEPT
float getDynamicTrend() FL_NO_EXCEPT
float getGain() const FL_NO_EXCEPT
float getDropImpact() FL_NO_EXCEPT
float getBackbeatStrength() FL_NO_EXCEPT
void detectEnhancedBeats(fl::u32 currentTimeMs) FL_NO_EXCEPT
fl::unique_ptr< PerceptualWeighting > mPerceptualWeighting
void begin(const ReactiveConfig &config=ReactiveConfig{}) FL_NO_EXCEPT
fl::array< float, 16 > mPreviousMagnitudes
Processor & ensureAudioProcessor() FL_NO_EXCEPT
fl::u8 volumeToScale255() const FL_NO_EXCEPT
void update(fl::u32 currentTimeMs) FL_NO_EXCEPT
float getVolume() const FL_NO_EXCEPT
float mapFrequencyBin(int fromBin, int toBin) FL_NO_EXCEPT
float getMidLevel() FL_NO_EXCEPT
static constexpr fl::u32 BEAT_COOLDOWN
void smoothResults() FL_NO_EXCEPT
float getChordConfidence() FL_NO_EXCEPT
fl::u8 frequencyToScale255(fl::u8 binIndex) const FL_NO_EXCEPT
bool isMidBeat() const FL_NO_EXCEPT
float getEnergyLevel() FL_NO_EXCEPT
bool isKick() FL_NO_EXCEPT
float getTransientStrength() FL_NO_EXCEPT
SilenceEnvelope mSpectralFluxEnvelope
FrequencyBinMapper mFrequencyBinMapper
float getSpectralFlux() const FL_NO_EXCEPT
void processFFT(const Sample &sample) FL_NO_EXCEPT
SignalConditioner performs low-level audio preprocessing to clean raw PCM samples before FFT analysis...
SpectralEqualizer applies frequency-dependent gain correction to address mid-frequency dominance and ...
~SpectralFluxDetector() FL_NO_EXCEPT
bool detectOnset(span< const float, 16 > currentBins) FL_NO_EXCEPT
float calculateSpectralFlux(span< const float, 16 > currentBins, span< const float, 16 > previousBins) FL_NO_EXCEPT
float getThreshold() const FL_NO_EXCEPT
void setThreshold(float threshold) FL_NO_EXCEPT
fl::array< float, 16 > mPreviousMagnitudes
MultiBandBeat performs frequency-specific beat detection.
MusicalBeat distinguishes true musical beats from random onset detection.
#define constexpr
Declares that it is possible to evaluate a value at compile time, introduced in C++11.
Definition cpp_compat.h:15
Utility functions for color fill, palettes, blending, and more.
Defines the 8-bit red, green, and blue (RGB) pixel type in the fl namespace.
float frequencyBins[16]
unsigned char u8
Definition s16x16x4.h:132
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
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.
Base definition for an LED controller.
Representation of an 8-bit RGB pixel (Red, Green, Blue)
Definition crgb.h:38
void setThresholds(float bassThresh, float midThresh, float trebleThresh) FL_NO_EXCEPT
~BeatDetectors() FL_NO_EXCEPT
void detectBeats(span< const float, 16 > frequencyBins, Data &audioData) FL_NO_EXCEPT
SpectralFluxDetector combined