imported from "final" folder
This commit is contained in:
39
.pio/libdeps/esp01_1m/FastLED/examples/Audio/Audio.ino
Normal file
39
.pio/libdeps/esp01_1m/FastLED/examples/Audio/Audio.ino
Normal 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
|
||||
148
.pio/libdeps/esp01_1m/FastLED/examples/Audio/advanced/README.md
Normal file
148
.pio/libdeps/esp01_1m/FastLED/examples/Audio/advanced/README.md
Normal 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.
|
||||
562
.pio/libdeps/esp01_1m/FastLED/examples/Audio/advanced/advanced.h
Normal file
562
.pio/libdeps/esp01_1m/FastLED/examples/Audio/advanced/advanced.h
Normal 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
|
||||
157
.pio/libdeps/esp01_1m/FastLED/examples/Audio/simple/README.md
Normal file
157
.pio/libdeps/esp01_1m/FastLED/examples/Audio/simple/README.md
Normal 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.
|
||||
@@ -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
|
||||
};
|
||||
253
.pio/libdeps/esp01_1m/FastLED/examples/Audio/simple/simple.h
Normal file
253
.pio/libdeps/esp01_1m/FastLED/examples/Audio/simple/simple.h
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user