Repository navigation
Expand file tree
/
Copy pathVisualEar.ino
More file actions
284 lines (225 loc) · 7.45 KB
/
Copy pathVisualEar.ino
File metadata and controls
284 lines (225 loc) · 7.45 KB
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
/*
Visual Ear
Audio spectrum analyser
LED strip display.
For details see: https://hackaday.io/project/175944-the-visual-ear
Copyright (C) 2021 Philip Malone
Display Modes:
0 - Off
1 - VU Mater
2 - Specturm Analyser
3 - Tone Detector
4 - Fireworks
Version Date Description
2.0 2/2/2022 First release with 4 functions and 104 LEDs
*/
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>
#include <EEPROM.h>
#include "audioAnalyzer.h"
#include "devconf.h"
#include "display.h"
#define FASTLED_INTERNAL
#include "FastLED.h"
// Set project identification here
const char Version[] = "Visyual Ear. V2.0";
const char Branch[] = "Main";
const char Description[] = "104 Bands. 43Hz to 16744 Hz";
// -- LED Display Constants
#define START_NOISE_FLOOR 60 // Frequency Bin Magnitudes below this value will not get summed into Bands. (Initial high value) was 80
#define BASE_NOISE_FLOOR 40 // Frequency Bin Magnitudes below this value will not get summed into Bands. (Final minimumm value)
#define UI_HOLD_MS 3000
#define UI_STEP_MS 200
#define UI_BUTTON_PIN 3
const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;
float upGainAccumulator = 0;
float downGainAccumulator = 0;
// -- LED Display Data
uint32_t bandValues[NUM_BANDS];
uint16_t LO_bandBins[NUM_LO_BANDS + 1] = {13,14,15,16,17,18,20,21,22,23,25,26,28,29,31,33,35,37,39,41,44,46,49,52,55,58};
uint16_t MD_bandBins[NUM_MD_BANDS + 1] = {29,31,33,35,37,39,41,44,46,49,52,55,58,62,66,70,74,78,83,88,93,98,104,110,117,124,131,139,147,156,165,175};
uint16_t HI_bandBins[NUM_HI_BANDS + 1] = {25,27,28,30,32,33,35,37,40,42,45,47,50,53,56,60,63,67,71,75,79,84,89,94,100,106,112,119,126,134,142,150,159,168,178,189,200,212,225,238,252,267,283,300,318,337,357,378,400};
// Create the Audio components. These should be created in the
AudioInputI2S audioInput; // audio shield: mic or line-in
AudioAnalyzeFFT myFFT;
AudioAnalyzePeak peak; //xy=393,385
// Connect the live input
AudioConnection patchCord1(audioInput, 0, myFFT, 0);
AudioConnection patchCord2(audioInput, 0, peak, 0);
unsigned long startTime = millis();
unsigned long lastTime = millis();
unsigned long cycleTime = 0;
unsigned long buttonStart = 0;
bool adjusting = false;
bool UIHolding = false;
bool UIButton = false;
bool lastUIButton = false;
int activeBands = 0;
// Non Volatile values
short gainNumber = 0;
// ==================================================================================================
void setup() {
Serial.begin(500000);
pinMode(UI_BUTTON_PIN, INPUT_PULLUP);
// Audio connections require memory to work. For more
AudioMemory(NUM_BURSTS);
// read NV Ram
setDisplayMode( EEPROM.read(MODE_ADDRESS));
setGain(EEPROM.read(GAIN_ADDRESS));
Serial.println(Version);
Serial.println(Branch);
Serial.println(Description);
delay(500);
initDisplay();
}
void loop() {
// check the button and see if we have a change
runUI();
if (getDisplayMode() == 1) {
if (peak.available()){
updateVuDisplay(peak.readPeakToPeak());
}
} else {
if (myFFT.available()) {
// each time new FFT data is available update the diplay
startTime = millis();
cycleTime = startTime - lastTime;
lastTime = startTime;
fillBands();
updateDisplay(bandValues);
runAGC();
}
}
}
// ==================================================================================================
void runUI() {
// debounce
if (pressElapsed() > 100) {
UIButton = !digitalRead(UI_BUTTON_PIN);
// Just Pressed, start the hold timer
if (UIButton && !lastUIButton) {
pressReset();
}
// Look for button release. Bump mode
if (!UIButton && lastUIButton) {
if (!UIHolding) {
bumpMode();
}
}
lastUIButton = UIButton;
}
}
long pressElapsed() {
return millis() - buttonStart;
}
void pressReset() {
buttonStart = millis() ;
}
void runAGC(){
// Automatic Gain Control
// Automatically control the gain to keep a reasonable quantity of active frequency buckets
float OCR = (float)activeBands / (float)NUM_BANDS;
/*
Serial.print("AB ");
Serial.print(activeBands);
Serial.print(", GN ");
Serial.println(gainNumber);
*/
if (OCR < lowTrip) {
upGainAccumulator += 0.01;
} else if (OCR > highTrip) {
upGainAccumulator -= 0.04;
}
if (upGainAccumulator > 1.0) {
bumpGain(1);
upGainAccumulator = 0.0;
} else if (upGainAccumulator < -1.0) {
bumpGain(-1);
upGainAccumulator = 0.0;
}
/*
Serial.print("AGC OLC ");
Serial.print(OLC);
Serial.print(", OCR ");
Serial.print(OCR);
Serial.print(", UA ");
Serial.print(upGainAccumulator);
Serial.print(", Gain Number ");
Serial.println(gainNumber);
*/
}
void bumpGain(int step ) {
buttonStart = millis();
setGain(gainNumber + step);
pressReset();
}
void bumpMode() {
buttonStart = millis();
EEPROM.write(MODE_ADDRESS, switchDisplayMode());
initDisplay();
}
// save the current selected gain value (which LED) and calculate Amplitude divide factor
int setGain(short newgain) {
double gainScale;
if (newgain < MIN_GAIN_NUM)
newgain = MIN_GAIN_NUM;
else if (newgain > MAX_GAIN_NUM)
newgain = MAX_GAIN_NUM;
gainNumber = newgain;
EEPROM.write(GAIN_ADDRESS, gainNumber);
gainScale = minScale * pow(gainSlope, gainNumber);
myFFT.setInputScale(gainScale);
/*
Serial.print("Gain # ");
Serial.print(gainNumber);
Serial.print(" Gain ");
Serial.println(gainScale * 100);
*/
return gainNumber;
}
// ==================================================================================================
// Group Frequency Bins into Band Buckets based on the maximum nun number for each band
// Each band covers more bind because bins are linear and bands are logorithmic.
void fillBands (void){
uint32_t noiseFloor;
uint8_t band;
// zero out all the LED band magnitudes.
memset(bandValues, 0, sizeof(bandValues));
// Cycle through each of the LED bands. Set initial noise threshold high and drop down.
noiseFloor = START_NOISE_FLOOR;
band = 0;
activeBands = 0;
for (int b = 0; b < NUM_LO_BANDS; b++, band++){
// Accumulate freq values from all bins that match this LED band,
bandValues[band] = (uint32_t)myFFT.read(0, LO_bandBins[b], LO_bandBins[b+1], noiseFloor);
if (bandValues[band] > 2)
activeBands++;
// Adjust Noise Floor
if (noiseFloor > BASE_NOISE_FLOOR) {
noiseFloor = 97 * noiseFloor / 100; // equiv 0.97 factor.
}
}
for (int b = 0; b < NUM_MD_BANDS; b++, band++){
// Accumulate freq values from all bins that match this LED band,
bandValues[band] = (uint32_t)myFFT.read(1, MD_bandBins[b], MD_bandBins[b+1], noiseFloor);
if (bandValues[band] > 2)
activeBands++;
// Adjust Noise Floor
if (noiseFloor > BASE_NOISE_FLOOR) {
noiseFloor = 97 * noiseFloor / 100; // equiv 0.97 factor.
}
}
for (int b = 0; b < NUM_HI_BANDS; b++, band++){
// Accumulate freq values from all bins that match this LED band,
bandValues[band] = (uint32_t)myFFT.read(2, HI_bandBins[b], HI_bandBins[b+1], noiseFloor);
if (bandValues[band] > 2)
activeBands++;
// Adjust Noise Floor
if (noiseFloor > BASE_NOISE_FLOOR) {
noiseFloor = 95 * noiseFloor / 100; // equiv 0.95 factor.
}
}
}