FastLED 3.9.15
Loading...
Searching...
No Matches
sound_orchestrator.h
Go to the documentation of this file.
1// sound_orchestrator.h - 3-state audio orchestrator for AnimartrixRing.
2//
3// Classifies the live audio stream into one of three states:
4// * Silence -> ambient visuals (slow, restrained)
5// * Disorganized -> energy / spectrum visuals (bass radial, mid hue,
6// treble sparkle); time warp is allowed but secondary
7// * BpmLocked -> beat geometry (rings/spirals) driven by kick,
8// snare, downbeat, measure phase
9//
10// Hysteresis: each state has a minimum dwell time so the classifier does not
11// chatter on borderline audio. Transition out of a state requires both:
12// 1. the entry condition for a *different* state to be satisfied for at
13// least kClassifierHysteresisMs of contiguous audio, and
14// 2. the current state to have been held for at least kMinDwellMs.
15//
16// This file is intentionally sketch-scoped: the building blocks it consumes
17// (Processor::isSilent / getTempoConfidence / getBeatConfidence / getEqBass /
18// onDownbeat / onKick / onSnare / getMeasurePhase / getVibeBass etc.) all
19// already live in src/fl/audio/. We're orchestrating, not adding primitives.
20#pragma once
21
22#include "FastLED.h"
26#include "fl/fx/fx_engine.h"
27#include "fl/stl/shared_ptr.h"
28#include "fl/stl/stdint.h"
29
30namespace animartrix_ring {
31
32enum class SoundState : fl::u8 {
36};
37
38const char *toString(SoundState s);
39
41 // Classifier thresholds.
42 float tempoConfidenceEnter = 0.60f;
43 float tempoConfidenceExit = 0.45f;
44 float beatConfidenceEnter = 0.50f;
45 float beatConfidenceExit = 0.35f;
46 fl::u32 silenceEnterMs = 700;
47 fl::u32 silenceExitMs = 150;
48
49 // Minimum dwell time per state (anti-chatter floor).
50 fl::u32 minDwellMs = 1500;
51
52 // How long the *other* state's entry condition must hold continuously
53 // before we accept a transition (additional hysteresis on top of dwell).
55
56 // BpmLocked: pulse decay (ms) for a single kick/snare/downbeat event.
57 fl::u32 pulseDecayMs = 220;
58
59 // Disorganized: how much vibe.bass scales engine speed (above baseline 1.0).
61
62 // Silence: ambient engine speed.
63 float silenceSpeed = 0.25f;
64
65 // BpmLocked: engine baseline speed (BPM modulation rides on top).
66 float bpmLockedBaseSpeed = 1.0f;
67};
68
72public:
75 fl::FxEngine *engine);
76
80 void begin();
81
85 float tick(fl::u32 nowMs, float manualSpeedScalar);
86
88 SoundState state() const { return mState; }
89 float lastEngineSpeed() const { return mLastEngineSpeed; }
90 fl::u32 stateEnteredAtMs() const { return mStateEnteredAtMs; }
91
93 void setConfig(const OrchestratorConfig &cfg) { mCfg = cfg; }
94 const OrchestratorConfig &config() const { return mCfg; }
95
96private:
97 // Visual bank selection per state.
98 static fl::AnimartrixAnim pickAnimationFor(SoundState s, fl::u32 nowMs);
99 void switchAnimationIfNeeded(SoundState newState, fl::u32 nowMs);
100
101 // Per-state behavior.
102 float driveSilence(fl::u32 nowMs, float manualSpeedScalar);
103 float driveDisorganized(fl::u32 nowMs, float manualSpeedScalar);
104 float driveBpmLocked(fl::u32 nowMs, float manualSpeedScalar);
105
106 // Classifier.
107 SoundState classify(fl::u32 nowMs);
108
109private:
113
115
117 fl::u32 mStateEnteredAtMs = 0;
119
120 // Classifier hysteresis: how long a *candidate* state has held continuously.
122 fl::u32 mCandidateSinceMs = 0;
123
124 // Silence hysteresis: separate timers because Silence enters slow / leaves fast.
125 fl::u32 mSilentSinceMs = 0; // 0 = "not currently silent"
126 fl::u32 mNonSilentSinceMs = 0;
127
128 // BpmLocked pulse state. Updated by event callbacks.
129 fl::u32 mLastKickMs = 0;
130 fl::u32 mLastSnareMs = 0;
131 fl::u32 mLastDownbeatMs = 0;
133
134 float mLastEngineSpeed = 1.0f;
135};
136
137} // namespace animartrix_ring
fl::Animartrix animartrix(xyMap, FIRST_ANIMATION)
float tick(fl::u32 nowMs, float manualSpeedScalar)
Per-frame tick.
void begin()
Wire up audio callbacks (downbeat/kick/snare).
float driveBpmLocked(fl::u32 nowMs, float manualSpeedScalar)
static fl::AnimartrixAnim pickAnimationFor(SoundState s, fl::u32 nowMs)
SoundOrchestrator(fl::shared_ptr< fl::audio::Processor > processor, fl::shared_ptr< fl::Animartrix > animartrix, fl::FxEngine *engine)
const OrchestratorConfig & config() const
void switchAnimationIfNeeded(SoundState newState, fl::u32 nowMs)
float driveSilence(fl::u32 nowMs, float manualSpeedScalar)
float driveDisorganized(fl::u32 nowMs, float manualSpeedScalar)
SoundState state() const
Observability.
fl::shared_ptr< fl::audio::Processor > mProcessor
fl::shared_ptr< fl::Animartrix > mAnimartrix
void setConfig(const OrchestratorConfig &cfg)
Override config (e.g. from UI sliders). Cheap; safe to call every tick.
Manages and renders multiple visual effects (Fx) for LED strips.
Definition fx_engine.h:33
const char * toString(SoundState s)
float tempoConfidenceExit
leave BpmLocked when tempoConf < this
fl::u32 silenceExitMs
sound must persist this long to leave Silence
float beatConfidenceEnter
also require beatConf >= this for BpmLocked
fl::u32 silenceEnterMs
silence must persist this long to enter Silence
float tempoConfidenceEnter
enter BpmLocked when tempoConf >= this
unsigned char u8
Definition stdint.h:131
AnimartrixAnim