FastLED 3.9.15
Loading...
Searching...
No Matches
transient.cpp.hpp
Go to the documentation of this file.
3#include "fl/math/math.h"
4#include "fl/stl/algorithm.h"
5#include "fl/stl/noexcept.h"
6
7namespace fl {
8namespace audio {
9namespace detector {
10
12 : mTransientDetected(false)
13 , mStrength(0.0f)
14 , mThreshold(1.5f)
15 , mSensitivity(1.0f)
16 , mMinIntervalMs(30) // Allow up to ~33 transients per second
18 , mPreviousEnergy(0.0f)
19 , mCurrentEnergy(0.0f)
20 , mAttackTime(0.0f)
21{
22 mPreviousHighFreq.resize(16, 0.0f);
23}
24
26
28 mRetainedFFT = context->getFFT16();
29 const fft::Bins& fft = *mRetainedFFT;
30 u32 timestamp = context->getTimestamp();
31
32 // Calculate high-frequency energy (transients have strong high-freq components)
34
35 // Calculate energy flux (rate of change)
37
38 // Detect transient
39 mTransientDetected = detectTransient(flux, timestamp);
40
42 updateAttackTime(flux);
43 mLastTransientTime = timestamp;
44 }
45
46 // Update previous energy for next frame
48
49 // Filter energy through HampelFilter to reject outliers before history
50 float filteredEnergy = mEnergyOutlierFilter.update(mCurrentEnergy);
51
52 // Update energy history with outlier-rejected values
53 if (mEnergyHistory.size() >= ENERGY_HISTORY_SIZE) {
54 mEnergyHistory.pop_front();
55 }
56 mEnergyHistory.push_back(filteredEnergy);
57}
58
61 if (onTransient) {
63 }
66 }
67 if (onAttack) {
69 }
70 }
71}
72
74 mTransientDetected = false;
75 mStrength = 0.0f;
77 mPreviousEnergy = 0.0f;
78 mCurrentEnergy = 0.0f;
79 mAttackTime = 0.0f;
80 fl::fill(mPreviousHighFreq.begin(), mPreviousHighFreq.end(), 0.0f);
81 mEnergyHistory.clear();
83}
84
86 // Focus on mid-high to high frequencies (bins 4-15) for transient detection
87 // Low frequencies tend to have slower attack times
88 float energy = 0.0f;
89 size numBins = fft.raw().size();
90
91 // Weight higher frequencies more for transient detection
92 for (size i = 4; i < numBins; i++) {
93 float weight = static_cast<float>(i) / static_cast<float>(numBins);
94 energy += fft.raw()[i] * (1.0f + weight);
95 }
96
97 size validBins = (numBins > 4) ? (numBins - 4) : 1;
98 return energy / static_cast<float>(validBins);
99}
100
101float Transient::calculateEnergyFlux(float currentEnergy) {
102 // Calculate positive energy flux (increase in energy)
103 float flux = fl::max(0.0f, currentEnergy - mPreviousEnergy);
104
105 // Normalize by previous energy to get relative change
106 if (mPreviousEnergy > 1e-6f) {
107 flux = flux / mPreviousEnergy;
108 }
109
110 return flux;
111}
112
113bool Transient::detectTransient(float flux, u32 timestamp) {
114 // Check cooldown period
115 u32 timeSinceLastTransient = timestamp - mLastTransientTime;
116 if (timeSinceLastTransient < mMinIntervalMs) {
117 return false;
118 }
119
120 // Calculate adaptive threshold based on recent energy history
121 float adaptiveThreshold = 0.0f;
122 if (!mEnergyHistory.empty()) {
123 float sum = 0.0f;
124 for (size i = 0; i < mEnergyHistory.size(); i++) {
125 sum += mEnergyHistory[i];
126 }
127 float meanEnergy = sum / static_cast<float>(mEnergyHistory.size());
128
129 // Threshold is based on mean energy and configured threshold
130 if (meanEnergy > 1e-6f) {
131 adaptiveThreshold = mThreshold * mSensitivity;
132 }
133 }
134
135 // Check if flux exceeds threshold
136 if (flux <= adaptiveThreshold) {
137 mStrength = 0.0f;
138 return false;
139 }
140
141 // Calculate strength based on how much we exceeded threshold
142 if (adaptiveThreshold > 0.0f) {
143 mStrength = fl::min(1.0f, (flux - adaptiveThreshold) / adaptiveThreshold);
144 } else {
145 mStrength = fl::min(1.0f, flux);
146 }
147
148 return true;
149}
150
152 // Estimate attack time based on flux magnitude
153 // Higher flux = faster attack = shorter attack time
154 // Range: ~1-20ms for typical transients
155 const float minAttackTime = 1.0f; // ms
156 const float maxAttackTime = 20.0f; // ms
157
158 // Inverse relationship: stronger flux = shorter attack time
159 float normalized = fl::min(1.0f, flux / 10.0f);
160 mAttackTime = maxAttackTime - (normalized * (maxAttackTime - minAttackTime));
161}
162
163} // namespace detector
164} // namespace audio
165} // namespace fl
float calculateEnergyFlux(float currentEnergy)
function_list< void()> onTransient
Definition transient.h:33
function_list< void(float strength)> onTransientWithStrength
Definition transient.h:34
vector< float > mPreviousHighFreq
Definition transient.h:61
bool detectTransient(float flux, u32 timestamp)
float calculateHighFreqEnergy(const fft::Bins &fft)
~Transient() FL_NOEXCEPT override
void update(shared_ptr< Context > context) override
shared_ptr< const fft::Bins > mRetainedFFT
Definition transient.h:68
function_list< void(float strength)> onAttack
Definition transient.h:35
static constexpr size ENERGY_HISTORY_SIZE
Definition transient.h:63
deque< float > mEnergyHistory
Definition transient.h:62
HampelFilter< float, 7 > mEnergyOutlierFilter
Definition transient.h:66
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
Definition math.h:71
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
Definition math.h:75
void fill(Iterator first, Iterator last, const T &value) FL_NOEXCEPT
Definition algorithm.h:204
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT