imported from "final" folder

This commit is contained in:
2025-11-28 12:12:50 +01:00
parent f9288986cf
commit ff8e725b35
1061 changed files with 225150 additions and 96 deletions

View File

@@ -0,0 +1,39 @@
/// @file Audio.ino
/// @brief Audio visualization example with XY mapping
/// @example Audio.ino
///
/// This sketch is fully compatible with the FastLED web compiler. To use it do the following:
/// 1. Install Fastled: `pip install fastled`
/// 2. cd into this examples page.
/// 3. Run the FastLED web compiler at root: `fastled`
/// 4. When the compiler is done a web page will open.
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
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.
*/
// #define SIMPLE_EXAMPLE
#include <FastLED.h>
#if !SKETCH_HAS_LOTS_OF_MEMORY
// Platform does not have enough memory
void setup() {}
void loop() {}
#else
#ifdef SIMPLE_EXAMPLE
#include "simple/simple.h"
#else
#include "advanced/advanced.h"
#endif
#endif

View File

@@ -0,0 +1,148 @@
# Audio Reactive Visualizations
This example demonstrates various audio-reactive visualization modes using FastLED. It processes real-time audio input and creates stunning visual effects synchronized to music.
## Features
### Visualization Modes
1. **Spectrum Bars** - Classic frequency spectrum analyzer with vertical bars
2. **Radial Spectrum** - Circular frequency visualization radiating from center
3. **Waveform** - Real-time audio waveform display
4. **VU Meter** - Traditional volume unit meter with RMS and peak levels
5. **Matrix Rain** - Audio-reactive digital rain effect
6. **Fire Effect** - Flame simulation that responds to audio intensity
7. **Plasma Wave** - Animated plasma patterns modulated by audio
### Audio Processing
- **Real-time FFT Analysis** - Frequency spectrum analysis
- **Beat Detection** - Automatic beat detection with adjustable sensitivity
- **Auto Gain Control** - Automatically adjusts to varying audio levels
- **Noise Floor Filtering** - Removes background noise
- **Peak Detection** - Tracks audio peaks with smoothing
### Visual Controls
- **7 Color Palettes** - Rainbow, Heat, Ocean, Forest, Party, Lava, Cloud
- **Brightness Control** - Adjustable LED brightness
- **Fade Speed** - Control trail/persistence effects
- **Mirror Mode** - Create symmetrical displays
- **Beat Flash** - Visual feedback on beat detection
## Hardware Requirements
- **Controller**: ESP32, Teensy, or other platform with sufficient memory
- **LED Matrix**: 100x100 pixels (10,000 LEDs total)
- **Memory**: Requires `SKETCH_HAS_LOTS_OF_MEMORY` (not suitable for Arduino UNO)
- **Audio Input**: Microphone or line-in audio source
## Wiring
```
LED Matrix:
- Data Pin: GPIO 3 (configurable via LED_PIN)
- Power: 5V (ensure adequate power supply for 10,000 LEDs!)
- Ground: Common ground with controller
Audio Input:
- Follow your platform's audio input configuration
```
## Configuration
### Display Settings
```cpp
#define WIDTH 100 // Matrix width
#define HEIGHT 100 // Matrix height
#define LED_PIN 3 // Data pin for LEDs
#define LED_TYPE WS2812B // LED chipset
#define COLOR_ORDER GRB // Color order
```
### Audio Settings
```cpp
#define SAMPLE_RATE 44100 // Audio sample rate
#define FFT_SIZE 512 // FFT size for frequency analysis
```
## UI Controls
### Master Controls
- **Enable Audio** - Toggle audio processing on/off
- **Visualization Mode** - Select from 7 different modes
### Audio Controls
- **Audio Gain** - Manual gain adjustment (0.1 - 5.0)
- **Noise Floor** - Background noise threshold (0.0 - 1.0)
- **Auto Gain** - Enable automatic gain control
### Visual Controls
- **Brightness** - LED brightness (0 - 255)
- **Fade Speed** - Trail effect speed (0 - 255)
- **Color Palette** - Choose color scheme
- **Mirror Mode** - Enable symmetrical display
### Beat Detection
- **Beat Detection** - Enable/disable beat detection
- **Beat Sensitivity** - Adjust detection threshold (0.5 - 3.0)
- **Beat Flash** - Enable visual flash on beats
## Usage
1. Upload the sketch to your controller
2. Connect your LED matrix
3. Provide audio input (microphone or line-in)
4. Use the web UI to control visualizations
5. Select different modes and adjust parameters in real-time
## Performance Tips
- This example requires significant processing power
- Reduce matrix size if experiencing lag
- Disable beat detection for lower CPU usage
- Use simpler visualization modes on slower processors
## Customization
### Adding New Visualizations
1. Add your mode name to the `visualMode` dropdown
2. Create a new `drawYourMode()` function
3. Add a case in the main switch statement
4. Implement your visualization logic
### Modifying Color Palettes
Edit the `getCurrentPalette()` function to add custom palettes:
```cpp
case 7: return YourCustomPalette_p;
```
### Adjusting Matrix Size
For different matrix sizes, modify:
```cpp
#define WIDTH your_width
#define HEIGHT your_height
```
## Memory Usage
This example uses approximately:
- 30KB for LED buffer (100x100 RGB)
- 2KB for FFT data
- 1KB for audio buffers
- Additional memory for effect buffers
## Troubleshooting
- **No visualization**: Check audio input and "Enable Audio" setting
- **Choppy animation**: Reduce matrix size or disable some features
- **No beat detection**: Increase beat sensitivity or check audio levels
- **Dim display**: Increase brightness or check power supply
- **Compilation error**: Ensure platform has sufficient memory
## Credits
This example demonstrates the audio processing capabilities of FastLED, including FFT analysis, beat detection, and various visualization techniques suitable for LED art installations, music visualizers, and interactive displays.

