FastLED 3.9.15
Loading...
Searching...
No Matches
AnimartrixRing.ino
Go to the documentation of this file.
1// @filter: (memory is large)
2
3// AnimartrixRing: Sample a circle from an Animartrix rectangular grid.
4//
5// Audio model (issue #2713):
6//
7// audio -> SoundOrchestrator { Silence | Disorganized | BpmLocked }
8// -> per-state Animartrix bank
9// -> per-state audio->visual mapping (energy, bass, kick, downbeat, ...)
10//
11// Time warp is preserved as a *secondary* effect inside the Disorganized state
12// (vibe.bass nudges engine speed within a bounded span). It is no longer the
13// primary audio interaction.
14
15// Use SPI-based WS2812 driver instead of RMT on ESP32
16#define FASTLED_ESP32_USE_CLOCKLESS_SPI
17
18// FastLED.h must be included first to trigger precompiled headers for FastLED's
19// build system
20#include "FastLED.h"
21
22#if defined(FL_IS_TEENSY)
23// Keep fbuild's library scanner aware of PJRC Audio sources for Teensy.
24#include <Audio.h>
25#endif
26
27#include "fl/math/math.h"
28#include "fl/math/screenmap.h"
29#include "fl/ui/ui.h"
33#include "fl/fx/fx2d_to_1d.h"
34#include "fl/fx/fx_engine.h"
35#include <FastLED.h>
36
37#include "auto_brightness.h"
38#include "sound_orchestrator.h"
39
40FASTLED_TITLE("AnimartrixRing");
41
42#ifndef TWO_PI
43#define TWO_PI \
44 6.2831853071795864769252867665590057683943387987502116419498891846156328125724179972560696506842341359
45#endif
46
47#define NUM_LEDS 244
48
49#ifndef PIN_DATA
50#define PIN_DATA 3 // ESP32C6 has this random pin available on the break out.
51#endif // PIN_DATA
52
53#define BRIGHTNESS 8
54
55// Grid dimensions for Animartrix sampling
56#define GRID_WIDTH 16
57#define GRID_HEIGHT 16
58
60
61// Animartrix 2D effect
62XYMap xymap = XYMap::constructRectangularGrid(GRID_WIDTH, GRID_HEIGHT);
64
65// ScreenMap for the ring - defines circular sampling positions using a lambda
67 fl::ScreenMap(NUM_LEDS, 0.15f, [](int index, fl::vec2f &pt_out) {
68 float centerX = GRID_WIDTH / 2.0f;
69 float centerY = GRID_HEIGHT / 2.0f;
70 float radius = fl::min(GRID_WIDTH, GRID_HEIGHT) / 2.0f - 1;
71 float angle = (TWO_PI * index) / NUM_LEDS;
72 pt_out.x = centerX + fl::cos(angle) * radius;
73 pt_out.y = centerY + fl::sin(angle) * radius;
74 });
75
76// Create the 2D-to-1D sampling effect
79
80// FxEngine for the 1D strip
82
83// UI controls
84fl::UISlider timeSpeed("Time Speed", 1, -10, 10, .1);
85fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255, 1);
86fl::UICheckbox autoBrightness("Auto Brightness", true);
87fl::UISlider autoBrightnessMax("Auto Brightness Max", 84, 0, 255, 1);
88fl::UISlider autoBrightnessLowThreshold("Auto Brightness Low Threshold", 8, 0,
89 100, 1);
90fl::UISlider autoBrightnessHighThreshold("Auto Brightness High Threshold", 22,
91 0, 100, 1);
92
93// Audio UI controls
94fl::UIAudio audio("Audio Input");
95fl::UICheckbox enableOrchestrator("Enable Sound Orchestrator", false);
96fl::UISlider orchestratorDwellMs("Orchestrator Min Dwell (ms)", 1500, 200, 5000, 100);
97fl::UISlider orchestratorHysteresisMs("Orchestrator Hysteresis (ms)", 400, 0, 2000, 50);
98
99// Processor + orchestrator (initialized in setup)
102bool gAutoPump = false;
103
104void setup() {
105 Serial.begin(115200);
106
107 // Setup LED strip
108 fl::ScreenMap screenMapLocal(screenmap);
109 screenMapLocal.setDiameter(
110 0.15); // 0.15 cm or 1.5mm - appropriate for dense 144 LED rope
111 FastLED.addLeds<WS2812, PIN_DATA>(leds, NUM_LEDS)
112 .setCorrection(TypicalLEDStrip)
113 .setScreenMap(screenMapLocal);
114 FastLED.setBrightness(brightness.value());
115
116 // Add the 2D-to-1D effect to FxEngine
117 fxEngine.addFx(fx2dTo1d);
118
119 // Route audio through FastLED.add() for auto-pump when available.
120 // gAutoPump may only be set true when FastLED.add() actually returned a
121 // live processor -- otherwise loop() would skip the manual pump path and
122 // the orchestrator would never see any samples.
123 auto input = audio.audioInput();
124 if (input) {
125 gAudioProcessor = FastLED.add(input);
126 if (gAudioProcessor) {
127 gAutoPump = true;
128 printf("AnimartrixRing: Audio routed via FastLED.add() (auto-pump)\n");
129 }
130 }
131 if (!gAudioProcessor) {
133 gAutoPump = false;
134 printf("AnimartrixRing: Audio using manual pump (fallback)\n");
135 }
136
137 // Build the 3-state orchestrator. It owns nothing: it just polls the
138 // Processor, asks the Animartrix to switch banks, and pokes the FxEngine
139 // speed on every frame.
142 gOrchestrator->begin();
143
144 // Log state transitions so a developer can see the classifier in action.
145 static animartrix_ring::SoundState sLastState =
147 (void)sLastState;
148
149 Serial.println("AnimartrixRing setup complete (3-state orchestrator)");
150}
151
152void loop() {
153 // Manual audio pump fallback (e.g. WASM / when FastLED.add() didn't take).
154 if (!gAutoPump) {
155 fl::audio::Sample sample = audio.next();
156 if (sample.isValid() && enableOrchestrator.value()) {
157 gAudioProcessor->update(sample);
158 }
159 }
160
161 // Per-frame orchestrator tick. When disabled, fall back to plain manual
162 // speed so the sketch still behaves like a non-audio Animartrix demo.
163 if (enableOrchestrator.value()) {
164 // Apply UI overrides cheaply on every tick.
166 cfg.minDwellMs = static_cast<fl::u32>(orchestratorDwellMs.value());
168 static_cast<fl::u32>(orchestratorHysteresisMs.value());
169 gOrchestrator->setConfig(cfg);
170
171 const fl::u32 now = millis();
172 gOrchestrator->tick(now, timeSpeed.value());
173
174 // Log state transitions.
175 static animartrix_ring::SoundState sLast =
177 if (gOrchestrator->state() != sLast) {
178 sLast = gOrchestrator->state();
179 printf("AnimartrixRing: state -> %s (engine speed=%.2f)\n",
181 gOrchestrator->lastEngineSpeed());
182 }
183 } else {
184 fxEngine.setSpeed(timeSpeed.value());
185 }
186
187 // Draw the effect
188 fxEngine.draw(millis(), leds);
189
190 // Calculate final brightness
191 uint8_t finalBrightness;
192 if (autoBrightness.value()) {
193 float avgBri = getAverageBrightness(leds, NUM_LEDS);
194 finalBrightness = applyBrightnessCompression(
195 avgBri, static_cast<uint8_t>(autoBrightnessMax.value()),
198 } else {
199 finalBrightness = static_cast<uint8_t>(brightness.value());
200 }
201
202 FastLED.setBrightness(finalBrightness);
203 FastLED.show();
204}
fl::UIAudio audio("Audio Input")
fl::FxEngine fxEngine(NUM_LEDS)
#define NUM_LEDS
fl::Animartrix animartrix(xyMap, FIRST_ANIMATION)
#define PIN_DATA
fl::CRGB leds[NUM_LEDS]
fl::UISlider timeSpeed("Time Speed", 1, -10, 10,.1)
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255)
#define BRIGHTNESS
fl::UIAudio audio("Audio Input")
fl::UISlider orchestratorHysteresisMs("Orchestrator Hysteresis (ms)", 400, 0, 2000, 50)
#define TWO_PI
fl::FxEngine fxEngine(NUM_LEDS)
fl::UICheckbox enableOrchestrator("Enable Sound Orchestrator", false)
void setup()
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255, 1)
XYMap xymap
#define GRID_WIDTH
fl::ScreenMap screenmap
#define GRID_HEIGHT
bool gAutoPump
fl::shared_ptr< animartrix_ring::SoundOrchestrator > gOrchestrator
fl::UISlider timeSpeed("Time Speed", 1, -10, 10,.1)
fl::UISlider autoBrightnessLowThreshold("Auto Brightness Low Threshold", 8, 0, 100, 1)
fl::UICheckbox autoBrightness("Auto Brightness", true)
fl::UISlider autoBrightnessHighThreshold("Auto Brightness High Threshold", 22, 0, 100, 1)
fl::shared_ptr< fl::audio::Processor > gAudioProcessor
fl::UISlider orchestratorDwellMs("Orchestrator Min Dwell (ms)", 1500, 200, 5000, 100)
fl::UISlider autoBrightnessMax("Auto Brightness Max", 84, 0, 255, 1)
FASTLED_TITLE("AnimartrixRing")
void loop()
auto fx2dTo1d
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
uint8_t applyBrightnessCompression(float inputBrightnessPercent, uint8_t maxBrightness, float lowThreshold, float highThreshold)
float getAverageBrightness(CRGB *leds, int numLeds)
@ BILINEAR
Bilinear interpolation (smooth)
Definition fx2d_to_1d.h:41
Manages and renders multiple visual effects (Fx) for LED strips.
Definition fx_engine.h:33
void setDiameter(float diameter) FL_NOEXCEPT
@ TypicalLEDStrip
Typical values for SMD5050 LEDs.
Definition color.h:15
fl::CRGB CRGB
Definition crgb.h:25
const char * toString(SoundState s)
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
Definition math.h:71
vec2< float > vec2f
Definition geometry.h:333
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
Definition shared_ptr.h:414
enable_if< is_fixed_point< T >::value, T >::type cos(T angle) FL_NOEXCEPT
enable_if< is_fixed_point< T >::value, T >::type sin(T angle) FL_NOEXCEPT
value_type y
Definition geometry.h:191
value_type x
Definition geometry.h:190
#define Serial
Definition serial.h:304
Aggregator header for the fl/ui/ family of per-element UI types.