MCPcopy Create free account
hub / github.com/FastLED/FastLED / classify

Method classify

examples/AnimartrixRing/sound_orchestrator.cpp:101–174  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

99}
100
101SoundState SoundOrchestrator::classify(fl::u32 nowMs) {
102 if (!mProcessor) return SoundState::Silence;
103
104 const bool isSilent = mProcessor->isSilent();
105
106 // Silence has its own asymmetric hysteresis (enter slow, exit fast) so a
107 // single audio sample can wake us up promptly but a brief gap won't drop
108 // us into ambient mode mid-song.
109 if (isSilent) {
110 if (mSilentSinceMs == 0) mSilentSinceMs = nowMs;
111 mNonSilentSinceMs = 0;
112 } else {
113 if (mNonSilentSinceMs == 0) mNonSilentSinceMs = nowMs;
114 mSilentSinceMs = 0;
115 }
116
117 // --- raw signals ---
118 const float tempoConf = mProcessor->getTempoConfidence();
119 const float beatConf = mProcessor->getBeatConfidence();
120
121 // Silence-exit gate: when we're currently in Silence, require the
122 // configured silenceExitMs of *contiguous* non-silent audio before we
123 // even consider leaving. Without this, mNonSilentSinceMs would be
124 // tracked but never consulted, so a single non-silent frame would
125 // immediately bounce us out -- defeating the asymmetric silence
126 // hysteresis documented on OrchestratorConfig.
127 const bool silenceReleased =
128 !isSilent &&
129 mNonSilentSinceMs != 0 &&
130 (nowMs - mNonSilentSinceMs) >= mCfg.silenceExitMs;
131
132 // --- determine the candidate (instantaneous) state ---
133 SoundState instant;
134 if (isSilent && mSilentSinceMs && (nowMs - mSilentSinceMs) >= mCfg.silenceEnterMs) {
135 instant = SoundState::Silence;
136 } else if (mState == SoundState::Silence && !silenceReleased) {
137 // Hold Silence until the non-silent run reaches silenceExitMs.
138 instant = SoundState::Silence;
139 } else if (!isSilent &&
140 tempoConf >= mCfg.tempoConfidenceEnter &&
141 beatConf >= mCfg.beatConfidenceEnter) {
142 instant = SoundState::BpmLocked;
143 } else if (mState == SoundState::BpmLocked &&
144 (tempoConf >= mCfg.tempoConfidenceExit &&
145 beatConf >= mCfg.beatConfidenceExit)) {
146 // Stay locked: we're below "enter" but above "exit" thresholds.
147 instant = SoundState::BpmLocked;
148 } else if (mState == SoundState::Silence && isSilent) {
149 instant = SoundState::Silence;
150 } else {
151 instant = SoundState::Disorganized;
152 }
153
154 // --- hysteresis: candidate must hold for classifierHysteresisMs AND
155 // current state must have served minDwellMs before we accept the switch.
156 if (instant != mState) {
157 if (instant != mCandidate) {
158 mCandidate = instant;

Callers

nothing calls this directly

Calls 3

isSilentMethod · 0.45
getTempoConfidenceMethod · 0.45
getBeatConfidenceMethod · 0.45

Tested by

no test coverage detected