View File

@@ -0,0 +1,562 @@
/// @file AudioReactive.ino
/// @brief Audio reactive visualization with multiple modes
/// @example AudioReactive.ino
#include <Arduino.h>
#include <FastLED.h>
#include "fl/ui.h"
#include "fl/audio.h"
#include "fl/fft.h"
#include "fl/xymap.h"
#include "fl/math.h"
#include "fl/math_macros.h"
#include "fl/compiler_control.h"
// This is used by fastled because we have extremely strict compiler settings.
// Stock Arduino/Platformio does not need these.
FL_DISABLE_WARNING_PUSH
FL_DISABLE_WARNING(float-conversion)
FL_DISABLE_WARNING(sign-conversion)
using namespace fl;
// Display configuration
// For WebAssembly, use a smaller display to avoid memory issues
#ifdef __EMSCRIPTEN__
#define WIDTH 32
#define HEIGHT 32
#else
#define WIDTH 64
#define HEIGHT 64
#endif
#define NUM_LEDS (WIDTH * HEIGHT)
#define LED_PIN 3
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
// Audio configuration
#define SAMPLE_RATE 44100
#define FFT_SIZE 512
// UI Elements
UITitle title("Audio Reactive Visualizations");
UIDescription description("Real-time audio visualizations with beat detection and multiple modes");
// Master controls
UICheckbox enableAudio("Enable Audio", true);
UIDropdown visualMode("Visualization Mode",
{"Spectrum Bars", "Radial Spectrum", "Waveform", "VU Meter", "Matrix Rain", "Fire Effect", "Plasma Wave"});
// Audio controls
UISlider audioGain("Audio Gain", 1.0f, 0.1f, 5.0f, 0.1f);
UISlider noiseFloor("Noise Floor", 0.1f, 0.0f, 1.0f, 0.01f);
UICheckbox autoGain("Auto Gain", true);
// Visual controls
UISlider brightness("Brightness", 128, 0, 255, 1);
UISlider fadeSpeed("Fade Speed", 20, 0, 255, 1);
UIDropdown colorPalette("Color Palette",
{"Rainbow", "Heat", "Ocean", "Forest", "Party", "Lava", "Cloud"});
UICheckbox mirrorMode("Mirror Mode", false);
// Beat detection
UICheckbox beatDetect("Beat Detection", true);
UISlider beatSensitivity("Beat Sensitivity", 1.5f, 0.5f, 3.0f, 0.1f);
UICheckbox beatFlash("Beat Flash", true);
// Audio input
UIAudio audio("Audio Input");
// Global variables
CRGB leds[NUM_LEDS];
XYMap xyMap(WIDTH, HEIGHT, false);
SoundLevelMeter soundMeter(0.0, 0.0);
// Audio processing variables - keep these smaller for WebAssembly
static const int NUM_BANDS = 16; // Reduced from 32
float fftSmooth[NUM_BANDS] = {0};
float beatHistory[20] = {0}; // Reduced from 43
int beatHistoryIndex = 0;
float beatAverage = 0;
float beatVariance = 0;
uint32_t lastBeatTime = 0;
bool isBeat = false;
float autoGainValue = 1.0f;
float peakLevel = 0;
// Visual effect variables
uint8_t hue = 0;
// Remove large static arrays for WebAssembly
#ifndef __EMSCRIPTEN__
float plasma[WIDTH][HEIGHT] = {{0}};
uint8_t fireBuffer[WIDTH][HEIGHT] = {{0}};
#endif
// Get current color palette
CRGBPalette16 getCurrentPalette() {
switch(colorPalette.as_int()) {
case 0: return CRGBPalette16(RainbowColors_p);
case 1: return CRGBPalette16(HeatColors_p);
case 2: return CRGBPalette16(OceanColors_p);
case 3: return CRGBPalette16(ForestColors_p);
case 4: return CRGBPalette16(PartyColors_p);
case 5: return CRGBPalette16(LavaColors_p);
case 6: return CRGBPalette16(CloudColors_p);
default: return CRGBPalette16(RainbowColors_p);
}
}
// Beat detection algorithm
bool detectBeat(float energy) {
beatHistory[beatHistoryIndex] = energy;
beatHistoryIndex = (beatHistoryIndex + 1) % 20;
// Calculate average
beatAverage = 0;
for (int i = 0; i < 20; i++) {
beatAverage += beatHistory[i];
}
beatAverage /= 20.0f;
// Calculate variance
beatVariance = 0;
for (int i = 0; i < 20; i++) {
float diff = beatHistory[i] - beatAverage;
beatVariance += diff * diff;
}
beatVariance /= 20.0f;
// Detect beat
float threshold = beatAverage + (beatSensitivity.value() * sqrt(beatVariance));
uint32_t currentTime = millis();
if (energy > threshold && (currentTime - lastBeatTime) > 80) {
lastBeatTime = currentTime;
return true;
}
return false;
}
// Update auto gain
void updateAutoGain(float level) {
if (!autoGain) {
autoGainValue = 1.0f;
return;
}
static float targetLevel = 0.7f;
static float avgLevel = 0.0f;
avgLevel = avgLevel * 0.95f + level * 0.05f;
if (avgLevel > 0.01f) {
float gainAdjust = targetLevel / avgLevel;
gainAdjust = fl::clamp(gainAdjust, 0.5f, 2.0f);
autoGainValue = autoGainValue * 0.9f + gainAdjust * 0.1f;
}
}
// Clear display
void clearDisplay() {
if (fadeSpeed.as_int() == 0) {
fill_solid(leds, NUM_LEDS, CRGB::Black);
} else {
fadeToBlackBy(leds, NUM_LEDS, fadeSpeed.as_int());
}
}
// Visualization: Spectrum Bars
void drawSpectrumBars(FFTBins* fft, float /* peak */) {
clearDisplay();
CRGBPalette16 palette = getCurrentPalette();
int barWidth = WIDTH / NUM_BANDS;
for (size_t band = 0; band < NUM_BANDS && band < fft->bins_db.size(); band++) {
float magnitude = fft->bins_db[band];
// Apply noise floor
magnitude = magnitude / 100.0f; // Normalize from dB
magnitude = MAX(0.0f, magnitude - noiseFloor.value());
// Smooth the FFT
fftSmooth[band] = fftSmooth[band] * 0.8f + magnitude * 0.2f;
magnitude = fftSmooth[band];
// Apply gain
magnitude *= audioGain.value() * autoGainValue;
magnitude = fl::clamp(magnitude, 0.0f, 1.0f);
int barHeight = magnitude * HEIGHT;
int xStart = band * barWidth;
for (int x = 0; x < barWidth - 1; x++) {
for (int y = 0; y < barHeight; y++) {
uint8_t colorIndex = fl::map_range<float, uint8_t>(
float(y) / HEIGHT, 0, 1, 0, 255
);
CRGB color = ColorFromPalette(palette, colorIndex + hue);
int ledIndex = xyMap(xStart + x, y);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = color;
}
if (mirrorMode) {
int mirrorIndex = xyMap(WIDTH - 1 - (xStart + x), y);
if (mirrorIndex >= 0 && mirrorIndex < NUM_LEDS) {
leds[mirrorIndex] = color;
}
}
}
}
}
}
// Visualization: Radial Spectrum
void drawRadialSpectrum(FFTBins* fft, float /* peak */) {
clearDisplay();
CRGBPalette16 palette = getCurrentPalette();
int centerX = WIDTH / 2;
int centerY = HEIGHT / 2;
for (size_t angle = 0; angle < 360; angle += 6) { // Reduced resolution
size_t band = (angle / 6) % NUM_BANDS;
if (band >= fft->bins_db.size()) continue;
float magnitude = fft->bins_db[band] / 100.0f;
magnitude = MAX(0.0f, magnitude - noiseFloor.value());
magnitude *= audioGain.value() * autoGainValue;
magnitude = fl::clamp(magnitude, 0.0f, 1.0f);
int radius = magnitude * (MIN(WIDTH, HEIGHT) / 2);
for (int r = 0; r < radius; r++) {
int x = centerX + (r * cosf(angle * PI / 180.0f));
int y = centerY + (r * sinf(angle * PI / 180.0f));
if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) {
uint8_t colorIndex = fl::map_range<int, uint8_t>(r, 0, radius, 255, 0);
int ledIndex = xyMap(x, y);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = ColorFromPalette(palette, colorIndex + hue);
}
}
}
}
}
// Visualization: Logarithmic Waveform (prevents saturation)
void drawWaveform(const Slice<const int16_t>& pcm, float /* peak */) {
clearDisplay();
CRGBPalette16 palette = getCurrentPalette();
int samplesPerPixel = pcm.size() / WIDTH;
int centerY = HEIGHT / 2;
for (size_t x = 0; x < WIDTH; x++) {
size_t sampleIndex = x * samplesPerPixel;
if (sampleIndex >= pcm.size()) break;
// Get the raw sample value
float sample = float(pcm[sampleIndex]) / 32768.0f; // Normalize to -1.0 to 1.0
// Apply logarithmic scaling to prevent saturation
float absSample = fabsf(sample);
float logAmplitude = 0.0f;
if (absSample > 0.001f) { // Avoid log(0)
// Logarithmic compression: log10(1 + gain * sample)
float scaledSample = absSample * audioGain.value() * autoGainValue;
logAmplitude = log10f(1.0f + scaledSample * 9.0f) / log10f(10.0f); // Normalize to 0-1
}
// Apply smooth sensitivity curve
logAmplitude = powf(logAmplitude, 0.7f); // Gamma correction for better visual response
// Calculate amplitude in pixels
int amplitude = int(logAmplitude * (HEIGHT / 2));
amplitude = fl::clamp(amplitude, 0, HEIGHT / 2);
// Preserve the sign for proper waveform display
if (sample < 0) amplitude = -amplitude;
// Color mapping based on amplitude intensity
uint8_t colorIndex = fl::map_range<int, uint8_t>(abs(amplitude), 0, HEIGHT/2, 40, 255);
CRGB color = ColorFromPalette(palette, colorIndex + hue);
// Apply brightness scaling for low amplitudes
if (abs(amplitude) < HEIGHT / 4) {
color.fadeToBlackBy(128 - (abs(amplitude) * 512 / HEIGHT));
}
// Draw vertical line from center
if (amplitude == 0) {
// Draw center point for zero amplitude
int ledIndex = xyMap(x, centerY);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = color.fadeToBlackBy(200);
}
} else {
// Draw line from center to amplitude
int startY = (amplitude > 0) ? centerY : centerY + amplitude;
int endY = (amplitude > 0) ? centerY + amplitude : centerY;
for (int y = startY; y <= endY; y++) {
if (y >= 0 && y < HEIGHT) {
int ledIndex = xyMap(x, y);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
// Fade edges for smoother appearance
CRGB pixelColor = color;
if (y == startY || y == endY) {
pixelColor.fadeToBlackBy(100);
}
leds[ledIndex] = pixelColor;
}
}
}
}
}
}
// Visualization: VU Meter
void drawVUMeter(float rms, float peak) {
clearDisplay();
CRGBPalette16 palette = getCurrentPalette();
// RMS level bar
int rmsWidth = rms * WIDTH * audioGain.value() * autoGainValue;
rmsWidth = MIN(rmsWidth, WIDTH);
for (int x = 0; x < rmsWidth; x++) {
for (int y = HEIGHT/3; y < 2*HEIGHT/3; y++) {
uint8_t colorIndex = fl::map_range<int, uint8_t>(x, 0, WIDTH, 0, 255);
int ledIndex = xyMap(x, y);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = ColorFromPalette(palette, colorIndex);
}
}
}
// Peak indicator
int peakX = peak * WIDTH * audioGain.value() * autoGainValue;
peakX = MIN(peakX, WIDTH - 1);
for (int y = HEIGHT/4; y < 3*HEIGHT/4; y++) {
int ledIndex = xyMap(peakX, y);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = CRGB::White;
}
}
// Beat indicator
if (isBeat && beatFlash) {
for (int x = 0; x < WIDTH; x++) {
int ledIndex1 = xyMap(x, 0);
int ledIndex2 = xyMap(x, HEIGHT - 1);
if (ledIndex1 >= 0 && ledIndex1 < NUM_LEDS) leds[ledIndex1] = CRGB::White;
if (ledIndex2 >= 0 && ledIndex2 < NUM_LEDS) leds[ledIndex2] = CRGB::White;
}
}
}
// Visualization: Matrix Rain
void drawMatrixRain(float peak) {
// Shift everything down
for (int x = 0; x < WIDTH; x++) {
for (int y = HEIGHT - 1; y > 0; y--) {
int currentIndex = xyMap(x, y);
int aboveIndex = xyMap(x, y - 1);
if (currentIndex >= 0 && currentIndex < NUM_LEDS &&
aboveIndex >= 0 && aboveIndex < NUM_LEDS) {
leds[currentIndex] = leds[aboveIndex];
leds[currentIndex].fadeToBlackBy(40);
}
}
}
// Add new drops based on audio
int numDrops = peak * WIDTH * audioGain.value() * autoGainValue;
for (int i = 0; i < numDrops; i++) {
int x = random(WIDTH);
int ledIndex = xyMap(x, 0);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = CHSV(96, 255, 255); // Green
}
}
}
// Visualization: Fire Effect (simplified for WebAssembly)
void drawFireEffect(float peak) {
// Simple fire effect without buffer
clearDisplay();
// Add heat at bottom based on audio
int heat = 100 + (peak * 155 * audioGain.value() * autoGainValue);
heat = MIN(heat, 255);
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
// Simple gradient from bottom to top
int heatLevel = heat * (HEIGHT - y) / HEIGHT;
heatLevel = heatLevel * random(80, 120) / 100; // Add randomness
heatLevel = MIN(heatLevel, 255);
int ledIndex = xyMap(x, y);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = HeatColor(heatLevel);
}
}
}
}
// Visualization: Plasma Wave
void drawPlasmaWave(float peak) {
static float time = 0;
time += 0.05f + (peak * 0.2f);
CRGBPalette16 palette = getCurrentPalette();
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
float value = sinf(x * 0.1f + time) +
sinf(y * 0.1f - time) +
sinf((x + y) * 0.1f + time) +
sinf(sqrtf(x * x + y * y) * 0.1f - time);
value = (value + 4) / 8; // Normalize to 0-1
value *= audioGain.value() * autoGainValue;
uint8_t colorIndex = value * 255;
int ledIndex = xyMap(x, y);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = ColorFromPalette(palette, colorIndex + hue);
}
}
}
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Audio Reactive Visualizations");
Serial.println("Initializing...");
Serial.print("Display size: ");
Serial.print(WIDTH);
Serial.print("x");
Serial.println(HEIGHT);
// Initialize LEDs
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(brightness.as_int());
FastLED.clear();
FastLED.show();
// Set up UI callbacks
brightness.onChanged([](float value) {
FastLED.setBrightness(value);
});
Serial.println("Setup complete!");
}
void loop() {
// Check if audio is enabled
if (!enableAudio) {
// Show a simple test pattern
fill_rainbow(leds, NUM_LEDS, hue++, 7);
FastLED.show();
delay(20);
return;
}
// Process only one audio sample per frame to avoid accumulation
AudioSample sample = audio.next();
if (sample.isValid()) {
// Update sound meter
soundMeter.processBlock(sample.pcm());
// Get audio levels
float rms = sample.rms() / 32768.0f;
// Calculate peak
int32_t maxSample = 0;
for (size_t i = 0; i < sample.pcm().size(); i++) {
int32_t absSample = fabsf(sample.pcm()[i]);
if (absSample > maxSample) {
maxSample = absSample;
}
}
float peak = float(maxSample) / 32768.0f;
peakLevel = peakLevel * 0.9f + peak * 0.1f; // Smooth peak
// Update auto gain
updateAutoGain(rms);
// Beat detection
if (beatDetect) {
isBeat = detectBeat(peak);
}
// Get FFT data - create local FFTBins to avoid accumulation
FFTBins fftBins(NUM_BANDS);
sample.fft(&fftBins);
// Update color animation
hue += 1;
// Apply beat flash
if (isBeat && beatFlash) {
for (int i = 0; i < NUM_LEDS; i++) {
leds[i].fadeLightBy(-50); // Make brighter
}
}
// Draw selected visualization
switch (visualMode.as_int()) {
case 0: // Spectrum Bars
drawSpectrumBars(&fftBins, peakLevel);
break;
case 1: // Radial Spectrum
drawRadialSpectrum(&fftBins, peakLevel);
break;
case 2: // Waveform
drawWaveform(sample.pcm(), peakLevel);
break;
case 3: // VU Meter
drawVUMeter(rms, peakLevel);
break;
case 4: // Matrix Rain
drawMatrixRain(peakLevel);
break;
case 5: // Fire Effect
drawFireEffect(peakLevel);
break;
case 6: // Plasma Wave
drawPlasmaWave(peakLevel);
break;
}
}
FastLED.show();
// Add a small delay to prevent tight loops in WebAssembly
#ifdef __EMSCRIPTEN__
delay(1);
#endif
}
FL_DISABLE_WARNING_POP

