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,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