first commit

This commit is contained in:
stuce-bot 2025-06-30 20:47:33 +02:00
commit 5893b00dd2
1669 changed files with 1982740 additions and 0 deletions

View file

@ -0,0 +1,234 @@
/*
This demo is best viewed using the FastLED compiler.
Install: pip install fastled
Run: fastled <this sketch directory>
This will compile and preview the sketch in the browser, and enable
all the UI elements you see below.
*/
#include <Arduino.h>
#include <FastLED.h>
#include "fl/audio.h"
#include "fl/downscale.h"
#include "fl/draw_visitor.h"
#include "fl/fft.h"
#include "fl/math.h"
#include "fl/math_macros.h"
#include "fl/raster.h"
#include "fl/time_alpha.h"
#include "fl/ui.h"
#include "fl/xypath.h"
#include "fx.h"
#include "fx/time.h"
// Sketch.
#include "fl/function.h"
using namespace fl;
#define HEIGHT 128
#define WIDTH 128
#define NUM_LEDS ((WIDTH) * (HEIGHT))
#define IS_SERPINTINE false
#define TIME_ANIMATION 1000 // ms
UITitle title("Simple control of an xy path");
UIDescription description("This is more of a test for new features.");
UICheckbox enableVolumeVis("Enable volume visualization", false);
UICheckbox enableRMS("Enable RMS visualization", false);
UICheckbox enableFFT("Enable FFT visualization", true);
UICheckbox freeze("Freeze frame", false);
UIButton advanceFrame("Advance frame");
UISlider decayTimeSeconds("Fade time Seconds", .1, 0, 4, .02);
UISlider attackTimeSeconds("Attack time Seconds", .1, 0, 4, .02);
UISlider outputTimeSec("outputTimeSec", .17, 0, 2, .01);
UIAudio audio("Audio");
UISlider fadeToBlack("Fade to black by", 5, 0, 20, 1);
MaxFadeTracker audioFadeTracker(attackTimeSeconds.value(),
decayTimeSeconds.value(), outputTimeSec.value(),
44100);
CRGB framebuffer[NUM_LEDS];
XYMap frameBufferXY(WIDTH, HEIGHT, IS_SERPINTINE);
CRGB leds[NUM_LEDS / 4]; // Downscaled buffer
XYMap ledsXY(WIDTH / 2, HEIGHT / 2,
IS_SERPINTINE); // Framebuffer is regular rectangle LED matrix.
FFTBins fftOut(WIDTH); // 2x width due to super sampling.
// CRGB framebuffer[NUM_LEDS];
// CRGB framebuffer[WIDTH_2X * HEIGHT_2X]; // 2x super sampling.
// XYMap frameBufferXY(WIDTH, HEIGHT, IS_SERPINTINE); // LED output, serpentine
// as is common for LED matrices. XYMap xyMap_2X(WIDTH_2X, HEIGHT_2X, false); //
// Framebuffer is regular rectangle LED matrix.
int x = 0;
int y = 0;
bool triggered = false;
SoundLevelMeter soundLevelMeter(.0, 0.0);
float rms(Slice<const int16_t> data) {
double sumSq = 0.0;
const int N = data.size();
for (int i = 0; i < N; ++i) {
int32_t x32 = int32_t(data[i]);
sumSq += x32 * x32;
}
float rms = sqrt(float(sumSq) / N);
return rms;
}
void setup() {
Serial.begin(115200);
// auto screenmap = frameBufferXY.toScreenMap();
// screenmap.setDiameter(.2);
// FastLED.addLeds<NEOPIXEL, 2>(framebuffer,
// NUM_LEDS).setScreenMap(screenmap);
auto screenmap = ledsXY.toScreenMap();
screenmap.setDiameter(.2);
decayTimeSeconds.onChanged([](float value) {
audioFadeTracker.setDecayTime(value);
FASTLED_WARN("Fade time seconds: " << value);
});
attackTimeSeconds.onChanged([](float value) {
audioFadeTracker.setAttackTime(value);
FASTLED_WARN("Attack time seconds: " << value);
});
outputTimeSec.onChanged([](float value) {
audioFadeTracker.setOutputTime(value);
FASTLED_WARN("Output time seconds: " << value);
});
FastLED.addLeds<NEOPIXEL, 2>(leds, ledsXY.getTotal())
.setScreenMap(screenmap);
}
void shiftUp() {
// fade each led by 1%
if (fadeToBlack.as_int()) {
for (int i = 0; i < NUM_LEDS; ++i) {
auto &c = framebuffer[i];
c.fadeToBlackBy(fadeToBlack.as_int());
}
}
for (int y = HEIGHT - 1; y > 0; --y) {
CRGB* row1 = &framebuffer[frameBufferXY(0, y)];
CRGB* row2 = &framebuffer[frameBufferXY(0, y - 1)];
memcpy(row1, row2, WIDTH * sizeof(CRGB));
}
CRGB* row = &framebuffer[frameBufferXY(0, 0)];
memset(row, 0, sizeof(CRGB) * WIDTH);
}
bool doFrame() {
if (!freeze) {
return true;
}
if (advanceFrame.isPressed()) {
return true;
}
return false;
}
void loop() {
if (triggered) {
FASTLED_WARN("Triggered");
}
// fl::clear(framebuffer);
// fl::clear(framebuffer);
static uint32_t frame = 0;
// x = pointX.as_int();
y = HEIGHT / 2;
bool do_frame = doFrame();
while (AudioSample sample = audio.next()) {
if (!do_frame) {
continue;
}
float fade = audioFadeTracker(sample.pcm().data(), sample.pcm().size());
shiftUp();
// FASTLED_WARN("Audio sample size: " << sample.pcm().size());
soundLevelMeter.processBlock(sample.pcm());
// FASTLED_WARN("")
auto dbfs = soundLevelMeter.getDBFS();
// FASTLED_WARN("getDBFS: " << dbfs);
int32_t max = 0;
for (int i = 0; i < sample.pcm().size(); ++i) {
int32_t x = ABS(sample.pcm()[i]);
if (x > max) {
max = x;
}
}
float anim =
fl::map_range<float, float>(max, 0.0f, 32768.0f, 0.0f, 1.0f);
anim = fl::clamp(anim, 0.0f, 1.0f);
x = fl::map_range<float, float>(anim, 0.0f, 1.0f, 0.0f, WIDTH - 1);
// FASTLED_WARN("x: " << x);
// fft.run(sample.pcm(), &fftOut);
sample.fft(&fftOut);
// FASTLED_ASSERT(fftOut.bins_raw.size() == WIDTH_2X,
// "FFT bins size mismatch");
if (enableFFT) {
auto max_x = fftOut.bins_raw.size() - 1;
for (int i = 0; i < fftOut.bins_raw.size(); ++i) {
auto x = i;
auto v = fftOut.bins_db[i];
// Map audio intensity to a position in the heat palette (0-255)
v = fl::map_range<float, float>(v, 45, 70, 0, 1.f);
v = fl::clamp(v, 0.0f, 1.0f);
uint8_t heatIndex =
fl::map_range<float, uint8_t>(v, 0, 1, 0, 255);
// FASTLED_WARN(v);
// Use FastLED's built-in HeatColors palette
auto c = ColorFromPalette(HeatColors_p, heatIndex);
c.fadeToBlackBy(255 - heatIndex);
framebuffer[frameBufferXY(x, 0)] = c;
// FASTLED_WARN("y: " << i << " b: " << b);
}
}
if (enableVolumeVis) {
framebuffer[frameBufferXY(x, HEIGHT / 2)] = CRGB(0, 255, 0);
}
if (enableRMS) {
float rms = sample.rms();
FASTLED_WARN("RMS: " << rms);
rms = fl::map_range<float, float>(rms, 0.0f, 32768.0f, 0.0f, 1.0f);
rms = fl::clamp(rms, 0.0f, 1.0f) * WIDTH;
framebuffer[frameBufferXY(rms, HEIGHT * 3 / 4)] = CRGB(0, 0, 255);
}
if (true) {
uint16_t fade_width = fade * (WIDTH - 1);
uint16_t h = HEIGHT / 4;
// yellow
int index = frameBufferXY(fade_width, h);
auto c = CRGB(255, 255, 0);
framebuffer[index] = c;
}
}
// now downscale the framebuffer to the led matrix
downscale(framebuffer, frameBufferXY, leds, ledsXY);
FastLED.show();
}