View File

@@ -0,0 +1,157 @@
# Audio Reactive Visualization Example
This example demonstrates advanced audio reactive visualization capabilities using FastLED. It processes real-time audio input and creates stunning visual effects synchronized to music.
## Features
### Visualization Modes
1. **Spectrum Analyzer** - Classic frequency spectrum display with customizable colors
2. **Waveform** - Real-time audio waveform visualization
3. **VU Meter** - Traditional volume unit meter with RMS and peak indicators
4. **Spectrogram** - Scrolling frequency analysis over time
5. **Combined** - Split-screen showing both spectrum and waveform
6. **Reactive Patterns** - Dynamic patterns that respond to audio energy and beats
### Audio Processing
- **Real-time FFT** - Fast Fourier Transform for frequency analysis
- **Beat Detection** - Automatic beat detection with adjustable sensitivity
- **Auto Gain Control (AGC)** - Automatically adjusts to varying audio levels
- **Noise Floor Filtering** - Removes background noise for cleaner visuals
- **Attack/Decay/Sustain** - Professional audio envelope controls
### Visual Controls
- **Multiple Color Palettes** - Heat, Rainbow, Ocean, Forest, Lava, Cloud, Party
- **Mirror Mode** - Creates symmetrical displays
- **Brightness Control** - Adjustable LED brightness
- **Fade Effects** - Smooth transitions with adjustable fade time
- **Color Animation** - Animated color cycling with speed control
- **Smoothing** - Optional smoothing for less jittery displays
### Advanced Features
- **Frequency Band Analysis** - 8-band frequency analyzer for detailed audio analysis
- **FFT Smoothing** - Temporal smoothing of frequency data
- **Logarithmic Scale** - Optional log scale for frequency display
- **Freeze Frame** - Pause the visualization at any moment
- **Frame Advance** - Step through frozen frames
## UI Controls
### Main Controls
- **Enable Audio Reactive Mode** - Master on/off switch for audio processing
- **Visualization Mode** - Dropdown to select visualization type
### Audio Processing Group
- **Fade Time** - How quickly levels decay (0-4 seconds)
- **Attack Time** - How quickly levels rise (0-4 seconds)
- **Output Smoothing** - Final output smoothing (0-2 seconds)
- **Audio Gain** - Manual gain adjustment (0.1-5.0)
- **Noise Floor** - Background noise threshold (-80 to -20 dB)
### Visual Controls Group
- **Fade to Black** - Trail/persistence effect (0-50)
- **Brightness** - LED brightness (0-255)
- **Color Speed** - Animation speed (0.1-5.0)
- **Color Palette** - Choose from 7 palettes
- **Mirror Mode** - Enable symmetrical display
- **Smoothing** - Enable temporal smoothing
### FFT Controls Group
- **Min Frequency** - Lower frequency bound (20-1000 Hz)
- **Max Frequency** - Upper frequency bound (1000-20000 Hz)
- **Logarithmic Scale** - Use log scale for frequency
- **FFT Smoothing** - Smoothing factor (0-0.95)
### Advanced Controls Group
- **Freeze Frame** - Pause visualization
- **Advance Frame** - Step forward when frozen
- **Beat Detection** - Enable beat detection
- **Beat Sensitivity** - Beat detection threshold (0.5-3.0)
- **Auto Gain Control** - Enable automatic gain adjustment
## Hardware Setup
### LED Configuration
- Default: 128x128 LED matrix (16,384 LEDs)
- Downscaled to 64x64 for output (4,096 LEDs)
- Data pin: GPIO 3 (configurable)
- LED type: WS2812B (Neopixel)
### Audio Input
The example uses the FastLED audio system which can accept input from:
- Microphone (real-time audio capture)
- Audio file playback
- System audio (on supported platforms)
## Usage
1. **Basic Operation**
- Upload the sketch to your controller
- Connect your LED matrix
- Provide audio input
- Use the web UI to control visualization
2. **Optimizing for Your Setup**
- Adjust the noise floor if visualization is too sensitive/insensitive
- Use AGC for varying audio levels
- Tune beat sensitivity for your music style
- Experiment with different color palettes and speeds
3. **Performance Tips**
- Reduce matrix size for slower controllers
- Disable smoothing for more responsive display
- Use simpler visualization modes for lower CPU usage
## Code Structure
### Main Components
1. **Audio Processing Pipeline**
```cpp
AudioSample → FFT → Band Analysis → Beat Detection → Visualization
```
2. **Visualization Functions**
- `drawSpectrumAnalyzer()` - Frequency spectrum bars
- `drawWaveform()` - Audio waveform display
- `drawVUMeter()` - Volume meter visualization
- `drawSpectrogram()` - Time-frequency plot
- `drawReactivePatterns()` - Beat-reactive patterns
3. **Audio Analysis Classes**
- `MaxFadeTracker` - Smooth peak tracking with attack/decay
- `BeatDetector` - Energy-based beat detection
- `FrequencyBandAnalyzer` - 8-band frequency analysis
## Customization
### Adding New Visualizations
1. Create a new draw function
2. Add it to the visualization mode dropdown
3. Add a case in the main switch statement
### Modifying Color Palettes
Edit the `getCurrentPalette()` function to add custom palettes.
### Adjusting Frequency Bands
Modify the `FrequencyBandAnalyzer` constructor to change band boundaries.
## Troubleshooting
- **No visualization**: Check audio input and ensure "Enable Audio Reactive Mode" is on
- **Too dim/bright**: Adjust brightness control
- **Choppy animation**: Increase smoothing or reduce matrix size
- **No beat detection**: Adjust beat sensitivity or check audio levels
- **Visualization too sensitive**: Increase noise floor value
## Memory Requirements
This example requires significant memory for:
- Framebuffer: 128×128×3 = 49,152 bytes
- LED buffer: 64×64×3 = 12,288 bytes
- Audio buffers and FFT data
Platforms with limited memory may need to reduce the matrix size.

