• How to create a simple audio spectrum analyzer with adjustable parameters

QC

How to create a simple audio spectrum analyzer with adjustable parameters

Simple audio spectrum analyzer with multiple display modes, adjustable speed and sensitivity for a customizable audio viewing experience.

Devices and components

Arduino-Nano

128x64 STN LCD Graphic LED Backlight

Press the button

10k resistance

Adjustable potentiometer, 10 kohm

Resistance 4K7

Materials and tools

Soldering iron (generic)

Software and tools

Arduino IDE

Project description

1#define AUTO_GAIN 0 // Auto volume adjustment (disabled for manual control)
2#define VOL_THR 25 // Silence threshold (no display on matrix below this)
3#define LOW_PASS 20 // Lower sensitivity threshold for noise (no jumps when no sound)
4#define DEF_GAIN 80 // Default maximum threshold (ignored when GAIN_CONTROL is active)
5#define FHT_N 256 // Spectrum width x2
6#define LOG_OUT 1
7#define PEAK_HOLD_TIME 2000 // Peak hold time in ms
8
9// Button pins
10#define BUTTON1 8
11#define BUTTON2 9
12#define BUTTON3 10
13
14// Manually defined array of tones, first smooth, then steeper
15byte posOffset[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // 1500 Hz
16//byte posOffset[16] = {1, 2, 3, 4, 6, 8, 10, 13, 16, 20, 25, 30, 35, 40, 45, 50}; // 4000 Hz
17
18#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
19#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
20
21#include <Wire.h>
22#include <U8glib.h> // http://rcl-radio.ru/wp-content/uploads/2023/04/U8glib.zip
23#include <FHT.h> // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=297&download=1
24
25#define EN 6
26#define RW 5
27#define CS 4
28
29//U8GLIB_SH1106_128X64 lcd(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST); // Dev 0, Fast I2C / TWI
30U8GLIB_ST7920_128X64_1X lcd(EN, RW, CS); // serial use, PSB = GND
31
32byte gain = DEF_GAIN;
33unsigned long gainTimer, times;
34byte maxValue, maxValue_f;
35float k = 0.1;
36byte ur[16], urr[16];
37
38// Button state variables
39bool button1State = false;
40bool button2State = false;
41bool button3State = false;
42unsigned long button1Time = 0;
43unsigned long button2Time = 0;
44unsigned long button3Time = 0;
45
46// Mode variables
47byte displayMode = 0; // 0=normal, 1=peak hold, 2=falling dots, 3=symmetrical
48byte speedMode = 0; // 0=normal, 1=fast, 2=slow
49byte sensitivityMode = 0; // 0=normal, 1=high, 2=low
50byte peakHold[16]; // Peak hold values for each band
51unsigned long peakTimer[16]; // Timer for peak decay
52
53void setup() {
54 delay(100);
55 sbi(ADCSRA, ADPS2);
56 cbi(ADCSRA, ADPS1);
57 sbi(ADCSRA, ADPS0);
58 Serial.begin(9600);
59 Wire.begin();
60 Wire.setClock(800000L);
61 lcd.begin();
62 // lcd.setRot180();
63 lcd.setFont(u8g_font_profont11r);
64 analogReadResolution(10); // ADC 10 BIT
65 analogReference(INTERNAL1V024);
66 pinMode(A0, INPUT); // INPUT AUDIO
67
68 // Initialize button pins
69 pinMode(BUTTON1, INPUT_PULLUP);
70 pinMode(BUTTON2, INPUT_PULLUP);
71 pinMode(BUTTON3, INPUT_PULLUP);
72
73 // Initialize peak hold array
74 for(int i = 0; i < 16; i++) {
75 peakHold[i] = 0;
76 peakTimer[i] = 0;
77 }
78}
79
80void handleButtons() {
81 // Button 1 - Display Mode Cycle
82 if (digitalRead(BUTTON1) == LOW) {
83 if (millis() - button1Time > 300) { // Debounce
84 displayMode = (displayMode + 1) % 4; // Cycle through 4 modes
85 button1Time = millis();
86 }
87 }
88
89 // Button 2 - Speed Mode Cycle
90 if (digitalRead(BUTTON2) == LOW) {
91 if (millis() - button2Time > 300) {
92 speedMode = (speedMode + 1) % 3; // Cycle through 3 speed modes
93 button2Time = millis();
94 }
95 }
96
97 // Button 3 - Sensitivity Cycle
98 if (digitalRead(BUTTON3) == LOW) {
99 if (millis() - button3Time > 300) {
100 sensitivityMode = (sensitivityMode + 1) % 3; // Cycle through 3 sensitivity modes
101 button3Time = millis();
102
103 // Adjust gain based on sensitivity
104 switch(sensitivityMode) {
105 case 0: gain = DEF_GAIN; break; // Normal
106 case 1: gain = DEF_GAIN / 2; break; // High sensitivity
107 case 2: gain = DEF_GAIN * 2; break; // Low sensitivity
108 }
109 }
110 }
111}
112
113void updatePeakHold() {
114 for (int i = 0; i < 16; i++) {
115 int posLevel = map(fht_log_out[posOffset[i]], LOW_PASS, gain, 0, 60);
116 posLevel = constrain(posLevel, 0, 60);
117
118 if (posLevel > peakHold[i]) {
119 peakHold[i] = posLevel;
120 peakTimer[i] = millis();
121 } else if (millis() - peakTimer[i] > PEAK_HOLD_TIME) {
122 if (peakHold[i] > 0) peakHold[i]--;
123 }
124 }
125}
126
127void drawModeIndicators() {
128 // Display mode indicators at top right
129 lcd.setFont(u8g_font_04b_03);
130
131 // Display mode indicator (N, P, D, S)
132 char modeChar = 'N';
133 switch(displayMode) {
134 case 0: modeChar = 'N'; break; // Normal
135 case 1: modeChar = 'P'; break; // Peak
136 case 2: modeChar = 'D'; break; // Dot
137 case 3: modeChar = 'S'; break; // Symmetrical
138 }
139
140 // Speed mode indicator (N, F, S)
141 char speedChar = 'N';
142 switch(speedMode) {
143 case 0: speedChar = 'N'; break; // Normal
144 case 1: speedChar = 'F'; break; // Fast
145 case 2: speedChar = 'S'; break; // Slow
146 }
147
148 // Sensitivity indicator (N, H, L)
149 char sensChar = 'N';
150 switch(sensitivityMode) {
151 case 0: sensChar = 'N'; break; // Normal
152 case 1: sensChar = 'H'; break; // High
153 case 2: sensChar = 'L'; break; // Low
154 }
155
156 // Draw all three indicators at top right
157 lcd.drawStr(100, 5, String(modeChar).c_str());
158 lcd.drawStr(110, 5, String(speedChar).c_str());
159 lcd.drawStr(120, 5, String(sensChar).c_str());
160}
161
162void drawSpectrum() {
163 lcd.firstPage();
164 do {
165 for (int pos = 0; pos < 128; pos += 8) {
166 int band = pos / 8;
167 int posLevel = map(fht_log_out[posOffset[band]], LOW_PASS, gain, 0, 60);
168 posLevel = constrain(posLevel, 0, 60);
169
170 if(millis() - times < 2000) {
171 posLevel = 60; // Startup animation
172 }
173
174 urr[band] = posLevel;
175
176 // Apply speed mode to falling effect
177 int fallSpeed = 1;
178 switch(speedMode) {
179 case 0: fallSpeed = 1; break; // Normal
180 case 1: fallSpeed = 3; break; // Fast fall
181 case 2: fallSpeed = 1; if(random(2) == 0) fallSpeed = 0; break; // Slow/random
182 }
183
184 if(urr[band] < ur[band]) {
185 ur[band] = max(ur[band] - fallSpeed, 0);
186 } else {
187 ur[band] = posLevel;
188 }
189
190 delayMicroseconds(200);
191
192 // Draw based on display mode
193 switch(displayMode) {
194 case 0: // Normal bars
195 for (int v_pos = 0; v_pos < ur[band] + 4; v_pos += 4) {
196 lcd.drawBox(pos, 61 - v_pos, 6, 2);
197 }
198 break;
199
200 case 1: // Peak hold with bars
201 for (int v_pos = 0; v_pos < ur[band] + 4; v_pos += 4) {
202 lcd.drawBox(pos, 61 - v_pos, 6, 2);
203 }
204 // Draw peak dots
205 if(peakHold[band] > 0) {
206 lcd.drawBox(pos + 1, 61 - peakHold[band], 4, 1);
207 }
208 break;
209
210 case 2: // Falling dots
211 for (int v_pos = 0; v_pos < ur[band]; v_pos += 4) {
212 lcd.drawBox(pos + 1, 61 - v_pos, 4, 1);
213 }
214 break;
215
216 case 3: // Symmetrical mode
217 for (int v_pos = 0; v_pos < ur[band] + 4; v_pos += 4) {
218 lcd.drawBox(pos, 61 - v_pos, 6, 2);
219 lcd.drawBox(pos, 3 + v_pos, 6, 2); // Mirror at top
220 }
221 break;
222 }
223 }
224
225 // Draw mode indicators at top right
226 drawModeIndicators();
227
228 } while(lcd.nextPage());
229}
230
231void loop() {
232 analyzeAudio();
233 handleButtons();
234 updatePeakHold();
235 drawSpectrum();
236
237 if (AUTO_GAIN) {
238 maxValue_f = maxValue * k + maxValue_f * (1 - k);
239 if (millis() - gainTimer > 1500) {
240 if (maxValue_f > VOL_THR) gain = maxValue_f;
241 else gain = 100;
242 gainTimer = millis();
243 }
244 }
245}
246
247void analyzeAudio() {
248 for (int i = 0 ; i < FHT_N ; i++) {
249 int sample = analogRead(A0);
250 fht_input[i] = sample; // put real data into bins
251 }
252 fht_window(); // window the data for better frequency response
253 fht_reorder(); // reorder the data before doing the fht
254 fht_run(); // process the data in the fht
255 fht_mag_log(); // take the output of the fht
256}

Documentation

Schematic

Diagram.jpg




Note: Content and images are from: https://projecthub.arduino.cc/, with some modifications.
If you want it removed due to copyright reasons, please leave a comment. Thank you.
I want to share this article more widely so that everyone knows about Arduino and your project.

Arduino Tutorial: Mini Piano

In this video I show you how to make a mini piano with Arduino. Devices and components Arduino Uno Rev3 Jumper wires (generic) Buzzer Breadb...