-
Notifications
You must be signed in to change notification settings - Fork 30
/
Uno_vu_line.ino
310 lines (260 loc) · 9.16 KB
/
Uno_vu_line.ino
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
// --------------------------------
// -- VU Meter - Scott's version --
// --------------------------------
#include <FastLED.h>
#include <EEPROM.h>
#include <JC_Button.h>
# define LEFT_OUT_PIN 6 // Left channel data out pin to LEDs [6]
# define RIGHT_OUT_PIN 5 // Right channel data out pin to LEDs [5]
# define LEFT_IN_PIN A5 // Left aux in signal [A5]
# define RIGHT_IN_PIN A4 // Right aux in signal [A4]
# define BTN_PIN 3 // Push button on this pin [3]
# define DEBOUNCE_MS 20 // Number of ms to debounce the button [20]
# define LONG_PRESS 500 // Number of ms to hold the button to count as long press [500]
# define N_PIXELS 18 // Number of pixels in each string [24]
# define MAX_MILLIAMPS 500 // Maximum current to draw [500]
# define COLOR_ORDER GRB // Colour order of LED strip [GRB]
# define LED_TYPE WS2812B // LED string type [WS2812B]
# define DC_OFFSET 0 // DC offset in aux signal [0]
# define NOISE 20 // Noise/hum/interference in aux signal [10]
# define SAMPLES 60 // Length of buffer for dynamic level adjustment [60]
# define TOP (N_PIXELS + 2) // Allow dot to go slightly off scale [(N_PIXELS + 2)]
# define PEAK_FALL 20 // Rate of peak falling dot [20]
# define N_PIXELS_HALF (N_PIXELS / 2)
# define PATTERN_TIME 10 // Seconds to show eaach pattern on auto [10]
# define STEREO true // If true, L&R channels are independent. If false, both L&R outputs display same data from L audio channel [false]
uint8_t volCountLeft = 0; // Frame counter for storing past volume data
int volLeft[SAMPLES]; // Collection of prior volume samples
int lvlLeft = 0; // Current "dampened" audio level
int minLvlAvgLeft = 0; // For dynamic adjustment of graph low & high
int maxLvlAvgLeft = 512;
uint8_t volCountRight = 0; // Frame counter for storing past volume data
int volRight[SAMPLES]; // Collection of prior volume samples
int lvlRight = 0; // Current "dampened" audio level
int minLvlAvgRight = 0; // For dynamic adjustment of graph low & high
int maxLvlAvgRight = 512;
CRGB ledsLeft[N_PIXELS];
CRGB ledsRight[N_PIXELS];
uint8_t myhue = 0;
void vu4(bool is_centered, uint8_t channel);
void vu5(bool is_centered, uint8_t channel);
void vu6(bool is_centered, uint8_t channel);
void vu7(bool show_background);
void vu8();
void vu9();
void vu10();
void balls();
void fire();
void juggle();
void ripple(boolean show_background);
void sinelon();
void twinkle();
void rainbow(uint8_t rate);
// --------------------
// --- Button Stuff ---
// --------------------
uint8_t state = 0;
int buttonPushCounter = 0;
bool autoChangeVisuals = false;
Button modeBtn(BTN_PIN, DEBOUNCE_MS);
void incrementButtonPushCounter() {
buttonPushCounter = ++buttonPushCounter %17;
EEPROM.write(1, buttonPushCounter);
}
void setup() {
delay(1000); // power-up safety delay
FastLED.addLeds < LED_TYPE, LEFT_OUT_PIN, COLOR_ORDER > (ledsLeft, N_PIXELS).setCorrection(TypicalLEDStrip);
FastLED.addLeds < LED_TYPE, RIGHT_OUT_PIN, COLOR_ORDER > (ledsRight, N_PIXELS).setCorrection(TypicalLEDStrip);
//FastLED.setBrightness(BRIGHTNESS);
FastLED.setMaxPowerInVoltsAndMilliamps(5, MAX_MILLIAMPS);
modeBtn.begin();
Serial.begin(57600);
buttonPushCounter = (int)EEPROM.read(1); // load previous setting
buttonPushCounter = 0;
Serial.print("Starting pattern ");
Serial.println(buttonPushCounter);
}
void loop() {
// Read button
modeBtn.read();
switch (state) {
case 0:
if (modeBtn.wasReleased()) {
Serial.print("Short press, pattern ");
Serial.println(buttonPushCounter);
incrementButtonPushCounter();
autoChangeVisuals = false;
}
else if (modeBtn.pressedFor(LONG_PRESS))
state = 1;
break;
case 1:
if (modeBtn.wasReleased()) {
state = 0;
Serial.print("Long press, auto, pattern ");
Serial.println(buttonPushCounter);
autoChangeVisuals = true;
}
break;
}
// Switch pattern if on auto
if(autoChangeVisuals){
EVERY_N_SECONDS(PATTERN_TIME) {
incrementButtonPushCounter();
Serial.print("Auto, pattern ");
Serial.println(buttonPushCounter);
}
}
// Run selected pattern
switch (buttonPushCounter) {
case 0:
vu4(false, 0);
if (STEREO) vu4(false, 1);
else copyLeftToRight();
break;
case 1:
vu4(true, 0);
if (STEREO) vu4(true, 1);
else copyLeftToRight();
break;
case 2:
vu5(false, 0);
if (STEREO) vu5(false, 1);
else copyLeftToRight();
break;
case 3:
vu5(true, 0);
if (STEREO) vu5(true, 1);
else copyLeftToRight();
break;
case 4:
vu6(false, 0);
if (STEREO) vu6(false, 1);
else copyLeftToRight();
break;
case 5:
vu7(true);
copyLeftToRight();
break;
case 6:
vu8();
copyLeftToRight();
break;
case 7:
vu9();
break;
case 8:
vu10();
break;
case 9:
vu7(false);
copyLeftToRight();
break;
case 10:
twinkle();
break;
case 11:
sinelon();
break;
case 12:
balls();
break;
case 13:
juggle();
break;
case 14:
fire();
break;
case 15:
ripple(false);
break;
case 16:
rainbow(10);
break;
}
}
// ------------------
// -- VU functions --
// ------------------
uint16_t auxReading(uint8_t channel) {
int n = 0;
uint16_t height = 0;
if(channel == 0) {
int n = analogRead(LEFT_IN_PIN); // Raw reading from left line in
n = abs(n - 512 - DC_OFFSET); // Center on zero
n = (n <= NOISE) ? 0 : (n - NOISE); // Remove noise/hum
lvlLeft = ((lvlLeft * 7) + n) >> 3; // "Dampened" reading else looks twitchy (>>3 is divide by 8)
volLeft[volCountLeft] = n; // Save sample for dynamic leveling
volCountLeft = ++volCountLeft % SAMPLES;
// Calculate bar height based on dynamic min/max levels (fixed point):
height = TOP * (lvlLeft - minLvlAvgLeft) / (long)(maxLvlAvgLeft - minLvlAvgLeft);
}
else {
int n = analogRead(RIGHT_IN_PIN); // Raw reading from mic
n = abs(n - 512 - DC_OFFSET); // Center on zero
n = (n <= NOISE) ? 0 : (n - NOISE); // Remove noise/hum
lvlRight = ((lvlRight * 7) + n) >> 3; // "Dampened" reading (else looks twitchy)
volRight[volCountRight] = n; // Save sample for dynamic leveling
volCountRight = ++volCountRight % SAMPLES;
// Calculate bar height based on dynamic min/max levels (fixed point):
height = TOP * (lvlRight - minLvlAvgRight) / (long)(maxLvlAvgRight - minLvlAvgRight);
}
// Calculate bar height based on dynamic min/max levels (fixed point):
height = constrain(height, 0, TOP);
return height;
}
void copyLeftToRight() {
for (uint8_t i = 0; i < N_PIXELS; i++) {
ledsRight[i] = ledsLeft[i];
}
}
/*
* Function for dropping the peak
*/
uint8_t peakLeft, peakRight;
void dropPeak(uint8_t channel) {
static uint8_t dotCountLeft, dotCountRight;
if(channel == 0) {
if(++dotCountLeft >= PEAK_FALL) { //fall rate
if(peakLeft > 0) peakLeft--;
dotCountLeft = 0;
}
} else {
if(++dotCountRight >= PEAK_FALL) { //fall rate
if(peakRight > 0) peakRight--;
dotCountRight = 0;
}
}
}
/*
* Function for averaging the sample readings
*/
void averageReadings(uint8_t channel) {
uint16_t minLvl, maxLvl;
// 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(channel == 0) {
minLvl = maxLvl = volLeft[0];
for (int i = 1; i < SAMPLES; i++) {
if (volLeft[i] < minLvl) minLvl = volLeft[i];
else if (volLeft[i] > maxLvl) maxLvl = volLeft[i];
}
if ((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
minLvlAvgLeft = (minLvlAvgLeft * 63 + minLvl) >> 6; // Dampen min/max levels
maxLvlAvgLeft = (maxLvlAvgLeft * 63 + maxLvl) >> 6; // (fake rolling average)
}
else {
minLvl = maxLvl = volRight[0];
for (int i = 1; i < SAMPLES; i++) {
if (volRight[i] < minLvl) minLvl = volRight[i];
else if (volRight[i] > maxLvl) maxLvl = volRight[i];
}
if ((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
minLvlAvgRight = (minLvlAvgRight * 63 + minLvl) >> 6; // Dampen min/max levels
maxLvlAvgRight = (maxLvlAvgRight * 63 + maxLvl) >> 6; // (fake rolling average)
}
}