24 for (fl::size i = 0; i < mPreviousMagnitudes.size(); ++i) {
25 mPreviousMagnitudes[i] = 0.0f;
30 SilenceEnvelope::Config envCfg;
31 envCfg.decayTauSeconds = 0.2f;
32 envCfg.targetValue = 0.0f;
33 mDominantFrequencyEnvelope.configure(envCfg);
34 mMagnitudeEnvelope.configure(envCfg);
35 mSpectralFluxEnvelope.configure(envCfg);
53 scConfig.
enableNoiseGate = config.noiseGate && config.enableSignalConditioning;
58 nfConfig.
enabled = config.enableNoiseFloorTracking;
68 fbmConfig.
maxFrequency =
static_cast<float>(config.sampleRate) / 2.0f;
79 const float m =
fl::logf(fmax / fmin);
81 for (
int i = 0; i < 16; ++i) {
82 binCenters[i] = fmin *
fl::expf(m *
static_cast<float>(i) / 15.0f);
95 if (config.enableMusicalBeatDetection) {
100 mbdConfig.
minBPM = config.musicalBeatMinBPM;
101 mbdConfig.
maxBPM = config.musicalBeatMaxBPM;
111 if (config.enableMultiBandBeats) {
126 if (config.enableSpectralEqualizer) {
149 mContext->setSampleRate(config.sampleRate);
170 fl::u32 currentTimeMs =
sample.timestamp();
176 if (
mConfig.enableSignalConditioning) {
178 if (!processedSample.
isValid()) {
184 if (
mConfig.enableNoiseFloorTracking) {
185 float rms = processedSample.
rms();
190 mContext->setSample(processedSample);
201 if (
mConfig.enableNoiseFloorTracking) {
202 constexpr float kSilenceRmsThreshold = 10.0f;
203 mContext->setSilent(processedSample.
rms() < kSilenceRmsThreshold);
223 for (
int i = 0; i < 16; ++i) {
245 const bool silent =
mContext->isSilent();
282 const auto& pcmData =
sample.pcm();
283 if (pcmData.empty())
return;
286 auto cachedBins =
mContext->getFFT16();
299 if (rawBins.
empty()) {
300 for (
int i = 0; i < 16; ++i) {
307 for (
int i = 0; i < 16; ++i) {
308 if (i <
static_cast<int>(rawBins.
size())) {
321 float maxMagnitude = 0.0f;
323 for (
int i = 0; i < 16; ++i) {
337 const auto& pcmData =
sample.pcm();
338 if (pcmData.empty()) {
349 float raw =
rms / 23170.0f;
359 float maxSample = 0.0f;
360 for (fl::i16 pcmSample : pcmData) {
361 float absSample = (pcmSample < 0) ? -
static_cast<float>(pcmSample) :
static_cast<float>(pcmSample);
362 maxSample = (maxSample > absSample) ? maxSample : absSample;
382 currentVolume > 0.02f) {
390 float beatAttackRate =
mConfig.attack / 255.0f * 0.5f + 0.1f;
391 float beatDecayRate =
mConfig.decay / 255.0f * 0.3f + 0.05f;
404 float gainMultiplier =
static_cast<float>(
mConfig.gain) / 128.0f;
415 for (
int i = 0; i < 16; ++i) {
422 for (
int i = 0; i < 16; ++i) {
460 float attackFactor = 1.0f - (
mConfig.attack / 255.0f * 0.9f);
461 float decayFactor = 1.0f - (
mConfig.decay / 255.0f * 0.95f);
486 for (
int i = 0; i < 16; ++i) {
575 if (scaled < 0.0f) scaled = 0.0f;
576 if (scaled > 255.0f) scaled = 255.0f;
577 return static_cast<fl::u8>(scaled);
583 return CRGB(index, index, index);
587 if (binIndex >= 16)
return 0;
605 if (!
mConfig.enableSpectralEqualizer) {
610 float equalizedBins[16];
616 for (
int i = 0; i < 16; ++i) {
634 for (
int i = 0; i < 16; ++i) {
643 for (
int i = 0; i < 16; ++i) {
656 !
mConfig.enableMusicalBeatDetection && !
mConfig.enableMultiBandBeats) {
666 bool onsetDetected =
false;
667 float onsetStrength = 0.0f;
684 if (
mConfig.enableMusicalBeatDetection) {
692 }
else if (onsetDetected) {
699 if (
mConfig.enableMultiBandBeats) {
705 }
else if (
mConfig.enableMultiBand) {
738 if (fromBin < 0 || toBin >=
static_cast<int>(
mFFTBins.bands()) || fromBin > toBin) {
743 for (
int i = fromBin; i <= toBin; ++i) {
744 if (i <
static_cast<int>(
mFFTBins.raw().size())) {
749 return sum /
static_cast<float>(toBin - fromBin + 1);
753 if (samples.empty())
return 0.0f;
755 float sumSquares = 0.0f;
756 for (
const auto&
sample : samples) {
757 float f =
static_cast<float>(
sample);
761 return sqrtf(sumSquares / samples.size());
767#if SKETCH_HAS_LARGE_MEMORY
776#if SKETCH_HAS_LARGE_MEMORY
778 for (fl::size i = 0; i < mFluxHistory.size(); ++i) {
779 mFluxHistory[i] = 0.0f;
791#if SKETCH_HAS_LARGE_MEMORY
792 for (fl::size i = 0; i < mFluxHistory.size(); ++i) {
793 mFluxHistory[i] = 0.0f;
802#if SKETCH_HAS_LARGE_MEMORY
804 mFluxHistory[mHistoryIndex] = flux;
805 mHistoryIndex = (mHistoryIndex + 1) % mFluxHistory.size();
807 float adaptiveThreshold = calculateAdaptiveThreshold();
808 return flux > adaptiveThreshold;
819 for (
int i = 0; i < 16; ++i) {
820 float diff = currentBins[i] - previousBins[i];
827 for (
int i = 0; i < 16; ++i) {
842#if SKETCH_HAS_LARGE_MEMORY
843float SpectralFluxDetector::calculateAdaptiveThreshold()
FL_NO_EXCEPT {
846 for (fl::size i = 0; i < mFluxHistory.size(); ++i) {
847 sum += mFluxHistory[i];
849 float average = sum / mFluxHistory.size();
866#if SKETCH_HAS_LARGE_MEMORY
884 mBassEnergy = (frequencyBins[0] + frequencyBins[1]) / 2.0f;
885 mMidEnergy = (frequencyBins[6] + frequencyBins[7]) / 2.0f;
886 mTrebleEnergy = (frequencyBins[14] + frequencyBins[15]) / 2.0f;
900#if SKETCH_HAS_LARGE_MEMORY
901 bass.setThreshold(bassThresh);
902 mid.setThreshold(midThresh);
903 treble.setThreshold(trebleThresh);
905 combined.setThreshold((bassThresh + midThresh + trebleThresh) / 3.0f);
911#if SKETCH_HAS_LARGE_MEMORY
915#if SKETCH_HAS_LARGE_MEMORY
917 for (fl::size i = 0; i < mLoudnessHistory.size(); ++i) {
918 mLoudnessHistory[i] = 0.0f;
929 for (
int i = 0; i < 16; ++i) {
938 float currentLoudness = data.volumeRaw;
941 float compensationFactor = 1.0f;
942 if (currentLoudness < referenceLevel) {
944 compensationFactor = 1.0f + (referenceLevel - currentLoudness) / referenceLevel * 0.3f;
945 }
else if (currentLoudness > referenceLevel * 1.5f) {
947 compensationFactor = 1.0f - (currentLoudness - referenceLevel * 1.5f) / (referenceLevel * 2.0f) * 0.2f;
951 for (
int i = 0; i < 16; ++i) {
952 data.frequencyBins[i] *= compensationFactor;
955#if SKETCH_HAS_LARGE_MEMORY
982 return mConfig.enableSpectralEqualizer;
float rms(fl::span< const int16_t > data)
bool detectBeat(float energy)
#define FL_ASSERT(x, MSG)
Get current statistics (for monitoring/debugging)
void applyAWeighting(Data &data) const FL_NO_EXCEPT
static constexpr float A_WEIGHTING_COEFFS[16]
~PerceptualWeighting() FL_NO_EXCEPT
PerceptualWeighting() FL_NO_EXCEPT
void applyLoudnessCompensation(Data &data, float referenceLevel) const FL_NO_EXCEPT
float getBPM() FL_NO_EXCEPT
float getChordConfidence() FL_NO_EXCEPT
float getVocalConfidence() FL_NO_EXCEPT
float getPitch() FL_NO_EXCEPT
float getMoodArousal() FL_NO_EXCEPT
float getPitchConfidence() FL_NO_EXCEPT
float getNoteVelocity() FL_NO_EXCEPT
float getMidLevel() FL_NO_EXCEPT
float getTempoBPM() FL_NO_EXCEPT
float getDropImpact() FL_NO_EXCEPT
float getDownbeatConfidence() FL_NO_EXCEPT
float getDynamicTrend() FL_NO_EXCEPT
bool isSnare() FL_NO_EXCEPT
float getTrebleLevel() FL_NO_EXCEPT
bool isDiminuendo() FL_NO_EXCEPT
float getMeasurePhase() FL_NO_EXCEPT
float getTransientStrength() FL_NO_EXCEPT
float getNoteConfidence() FL_NO_EXCEPT
float getBassLevel() FL_NO_EXCEPT
bool isKick() FL_NO_EXCEPT
float getKeyConfidence() FL_NO_EXCEPT
float getBackbeatConfidence() FL_NO_EXCEPT
float getPeakLevel() FL_NO_EXCEPT
u32 getSilenceDuration() FL_NO_EXCEPT
u8 getCurrentBeatNumber() FL_NO_EXCEPT
bool isCrescendo() FL_NO_EXCEPT
float getTempoConfidence() FL_NO_EXCEPT
float getEnergy() FL_NO_EXCEPT
float getBuildupProgress() FL_NO_EXCEPT
bool isSilent() FL_NO_EXCEPT
bool isTom() FL_NO_EXCEPT
float getBackbeatStrength() FL_NO_EXCEPT
float getBeatConfidence() FL_NO_EXCEPT
float getMoodValence() FL_NO_EXCEPT
bool isHiHat() FL_NO_EXCEPT
float getBuildupIntensity() FL_NO_EXCEPT
u8 getCurrentNote() 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
bool isBeat() const FL_NO_EXCEPT
float mPinkNoiseGains[16]
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 detectBeat(fl::u32 currentTimeMs) 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
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
bool isSilent() 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
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
const VectorPCM & pcm() const FL_NO_EXCEPT
bool isValid() const FL_NO_EXCEPT
float rms() const FL_NO_EXCEPT
Get current statistics (for debugging/monitoring)
Get statistics (for debugging/monitoring)
~SpectralFluxDetector() FL_NO_EXCEPT
void reset() 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
SpectralFluxDetector() FL_NO_EXCEPT
void setThreshold(float threshold) FL_NO_EXCEPT
fl::array< float, 16 > mPreviousMagnitudes
constexpr bool empty() const FL_NO_EXCEPT
constexpr fl::size size() const FL_NO_EXCEPT
fl::size size() const FL_NO_EXCEPT
High-resolution microphone frequency response data and utilities.
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NO_EXCEPT
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
u32 samplesPerFrame
Samples per frame - used for timing calculations.
float midThreshold
Mid beat threshold (0.0-1.0) Energy increase required to trigger mid beat.
u32 sampleRate
Sample rate (Hz) - used for timing calculations.
float maxBPM
Maximum BPM to detect (default: 250 BPM)
float trebleThreshold
Treble beat threshold (0.0-1.0) Energy increase required to trigger treble beat.
float minBeatConfidence
Minimum beat confidence to report a beat (0.0-1.0) Higher values = fewer false positives,...
float minBPM
Minimum BPM to detect (default: 50 BPM)
float bassThreshold
Bass beat threshold (0.0-1.0) Energy increase required to trigger bass beat.
Configuration for musical beat detection.
Configuration for multi-band beat detection.
void computePinkNoiseGains(const float *binCenters, int numBins, float *out) FL_NO_EXCEPT
Compute pink noise compensation gains for all bins.
float computeAudioDt(fl::size pcmSize, int sampleRate) FL_NO_EXCEPT
Compute the time delta (in seconds) for an audio buffer.
bool enabled
Enable noise floor tracking.
bool enableDCRemoval
Enable DC offset removal (running average high-pass filter)
bool enableNoiseGate
Enable noise gate with hysteresis.
EqualizationCurve curve
Equalization curve type.
bool useLogSpacing
Use logarithmic spacing (recommended for audio) Logarithmic spacing provides better bass/mid/treble s...
u32 fftBinCount
Number of FFT bins available from FFT output For 512-sample FFT at 22050 Hz: 256 bins (512/2)
bool enableSpikeFilter
Enable spike filtering for I2S glitches.
float minFrequency
Minimum frequency (Hz) - default 20 Hz (bass)
float maxFrequency
Maximum frequency (Hz) - default 16000 Hz (treble)
u32 sampleRate
Sample rate (Hz) - must match FFT sample rate.
size numBands
Number of frequency bands (must match FrequencyBinMapper output)
FrequencyBinMode mode
Number of output frequency bins (16 or 32)
Configuration for frequency bin mapping.
Configuration for signal conditioning pipeline.
Configuration for noise floor tracking.
Configuration for spectral equalizer.
float logf(float value) FL_NO_EXCEPT
u8 u8 fract8 blur_amount FL_NO_EXCEPT
constexpr int type_rank< T >::value
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.
float sqrtf(float value) FL_NO_EXCEPT
float expf(float value) FL_NO_EXCEPT
Base definition for an LED controller.
Representation of an 8-bit RGB pixel (Red, Green, Blue)
void reset() FL_NO_EXCEPT
void setThresholds(float bassThresh, float midThresh, float trebleThresh) FL_NO_EXCEPT
~BeatDetectors() FL_NO_EXCEPT
float mPreviousTrebleEnergy
float mPreviousBassEnergy
BeatDetectors() FL_NO_EXCEPT
void detectBeats(span< const float, 16 > frequencyBins, Data &audioData) FL_NO_EXCEPT
SpectralFluxDetector combined
static float DefaultMinFrequency() FL_NO_EXCEPT
static float DefaultMaxFrequency() FL_NO_EXCEPT