View File

@@ -0,0 +1,64 @@
#pragma once
#include "fl/time_alpha.h"
#include "fl/math_macros.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 = ABS(samples[i]) * (1.0f/32768.0f);
peak = 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 - fl::exp(-attackRate_ * dt);
currentLevel_ += (peak - currentLevel_) * riseFactor;
} else {
float decayFactor = fl::exp(-decayRate_ * dt);
currentLevel_ *= decayFactor;
}
// 4) output inertia: smooth smoothedOutput_ → currentLevel_
float outFactor = 1.0f - fl::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
};

View File

@@ -0,0 +1,253 @@
/// @file Audio.ino
/// @brief Audio visualization example with XY mapping
/// @example Audio.ino
///
/// This sketch is fully compatible with the FastLED web compiler. To use it do the following:
/// 1. Install Fastled: `pip install fastled`
/// 2. cd into this examples page.
/// 3. Run the FastLED web compiler at root: `fastled`
/// 4. When the compiler is done a web page will open.
/*
This demo is best viewed using the FastLED compiler.
Windows/MacOS binaries: https://github.com/FastLED/FastLED/releases
Python
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 "fl/unused.h"
#include "fx/time.h"
#include "fl/function.h"
// Sketch.
#include "fx_audio.h"
#include "fl/memfill.h"
using namespace fl;
#define HEIGHT 128
#define WIDTH 128
#define NUM_LEDS ((WIDTH) * (HEIGHT))
#define IS_SERPINTINE false
#define TIME_ANIMATION 1000 // ms
#define PIN_DATA 3
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);
// Group related UI elements using UIGroup template multi-argument constructor
UIGroup visualizationControls("Visualization", enableVolumeVis, enableRMS, enableFFT);
UIGroup audioProcessingControls("Audio Processing", decayTimeSeconds, attackTimeSeconds, outputTimeSec);
UIGroup generalControls("General Controls", freeze, advanceFrame, fadeToBlack);
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, PIN_DATA>(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)];
fl::memfill(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");
}
// 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_UNUSED(dbfs);
// FASTLED_WARN("getDBFS: " << dbfs);
int32_t max = 0;
for (size_t 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;
FASTLED_UNUSED(max_x);
for (size_t 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();
}