View file

@ -0,0 +1,67 @@
#include <algorithm>
#include <cstdint>
#include <cmath>
#include <cassert>
#include "fl/time_alpha.h"
/// Tracks a smoothed peak with attack, decay, and output-inertia time-constants.
class MaxFadeTracker {
public:
/// @param attackTimeSec τ₁: how quickly to rise toward a new peak.
/// @param decayTimeSec τ₂: how quickly to decay to 1/e of value.
/// @param outputTimeSec τ₃: how quickly the returned value follows currentLevel_.
/// @param sampleRate audio sample rate (e.g. 44100 or 48000).
MaxFadeTracker(float attackTimeSec,
float decayTimeSec,
float outputTimeSec,
float sampleRate)
: attackRate_(1.0f / attackTimeSec)
, decayRate_(1.0f / decayTimeSec)
, outputRate_(1.0f / outputTimeSec)
, sampleRate_(sampleRate)
, currentLevel_(0.0f)
, smoothedOutput_(0.0f)
{}
void setAttackTime(float t){ attackRate_ = 1.0f/t; }
void setDecayTime (float t){ decayRate_ = 1.0f/t; }
void setOutputTime(float t){ outputRate_ = 1.0f/t; }
/// Process one 512-sample block; returns [0…1] with inertia.
float operator()(const int16_t* samples, size_t length) {
assert(length == 512);
// 1) block peak
float peak = 0.0f;
for (size_t i = 0; i < length; ++i) {
float v = std::abs(samples[i]) * (1.0f/32768.0f);
peak = std::max(peak, v);
}
// 2) time delta
float dt = static_cast<float>(length) / sampleRate_;
// 3) update currentLevel_ with attack/decay
if (peak > currentLevel_) {
float riseFactor = 1.0f - std::exp(-attackRate_ * dt);
currentLevel_ += (peak - currentLevel_) * riseFactor;
} else {
float decayFactor = std::exp(-decayRate_ * dt);
currentLevel_ *= decayFactor;
}
// 4) output inertia: smooth smoothedOutput_ → currentLevel_
float outFactor = 1.0f - std::exp(-outputRate_ * dt);
smoothedOutput_ += (currentLevel_ - smoothedOutput_) * outFactor;
return smoothedOutput_;
}
private:
float attackRate_; // = 1/τ₁
float decayRate_; // = 1/τ₂
float outputRate_; // = 1/τ₃
float sampleRate_;
float currentLevel_; // instantaneous peak with attack/decay
float smoothedOutput_; // returned value with inertia
};