#include <math.h>
#include "FastLED.h"
const byte pinData = 6;
const int timeIntervalSampling = 30; // Sample window width (50 ms would be 20Hz)
const int levelShift = 512;
const byte ledCount = 24;
const byte ledBrightness = 255;
struct CRGB ledRing[ledCount];
struct CRGB ledGradient[ledCount];
CHSV gradientHueStart = CHSV(96, 255, 255);
CHSV grandientHueEnd = CHSV(0, 255, 255);
//int sampleCounter;
int audioInRaw;
int audioInRectified;
float audioFiltered;
//float audioRMSPrecalc;
//float audioRMS;
const int intervalPeakHold = 600; // Peak hold interval in milliseconds
const byte intervalPeakDecay = 20; // Peak decay interval in milliseconds
int newPeak; // New peak value (= number of LEDs from 0 to peak)
int previousPeak;
unsigned long timeOfPeak; // Timestamp that updates with each new peak
bool peakDecay = false; // Toggles if the peak LED can decay or not
byte peakBrightness;
void setup()
{
Serial.begin(115200);
FastLED.addLeds<NEOPIXEL, pinData>(ledRing, ledCount);
FastLED.setBrightness(ledBrightness);
fill_gradient(ledGradient, 0, gradientHueStart, ledCount, grandientHueEnd, SHORTEST_HUES);
}
void loop()
{
unsigned long timeSamplingStart = millis();
uint16_t audioPeakToPeak = 0;
uint16_t audioMin = 1023;
uint16_t audioMax = 0;
while (millis() - timeSamplingStart < timeIntervalSampling)
{
audioInRaw = analogRead(0);
audioInRectified = abs(audioInRaw - levelShift);
audioMin = min(audioInRectified, audioMin);
audioMax = max(audioInRectified, audioMax);
// Only for dB(SPL) calculation
//audioRMSPrecalc += ((long) audioInRectified * audioInRectified); // Square each value and add them
//sampleCounter ++;
}
//Serial.println(audioInRaw);
//Serial.println(audioInRectified);
//Serial.print(audioMin);
//Serial.println(audioMax);
// Only for dB(SPL) calculation
//audioRMSPrecalc /= sampleCounter; // Divide by number of samples taken
//audioRMS = sqrt(audioRMSPrecalc); // Take the square root
//Serial.print("audioRMS:"); Serial.println(audioRMS);
//sampleCounter = 0;
audioPeakToPeak = audioMax - audioMin;
if (audioPeakToPeak <= 7) // Push down to silence level
audioPeakToPeak = 0;
//Serial.print(audioPeakToPeak);
audioFiltered = 0.8 * audioFiltered + (1 - 0.8) * audioPeakToPeak; // Leaky integrator, fast attack and slow decay
//Serial.print(audioFiltered);
// 10 and 150 are nasty magic numbers, this needs auto-range code later
newPeak = constrain(map(audioFiltered, 10, 150, 0, ledCount), 0, ledCount) - 1;
//Serial.print(newPeak);
// Peak hold and decay algorithm
if (newPeak >= previousPeak)
{
previousPeak = newPeak;
timeOfPeak = millis();
peakDecay = false;
}
else if (!peakDecay && (millis() - timeOfPeak > intervalPeakHold))
{
peakDecay = true;
timeOfPeak += intervalPeakHold - intervalPeakDecay;
peakBrightness = 255;
}
else if (peakDecay && (millis() - timeOfPeak > intervalPeakDecay))
{
if (previousPeak >= 0)
{
previousPeak--;
timeOfPeak += intervalPeakDecay;
}
}
FastLED.clear();
for ( byte i = 0; i <= newPeak; i++)
{
ledRing[i] = ledGradient[i];
}
ledRing[previousPeak] = CHSV(0, 255, peakBrightness);
FastLED.show();
}