FastLED 3.9.15
Loading...
Searching...
No Matches
simple.h
Go to the documentation of this file.
1
10
11/*
12This demo is best viewed using the FastLED compiler.
13
14Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
15
16Python
17
18Install: pip install fastled
19Run: fastled <this sketch directory>
20This will compile and preview the sketch in the browser, and enable
21all the UI elements you see below.
22*/
23
24#include <Arduino.h>
25#include <FastLED.h>
26
27#include "fl/audio/audio.h"
28#include "fl/gfx/downscale.h"
29#include "fl/gfx/draw_visitor.h"
30#include "fl/fft.h"
31#include "fl/math/math.h"
32#include "fl/math/math.h"
33#include "fl/gfx/raster.h"
34#include "fl/math/time_alpha.h"
35#include "fl/ui/ui.h"
36#include "fl/gfx/xypath.h"
38#include "fl/fx/time.h"
39#include "fl/stl/function.h"
40
41// Sketch.
42#include "fx_audio.h"
43
44#include "fl/stl/cstring.h"
45
46#define HEIGHT 128
47#define WIDTH 128
48#define NUM_LEDS ((WIDTH) * (HEIGHT))
49#define IS_SERPINTINE false
50#define TIME_ANIMATION 1000 // ms
51#define PIN_DATA 3
52
53fl::UITitle title("Simple control of an xy path");
54fl::UIDescription description("This is more of a test for new features.");
55fl::UICheckbox enableVolumeVis("Enable volume visualization", false);
56fl::UICheckbox enableRMS("Enable RMS visualization", false);
57fl::UICheckbox enableFFT("Enable FFT visualization", true);
58fl::UICheckbox enablePitchDetect("Enable pitch detection", false);
59fl::UICheckbox freeze("Freeze frame", false);
60fl::UIButton advanceFrame("Advance frame");
61fl::UISlider decayTimeSeconds("Fade time Seconds", .1, 0, 4, .02);
62fl::UISlider attackTimeSeconds("Attack time Seconds", .1, 0, 4, .02);
63fl::UISlider outputTimeSec("outputTimeSec", .17, 0, 2, .01);
64
66fl::UISlider fadeToBlack("Fade to black by", 5, 0, 20, 1);
67
68// Group related UI elements using UIGroup template multi-argument constructor
72
73fl::MaxFadeTracker audioFadeTracker(attackTimeSeconds.value(),
74 decayTimeSeconds.value(), outputTimeSec.value(),
75 44100);
76
79
80fl::CRGB leds[NUM_LEDS / 4]; // Downscaled buffer
82 IS_SERPINTINE); // Framebuffer is regular rectangle LED matrix.
83
84fl::audio::fft::Bins fftOut(WIDTH); // 2x width due to super sampling.
85
86// CRGB framebuffer[NUM_LEDS];
87// CRGB framebuffer[WIDTH_2X * HEIGHT_2X]; // 2x super sampling.
88// XYMap frameBufferXY(WIDTH, HEIGHT, IS_SERPINTINE); // LED output, serpentine
89// as is common for LED matrices. XYMap xyMap_2X(WIDTH_2X, HEIGHT_2X, false); //
90// Framebuffer is regular rectangle LED matrix.
91
92int x = 0;
93int y = 0;
94bool triggered = false;
95
97
98// Pitch detection engine
99fl::SoundToMIDI pitchConfig;
100fl::SoundToMIDIEngine* pitchEngine = nullptr;
101uint8_t currentMIDINote = 0;
102bool noteIsOn = false;
103
105 double sumSq = 0.0;
106 const int N = data.size();
107 for (int i = 0; i < N; ++i) {
108 int32_t x32 = int32_t(data[i]);
109 sumSq += x32 * x32;
110 }
111 float rms = fl::sqrt(float(sumSq) / N);
112 return rms;
113}
114
115void setup() {
116 Serial.begin(115200);
117 // auto screenmap = frameBufferXY.toScreenMap();
118 // screenmap.setDiameter(.2);
119 // FastLED.addLeds<NEOPIXEL, 2>(framebuffer,
120 // NUM_LEDS).setScreenMap(screenmap);
121 auto screenmap = ledsXY.toScreenMap();
122 screenmap.setDiameter(.2);
123
124 decayTimeSeconds.onChanged([](float value) {
125 audioFadeTracker.setDecayTime(value);
126 FL_WARN("Fade time seconds: " << value);
127 });
128 attackTimeSeconds.onChanged([](float value) {
129 audioFadeTracker.setAttackTime(value);
130 FL_WARN("Attack time seconds: " << value);
131 });
132 outputTimeSec.onChanged([](float value) {
133 audioFadeTracker.setOutputTime(value);
134 FL_WARN("Output time seconds: " << value);
135 });
136
137 // Initialize pitch detection
138 pitchConfig.sample_rate_hz = 44100.0f;
139 pitchEngine = new fl::SoundToMIDIEngine(pitchConfig); // ok bare allocation
140 pitchEngine->onNoteOn = [](uint8_t note, uint8_t velocity) {
141 currentMIDINote = note;
142 noteIsOn = true;
143 Serial.print("Note ON: ");
144 Serial.print(note);
145 Serial.print(" vel: ");
146 Serial.println(velocity);
147 };
148 pitchEngine->onNoteOff = [](uint8_t note) {
149 noteIsOn = false;
150 Serial.print("Note OFF: ");
151 Serial.println(note);
152 };
153
154 FastLED.addLeds<NEOPIXEL, PIN_DATA>(leds, ledsXY.getTotal())
155 .setScreenMap(screenmap);
156}
157
158void shiftUp() {
159 // fade each led by 1%
160 if (fadeToBlack.as_int()) {
161
162 for (int i = 0; i < NUM_LEDS; ++i) {
163 auto &c = framebuffer[i];
164 c.fadeToBlackBy(fadeToBlack.as_int());
165 }
166 }
167
168 for (int y = HEIGHT - 1; y > 0; --y) {
169 fl::CRGB* row1 = &framebuffer[frameBufferXY(0, y)];
170 fl::CRGB* row2 = &framebuffer[frameBufferXY(0, y - 1)];
171 fl::memcopy(row1, row2, WIDTH * sizeof(fl::CRGB));
172 }
173 fl::CRGB* row = &framebuffer[frameBufferXY(0, 0)];
174 fl::memset(row, 0, sizeof(fl::CRGB) * WIDTH);
175}
176
177
178bool doFrame() {
179 if (!freeze) {
180 return true;
181 }
182 if (advanceFrame.isPressed()) {
183 return true;
184 }
185 return false;
186}
187
188void loop() {
189 if (triggered) {
190 FL_WARN("Triggered");
191 }
192
193 // x = pointX.as_int();
194 y = HEIGHT / 2;
195
196 bool do_frame = doFrame();
197
198 while (fl::audio::Sample sample = audio.next()) {
199 if (!do_frame) {
200 continue;
201 }
202
203 // Process pitch detection if enabled
205 // Convert int16_t samples to float for pitch detection
206 static float floatBuffer[512];
207 size_t numSamples = fl::min(sample.pcm().size(), (size_t)512);
208 for (size_t i = 0; i < numSamples; i++) {
209 floatBuffer[i] = sample.pcm()[i] / 32768.0f;
210 }
211 pitchEngine->processFrame(floatBuffer, numSamples);
212 }
213
214 float fade = audioFadeTracker(sample.pcm().data(), sample.pcm().size());
215 shiftUp();
216 // FL_WARN("Audio sample size: " << sample.pcm().size());
217 soundLevelMeter.processBlock(sample.pcm());
218 // FL_WARN("")
219 auto dbfs = soundLevelMeter.getDBFS();
220 FASTLED_UNUSED(dbfs);
221 // FL_WARN("getDBFS: " << dbfs);
222 int32_t max = 0;
223 for (size_t i = 0; i < sample.pcm().size(); ++i) {
224 int32_t x = ABS(sample.pcm()[i]);
225 if (x > max) {
226 max = x;
227 }
228 }
229 float anim =
230 fl::map_range<float, float>(max, 0.0f, 32768.0f, 0.0f, 1.0f);
231 anim = fl::clamp(anim, 0.0f, 1.0f);
232
233 x = fl::map_range<float, float>(anim, 0.0f, 1.0f, 0.0f, WIDTH - 1);
234 // FL_WARN("x: " << x);
235
236 // fft.run(sample.pcm(), &fftOut);
237 sample.fft(&fftOut);
238
239 if (enableFFT) {
240 auto max_x = fftOut.raw().size() - 1;
241 FASTLED_UNUSED(max_x);
242 for (size_t i = 0; i < fftOut.raw().size(); ++i) {
243 auto x = i;
244 auto v = fftOut.db()[i];
245 // Map audio intensity to a position in the heat palette (0-255)
246 v = fl::map_range<float, float>(v, 45, 70, 0, 1.f);
247 v = fl::clamp(v, 0.0f, 1.0f);
248 uint8_t heatIndex =
249 fl::map_range<float, uint8_t>(v, 0, 1, 0, 255);
250
251 // FL_WARN(v);
252
253 // Use FastLED's built-in HeatColors palette
254 auto c = ColorFromPalette(HeatColors_p, heatIndex);
255 c.fadeToBlackBy(255 - heatIndex);
256 framebuffer[frameBufferXY(x, 0)] = c;
257 // FL_WARN("y: " << i << " b: " << b);
258 }
259 }
260
261 if (enableVolumeVis) {
262 framebuffer[frameBufferXY(x, HEIGHT / 2)] = fl::CRGB(0, 255, 0);
263 }
264
265 if (enableRMS) {
266 float rms = sample.rms();
267 FL_WARN("RMS: " << rms);
268 rms = fl::map_range<float, float>(rms, 0.0f, 32768.0f, 0.0f, 1.0f);
269 rms = fl::clamp(rms, 0.0f, 1.0f) * WIDTH;
270 framebuffer[frameBufferXY(rms, HEIGHT * 3 / 4)] = fl::CRGB(0, 0, 255);
271 }
272
273 // Display pitch detection result
275 // Map MIDI note to position (common range: 40-88)
276 float notePos = fl::map_range<float, float>(currentMIDINote, 40.0f, 88.0f, 0.0f, 1.0f);
277 notePos = fl::clamp(notePos, 0.0f, 1.0f);
278 uint16_t note_x = notePos * (WIDTH - 1);
279 uint16_t h = HEIGHT / 8;
280 // magenta color for pitch
281 framebuffer[frameBufferXY(note_x, h)] = fl::CRGB(255, 0, 255);
282 }
283
284 if (true) {
285 uint16_t fade_width = fade * (WIDTH - 1);
286 uint16_t h = HEIGHT / 4;
287 // yellow
288 int index = frameBufferXY(fade_width, h);
289 auto c = fl::CRGB(255, 255, 0);
290 framebuffer[index] = c;
291 }
292 }
293
294 // now downscale the framebuffer to the led matrix
295 downscale(framebuffer, frameBufferXY, leds, ledsXY);
296
297 FastLED.show();
298}
fl::UIAudio audio("Audio Input")
#define NUM_LEDS
fl::UIDescription description("Demo of the Animatrix effects. @author of fx is StefanPetrick")
#define PIN_DATA
fl::UITitle title("Animartrix")
fl::CRGB leds[NUM_LEDS]
fl::ScreenMap screenmap
fl::CRGB framebuffer[NUM_LEDS]
Definition simple.h:77
int y
Definition simple.h:93
fl::UIGroup audioProcessingControls("Audio Processing", decayTimeSeconds, attackTimeSeconds, outputTimeSec)
void shiftUp()
Definition simple.h:158
fl::MaxFadeTracker audioFadeTracker(attackTimeSeconds.value(), decayTimeSeconds.value(), outputTimeSec.value(), 44100)
fl::UIButton advanceFrame("Advance frame")
fl::audio::SoundLevelMeter soundLevelMeter(.0, 0.0)
fl::UISlider decayTimeSeconds("Fade time Seconds",.1, 0, 4,.02)
bool triggered
Definition simple.h:94
fl::UISlider outputTimeSec("outputTimeSec",.17, 0, 2,.01)
fl::audio::fft::Bins fftOut(WIDTH)
void setup()
Definition simple.h:115
float rms(fl::span< const int16_t > data)
Definition simple.h:104
fl::UICheckbox freeze("Freeze frame", false)
int x
Definition simple.h:92
fl::UIGroup generalControls("General Controls", freeze, advanceFrame, fadeToBlack)
fl::UISlider fadeToBlack("Fade to black by", 5, 0, 20, 1)
fl::XYMap frameBufferXY(WIDTH, HEIGHT, IS_SERPINTINE)
fl::UICheckbox enableRMS("Enable RMS visualization", false)
#define IS_SERPINTINE
Definition simple.h:49
fl::UICheckbox enableFFT("Enable FFT visualization", true)
fl::UISlider attackTimeSeconds("Attack time Seconds",.1, 0, 4,.02)
bool doFrame()
Definition simple.h:178
fl::UICheckbox enablePitchDetect("Enable pitch detection", false)
fl::XYMap ledsXY(WIDTH/2, HEIGHT/2, IS_SERPINTINE)
fl::UICheckbox enableVolumeVis("Enable volume visualization", false)
void loop()
Definition simple.h:188
fl::UIGroup visualizationControls("Visualization", enableVolumeVis, enableRMS, enableFFT, enablePitchDetect)
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
Definition math.h:75
#define WIDTH
fl::SoundToMIDI pitchConfig
Definition advanced.h:102
uint8_t currentMIDINote
Definition advanced.h:104
#define HEIGHT
bool noteIsOn
Definition advanced.h:106
fl::SoundToMIDIEngine * pitchEngine
Definition advanced.h:103
constexpr fl::size size() const FL_NOEXCEPT
Definition span.h:458
CRGB ColorFromPalette(const CRGBPalette16 &pal, fl::u8 index, fl::u8 brightness, TBlendType blendType)
const TProgmemRGBPalette16 HeatColors_p
Approximate "black body radiation" palette, akin to the FastLED HeatColor() function.
#define FL_WARN(X)
Definition log.h:276
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
Definition math.h:71
fl::CRGB CRGB
Definition video.h:15
FASTLED_FORCE_INLINE U map_range(T value, T in_min, T in_max, U out_min, U out_max) FL_NOEXCEPT
Definition math.h:174
constexpr enable_if< is_fixed_point< T >::value, T >::type sqrt(T x) FL_NOEXCEPT
void * memcopy(void *dest, const void *src, size_t n) FL_NOEXCEPT
Definition cstring.h:103
void * memset(void *s, int c, size_t n) FL_NOEXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type clamp(T x, T lo, T hi) FL_NOEXCEPT
#define FASTLED_UNUSED(x)
Representation of an 8-bit RGB pixel (Red, Green, Blue)
Definition crgb.h:38
#define Serial
Definition serial.h:304
Aggregator header for the fl/ui/ family of per-element UI types.