FastLED 3.9.15
Loading...
Searching...
No Matches

◆ update()

void fl::audio::detector::Vibe::update ( shared_ptr< Context > context)
overridevirtual

Implements fl::audio::Detector.

Definition at line 45 of file vibe.cpp.hpp.

45 {
46 if (!context) {
47 return;
48 }
49
51 mSampleRate = context->getSampleRate();
52
53 // --- Step 1: Get 3-band energy from shared context ---
54 BandEnergy energy = context->getBandEnergy();
56
57 span<const i16> pcm = context->getPCM();
58
59 // --- Step 2: Read bass/mid/treb directly ---
60 mImm[0] = energy.bass;
61 mImm[1] = energy.mid;
62 mImm[2] = energy.treb;
63
64 // --- Step 3: Temporal blending (MilkDrop v2.25c algorithm) ---
65 // Compute effective FPS from audio buffer duration
66 float dt = computeAudioDt(pcm.size(), mSampleRate);
67 float actualFps = (dt > 0.0f) ? (1.0f / dt) : mTargetFps;
68
69 if (mFrameCount == 1) {
70 // MilkDrop first-frame initialization: set averages directly
71 // Prevents false spikes and startup transients
72 for (int i = 0; i < 3; i++) {
73 mAvg[i] = mImm[i];
74 mLongAvg[i] = mImm[i];
75 }
76 } else {
77 // Pre-compute FPS-adjusted rates (avoids redundant powf in loop)
78 float attackRate = adjustRateToFPS(0.2f, 30.0f, actualFps);
79 float decayRate = adjustRateToFPS(0.5f, 30.0f, actualFps);
80 float longRate = adjustRateToFPS(0.992f, 30.0f, actualFps);
81
82 for (int i = 0; i < 3; i++) {
83 // Short-term average: asymmetric attack/decay
84 // Fast attack (rate=0.2 at 30fps → ~80% new signal on beats)
85 // Slow decay (rate=0.5 at 30fps → graceful fadeout)
86 float rate = (mImm[i] > mAvg[i]) ? attackRate : decayRate;
87 mAvg[i] = mAvg[i] * rate + mImm[i] * (1.0f - rate);
88
89 // Long-term average: slow symmetric EMA (MilkDrop v2.25c algorithm)
90 // Rate = 0.992 at 30fps → tau ≈ 4.2 seconds
91 // Tracks the overall energy level for self-normalization.
92 // Unlike a running maximum, this centers relative levels around 1.0,
93 // giving beats proper excursions above 1.0 and quiet sections below 1.0.
94 mLongAvg[i] = mLongAvg[i] * longRate + mAvg[i] * (1.0f - longRate);
95 }
96 }
97
98 // --- Step 4: Self-normalizing relative levels ---
99 // Division by long-term average makes levels independent of volume/genre.
100 // Values hover around 1.0; >1 means louder than average, <1 means quieter.
101 for (int i = 0; i < 3; i++) {
102 if (mLongAvg[i] < 0.001f) {
103 mImmRel[i] = 1.0f;
104 mAvgRel[i] = 1.0f;
105 } else {
106 mImmRel[i] = mImm[i] / mLongAvg[i];
107 mAvgRel[i] = mAvg[i] / mLongAvg[i];
108 }
109 }
110
111 // --- Step 4b: Silence gate ---
112 // MilkDrop's self-normalization drives mImmRel toward 1.0 when input is
113 // silent (noise / noise → 1.0), and the < 0.001f clamp above also pins
114 // to 1.0. Neither is what LED effects want when music stops. Route each
115 // band through its SilenceEnvelope: pass-through during audio, exponential
116 // decay toward 0 during silence. The Context::isSilent() flag is populated
117 // by Processor/Reactive from NoiseFloorTracker. Re-uses dt computed above.
118 const bool silent = context->isSilent();
119 for (int i = 0; i < 3; i++) {
120 mImmRel[i] = mImmRelEnv[i].update(silent, mImmRel[i], dt);
121 mAvgRel[i] = mAvgRelEnv[i].update(silent, mAvgRel[i], dt);
122 }
123
124 // --- Step 5: Spike detection ---
125 // When immediate exceeds smoothed, energy is rising — a beat is in progress.
129
130 mBassSpike = mImmRel[0] > mAvgRel[0];
131 mMidSpike = mImmRel[1] > mAvgRel[1];
132 mTrebSpike = mImmRel[2] > mAvgRel[2];
133}
SilenceEnvelope mImmRelEnv[3]
Definition vibe.h:170
static float adjustRateToFPS(float rateAtFps1, float fps1, float actualFps) FL_NOEXCEPT
Definition vibe.cpp.hpp:196
SilenceEnvelope mAvgRelEnv[3]
Definition vibe.h:171
static int sVibeFFTCount
Definition vibe.cpp.hpp:19
float computeAudioDt(fl::size pcmSize, int sampleRate) FL_NOEXCEPT
Compute the time delta (in seconds) for an audio buffer.

References Vibe(), adjustRateToFPS(), fl::audio::BandEnergy::bass, fl::audio::computeAudioDt(), mAvg, mAvgRel, mAvgRelEnv, mBassSpike, mFrameCount, fl::audio::BandEnergy::mid, mImm, mImmRel, mImmRelEnv, mLongAvg, mMidSpike, mPrevBassSpike, mPrevMidSpike, mPrevTrebSpike, mSampleRate, mTargetFps, mTrebSpike, fl::span< T, Extent >::size(), fl::audio::detector::sVibeFFTCount, fl::audio::BandEnergy::treb, and update().

Referenced by ~Vibe(), and update().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: