-
Notifications
You must be signed in to change notification settings - Fork 0
/
SoundReactive.h
131 lines (105 loc) · 5.12 KB
/
SoundReactive.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <FastLED.h>
#define DC_OFFSET 0 // DC offset in mic signal - if unusure, leave 0
// I calculated this value by serialprintln lots of mic values
#define NOISE 10 // Noise/hum/interference in mic signal and increased value until it went quiet
#define SAMPLES 60 // Length of buffer for dynamic level adjustment
#define TOP (MAX_SINGLE_STRIP_LENGTH + 2) // Allow dot to go slightly off scale
#define PEAK_FALL 4 // Rate of peak falling dot
byte
peak = 0, // Used for falling dot
dotCount = 0, // Frame counter for delaying dot-falling speed
volCount = 0; // Frame counter for storing past volume data
int
vol[SAMPLES], // Collection of prior volume samples
lvl = 10, // Current "dampened" audio level
minLvlAvg = 0, // For dynamic adjustment of graph low & high
maxLvlAvg = 512;
int centerPoint = 15;
void fadeLed(CRGB *leds, uint8_t i);
unsigned long lastHighSignal = millis();
unsigned long lastTwinkle = 0;
uint8_t soundAnimate(uint8_t randomPosition, uint8_t fakeTop) {
uint8_t i;
uint16_t minLvl, maxLvl;
int n, height;
n = analogRead(MIC_PIN); // Raw reading from mic
n = abs(n - 512 - DC_OFFSET); // Center on zero
n = (n <= NOISE) ? 0 : (n - NOISE); // Remove noise/hum
lvl = ((lvl * 7) + n) >> 3; // "Dampened" reading (else looks twitchy)
// Calculate bar height based on dynamic min/max levels (fixed point):
height = TOP * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
// PRINTX("height: ", height);
if (height < 0L) height = 0; // Clip output
else if (height > TOP) height = TOP;
if (height > peak) peak = height; // Keep 'peak' dot at top
if (randomPosition) {
if (height > 2) {
lastHighSignal = millis();
}
if ((millis() - lastHighSignal) > 2000L) {
if (millis() - lastTwinkle > 50) {
lastTwinkle = millis();
height = random8(3);
PRINTX("Detected silence, setting height to", height);
}
}
// draw front
for (i=0; i<NUM_LEDS; i++) {
int distanceFromCenter = abs(centerPoint - i);
if (distanceFromCenter >= height) {
leds[i] = CRGB::Black;
} else {
leds[i] = ColorFromPalette(gPalettes[gCurrentPaletteIndex], 90 + map(distanceFromCenter, 0, (NUM_LEDS-1)/5, 0, 255), 100, LINEARBLEND);
}
}
//move center point randomly
if ((height == 0) && (random8(2) == 1)) {
centerPoint = random8(NUM_LEDS);
}
} else {
for (uint8_t i = 0; i < MAX_SINGLE_STRIP_LENGTH; i++) {
CRGB color;
if (i >= height) {
color = CRGB(0, 0, 0);
} else {
color = ColorFromPalette(gPalettes[gCurrentPaletteIndex], 90 + map(i, 0, MAX_SINGLE_STRIP_LENGTH-1, 0, 255), 100, LINEARBLEND);
}
for (uint8_t iStrip = 0; iStrip < numberOfStrips; iStrip++) {
Strip strip = strips[iStrip];
if (i >= strip.mLength) continue;
leds[strip.mStart + i] = color;
}
}
bool showPeak = peak > 0 && peak <= MAX_SINGLE_STRIP_LENGTH-1;
CHSV peakColor = CHSV(map(peak,0,MAX_SINGLE_STRIP_LENGTH-1,30,150), 255, 255);
for (int iStrip = 0; iStrip < numberOfStrips; iStrip++) {
Strip strip = strips[iStrip];
if (showPeak) {
leds[strip.mStart + peak] = peakColor;
}
}
}
// Every few frames, make the peak pixel drop by 1:
if (++dotCount >= PEAK_FALL) { // fall rate
if (peak > 0) peak--;
dotCount = 0;
}
vol[volCount] = n; // Save sample for dynamic leveling
if (++volCount >= SAMPLES) volCount = 0; // Advance/rollover sample counter
// Get volume range of prior frames
minLvl = maxLvl = vol[0];
for (i=1; i< SAMPLES; i++) {
if (vol[i] < minLvl) minLvl = vol[i];
else if (vol[i] > maxLvl) maxLvl = vol[i];
}
// minLvl and maxLvl indicate the volume range over prior frames, used
// for vertically scaling the output graph (so it looks interesting
// regardless of volume level). If they're too close together though
// (e.g. at very low volume levels) the graph becomes super coarse
// and 'jumpy'...so keep some minimum distance between them (this
// also lets the graph go to zero when no sound is playing):
if((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6; // Dampen min/max levels
maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6; // (fake rolling average)
return 0;
}