59 fl::u32 currentTimeMs = sample.
timestamp();
92 const auto& pcmData = sample.
pcm();
93 if (pcmData.empty())
return;
104 for (
int i = 0; i < 16; ++i) {
105 if (i <
static_cast<int>(
mFFTBins.bins_raw.size())) {
113 for (
int i = 0; i < 16; ++i) {
118 float maxMagnitude = 0.0f;
120 for (
int i = 0; i < 16; ++i) {
129 const float binCenterFrequencies[16] = {
148 mCurrentData.dominantFrequency = binCenterFrequencies[maxBin];
154 const auto& pcmData = sample.
pcm();
155 if (pcmData.empty()) {
166 float maxSample = 0.0f;
167 for (fl::i16 pcmSample : pcmData) {
168 float absSample = (pcmSample < 0) ? -pcmSample : pcmSample;
169 maxSample = (maxSample > absSample) ? maxSample : absSample;
182 float agcAttackRate =
mConfig.attack / 255.0f * 0.2f + 0.01f;
183 float agcDecayRate =
mConfig.decay / 255.0f * 0.05f + 0.001f;
196 float targetLevel = 16384.0f;
197 float newMultiplier = targetLevel /
mMaxSample;
226 currentVolume > 5.0f) {
234 float beatAttackRate =
mConfig.attack / 255.0f * 0.5f + 0.1f;
235 float beatDecayRate =
mConfig.decay / 255.0f * 0.3f + 0.05f;
248 float gainMultiplier =
static_cast<float>(
mConfig.gain) / 128.0f;
254 for (
int i = 0; i < 16; ++i) {
264 for (
int i = 0; i < 16; ++i) {
272 for (
int i = 0; i < 16; ++i) {
278 value = logf(value) * 20.0f;
290 value = sqrtf(value) * 8.0f;
310 float attackFactor = 1.0f - (
mConfig.attack / 255.0f * 0.9f);
311 float decayFactor = 1.0f - (
mConfig.decay / 255.0f * 0.95f);
343 for (
int i = 0; i < 16; ++i) {
423 return static_cast<fl::u8>(vol);
429 return CRGB(index, index, index);
433 if (binIndex >= 16)
return 0;
435 float value = (
mCurrentData.frequencyBins[binIndex] < 0.0f) ? 0.0f :
437 return static_cast<fl::u8>(value);
465 for (
int i = 0; i < 16; ++i) {
531 if (fromBin < 0 || toBin >=
static_cast<int>(
mFFTBins.size()) || fromBin > toBin) {
536 for (
int i = fromBin; i <= toBin; ++i) {
537 if (i <
static_cast<int>(
mFFTBins.bins_raw.size())) {
542 return sum /
static_cast<float>(toBin - fromBin + 1);
546 if (samples.
empty())
return 0.0f;
548 float sumSquares = 0.0f;
549 for (
const auto& sample : samples) {
550 float f =
static_cast<float>(sample);
554 return sqrtf(sumSquares / samples.
size());
569#if SKETCH_HAS_LOTS_OF_MEMORY
571 for (fl::size i = 0; i < mFluxHistory.size(); ++i) {
572 mFluxHistory[i] = 0.0f;
584#if SKETCH_HAS_LOTS_OF_MEMORY
585 for (fl::size i = 0; i < mFluxHistory.size(); ++i) {
586 mFluxHistory[i] = 0.0f;
595#if SKETCH_HAS_LOTS_OF_MEMORY
597 mFluxHistory[mHistoryIndex] = flux;
598 mHistoryIndex = (mHistoryIndex + 1) % mFluxHistory.size();
600 float adaptiveThreshold = calculateAdaptiveThreshold();
601 return flux > adaptiveThreshold;
612 for (
int i = 0; i < 16; ++i) {
613 float diff = currentBins[i] - previousBins[i];
620 for (
int i = 0; i < 16; ++i) {
635#if SKETCH_HAS_LOTS_OF_MEMORY
636float SpectralFluxDetector::calculateAdaptiveThreshold() {
639 for (fl::size i = 0; i < mFluxHistory.size(); ++i) {
640 sum += mFluxHistory[i];
642 float average = sum / mFluxHistory.size();
659#if SKETCH_HAS_LOTS_OF_MEMORY
677 mBassEnergy = (frequencyBins[0] + frequencyBins[1]) / 2.0f;
678 mMidEnergy = (frequencyBins[6] + frequencyBins[7]) / 2.0f;
679 mTrebleEnergy = (frequencyBins[14] + frequencyBins[15]) / 2.0f;
681#if SKETCH_HAS_LOTS_OF_MEMORY
700#if SKETCH_HAS_LOTS_OF_MEMORY
701 bass.setThreshold(bassThresh);
702 mid.setThreshold(midThresh);
703 treble.setThreshold(trebleThresh);
705 combined.setThreshold((bassThresh + midThresh + trebleThresh) / 3.0f);
711#if SKETCH_HAS_LOTS_OF_MEMORY
715#if SKETCH_HAS_LOTS_OF_MEMORY
717 for (fl::size i = 0; i < mLoudnessHistory.size(); ++i) {
718 mLoudnessHistory[i] = 0.0f;
729 for (
int i = 0; i < 16; ++i) {
736 float currentLoudness = data.
volume;
739 float compensationFactor = 1.0f;
740 if (currentLoudness < referenceLevel) {
742 compensationFactor = 1.0f + (referenceLevel - currentLoudness) / referenceLevel * 0.3f;
743 }
else if (currentLoudness > referenceLevel * 1.5f) {
745 compensationFactor = 1.0f - (currentLoudness - referenceLevel * 1.5f) / (referenceLevel * 2.0f) * 0.2f;
749 for (
int i = 0; i < 16; ++i) {
753#if SKETCH_HAS_LOTS_OF_MEMORY
float rms(Slice< const int16_t > data)
fl::array< float, 16 > mPreviousMagnitudes
bool isTrebleBeat() const
CRGB volumeToColor(const CRGBPalette16 &palette) const
const AudioData & getData() const
void update(fl::u32 currentTimeMs)
void processFFT(const AudioSample &sample)
fl::u8 volumeToScale255() const
void detectBeat(fl::u32 currentTimeMs)
AudioReactiveConfig mConfig
void updateSpectralFlux()
void mapFFTBinsToFrequencyChannels()
float getSpectralFlux() const
float getMidEnergy() const
float computeRMS(const fl::vector< fl::i16 > &samples)
static constexpr float PINK_NOISE_COMPENSATION[16]
void calculateBandEnergies()
void updateVolumeAndPeak(const AudioSample &sample)
float getTrebleEnergy() const
fl::unique_ptr< SpectralFluxDetector > mSpectralFluxDetector
static constexpr fl::u32 BEAT_COOLDOWN
float mapFrequencyBin(int fromBin, int toBin)
void detectEnhancedBeats(fl::u32 currentTimeMs)
void applyPerceptualWeighting()
const AudioData & getSmoothedData() const
void begin(const AudioReactiveConfig &config=AudioReactiveConfig{})
void setConfig(const AudioReactiveConfig &config)
fl::u8 frequencyToScale255(fl::u8 binIndex) const
float getBassEnergy() const
void processSample(const AudioSample &sample)
fl::unique_ptr< PerceptualWeighting > mPerceptualWeighting
const VectorPCM & pcm() const
void fft(FFTBins *out) const
fl::u32 timestamp() const
void applyLoudnessCompensation(AudioData &data, float referenceLevel) const
static constexpr float A_WEIGHTING_COEFFS[16]
void applyAWeighting(AudioData &data) const
void setThreshold(float threshold)
float calculateSpectralFlux(const float *currentBins, const float *previousBins)
bool detectOnset(const float *currentBins, const float *previousBins)
fl::array< float, 16 > mPreviousMagnitudes
float getThreshold() const
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args)
HeapVector< T, Allocator > vector
#define SKETCH_HAS_LOTS_OF_MEMORY
Representation of an RGB pixel (Red, Green, Blue)
void detectBeats(const float *frequencyBins, AudioData &audioData)
void setThresholds(float bassThresh, float midThresh, float trebleThresh)
float mPreviousBassEnergy
float mPreviousTrebleEnergy
SpectralFluxDetector combined