QC

Arduino Music: note and chord detector

This project presents a program to detect musical notes and chords using Arduino (nano or higher).

Devices and components

Arduino-Nano

Max4466

Software and tools

Arduino IDE

Project description

Problem:

Note detection algorithm

for(int i=0;i<128;i++)

{

a=analogRead(Mic_pin)-500; //rough zero shift

sum1=sum1+a; //to average value

sum2=sum2+a*a; // to RMS value

a=a*(sin(i*3.14/128)*sin(i*3.14/128)); // Hann window

in[i]=4*a; // scaling for float to int conversion

delayMicroseconds(195); // based on operation frequency range

}

if(f_peaks[i]>1040){f_peaks[i]=0;}<br> if(f_peaks[i]>=65.4 && f_peaks[i]<=130.8) {f_peaks[i]=255*((f_peaks[i]/65.4)-1);}

if(f_peaks[i]>=130.8 && f_peaks[i]<=261.6) {f_peaks[i]=255*((f_peaks[i]/130.8)-1);}

if(f_peaks[i]>=261.6 && f_peaks[i]<=523.25){f_peaks[i]=255*((f_peaks[i]/261.6)-1);}

if(f_peaks[i]>=523.25 && f_peaks[i]<=1046) {f_peaks[i]=255*((f_peaks[i]/523.25)-1);}

if(f_peaks[i]>=1046 && f_peaks[i]<=2093) {f_peaks[i]=255*((f_peaks[i]/1046)-1);}

byte NoteV[13]={8,23,40,57,76,96,116,138,162,187,213,241,255};

A. Note detection:

B: chord detection:

for (int i=0;i<12;i++)

{

in[20+i]=in[i]*in[i+4]*in[i+7];

in[32+i]=in[i]*in[i+3]*in[i+7]; //all chord check

}

Application

void setup()

{Serial.begin(250000);

Mic_pin = A7;

}

if(sum2-sum1>5){

.

.

{ a=analogRead(Mic_pin)-500; //rough zero shift

sum1=sum1+a; //to average value

sum2=sum2+a*a; // to RMS value

a=a*(sin(i*3.14/128)*sin(i*3.14/128)); // Hann window

in[i]=4*a; // scaling for float to int conversion

delayMicroseconds(195); // based on operation frequency range

}

As mentioned in the EasyFFT tutorial, FFT consumes a huge amount of Arduino's memory. So if you have a program that needs to store certain values, it is recommended to use a card with higher memory.

This code may work well for one instrument/singer and poorly for another. Accurate real-time detection is not possible due to computational limitations.

Summery

Note detection program

arduino

This program will print the name of the detected note. Refer to the comment in the code for clarification.

1//---------------------------------------------------------------------------//
2int in[128];
3byte NoteV[13]={8,23,40,57,76,96,116,138,162,187,213,241,255}; //data for note detection based on frequency
4float f_peaks[5]; // top 5 frequencies peaks in descending order
5int Mic_pin;
6//---------------------------------------------------------------------------//
7
8
9void setup() {
10Serial.begin(250000);
11Mic_pin = A7; // change as per Microphone pin
12}
13
14
15void loop()
16{
17 Tone_det();
18
19}
20
21
22//-----------------------------Tone Detection Function----------------------------------------------//
23// Documentation on Tone_detection:https://www.instructables.com/member/abhilash_patel/instructables/
24// Code Written By: Abhilash Patel
25// Contact: abhilashpatel121@gmail.com
26// This code written for arduino Nano board (should also work for UNO)
27// This code won't work for any board having RAM less than 2kb,
28
29
30
31void Tone_det()
32{ long unsigned int a1,b,a2;
33 float a;
34 float sum1=0,sum2=0;
35 float sampling;
36 a1=micros();
37 for(int i=0;i<128;i++)
38 {
39 a=analogRead(Mic_pin)-500; //rough zero shift
40 //utilising time between two sample for windowing & amplitude calculation
41 sum1=sum1+a; //to average value
42 sum2=sum2+a*a; // to RMS value
43 a=a*(sin(i*3.14/128)*sin(i*3.14/128)); // Hann window
44 in[i]=10*a; // scaling for float to int conversion
45 delayMicroseconds(195); // based on operation frequency range
46 }
47b=micros();
48
49sum1=sum1/128; // Average amplitude
50sum2=sqrt(sum2/128); // RMS
51sampling= 128000000/(b-a1); // real time sampling frequency
52
53//for very low or no amplitude, this code wont start
54//it takes very small aplitude of sound to initiate for value sum2-sum1>3,
55//change sum2-sum1 threshold based on requirement
56if(sum2-sum1>3){
57 FFT(128,sampling);
58 //EasyFFT based optimised FFT code,
59 //this code updates f_peaks array with 5 most dominent frequency in descending order
60
61 for(int i=0;i<12;i++){in[i]=0;} // utilising in[] array for further calculation
62
63int j=0,k=0; //below loop will convert frequency value to note
64 for(int i=0; i<5;i++)
65 {
66 if(f_peaks[i]>1040){f_peaks[i]=0;}
67 if(f_peaks[i]>=65.4 && f_peaks[i]<=130.8) {f_peaks[i]=255*((f_peaks[i]/65.4)-1);}
68 if(f_peaks[i]>=130.8 && f_peaks[i]<=261.6) {f_peaks[i]=255*((f_peaks[i]/130.8)-1);}
69 if(f_peaks[i]>=261.6 && f_peaks[i]<=523.25){f_peaks[i]=255*((f_peaks[i]/261.6)-1);}
70 if(f_peaks[i]>=523.25 && f_peaks[i]<=1046) {f_peaks[i]=255*((f_peaks[i]/523.25)-1);}
71 if(f_peaks[i]>=1046 && f_peaks[i]<=2093) {f_peaks[i]=255*((f_peaks[i]/1046)-1);}
72 if(f_peaks[i]>255){f_peaks[i]=254;}
73 j=1;k=0;
74
75 while(j==1)
76 {
77 if(f_peaks[i]<NoteV[k]){f_peaks[i]=k;j=0;}
78 k++; // a note with max peaks (harmonic) with aplitude priority is selected
79 if(k>15){j=0;}
80 }
81
82 if(f_peaks[i]==12){f_peaks[i]=0;}
83 k=f_peaks[i];
84 in[k]=in[k]+(5-i);
85 }
86
87k=0;j=0;
88 for(int i=0;i<12;i++)
89 {
90 if(k<in[i]){k=in[i];j=i;} //Max value detection
91 }
92 // Note print
93 // if you need to use note value for some application, use of note number recomendded
94 // where, 0=c;1=c#,2=D;3=D#;.. 11=B;
95 //a2=micros(); // time check
96 k=j;
97 if(k==0) {Serial.println('C');}
98 if(k==1) {Serial.print('C');Serial.println('#');}
99 if(k==2) {Serial.println('D');}
100 if(k==3) {Serial.print('D');Serial.println('#');}
101 if(k==4) {Serial.println('E');}
102 if(k==5) {Serial.println('F');}
103 if(k==6) {Serial.print('F');Serial.println('#');}
104 if(k==7) {Serial.println('G');}
105 if(k==8) {Serial.print('G');Serial.println('#');}
106 if(k==9) {Serial.println('A');}
107 if(k==10){Serial.print('A');Serial.println('#');}
108 if(k==11){Serial.println('B');}
109 }
110}
111
112//-----------------------------FFT Function----------------------------------------------//
113// Documentation on EasyFFT:https://www.instructables.com/member/abhilash_patel/instructables/
114// EasyFFT code optimised for 128 sample size to reduce mamory consumtion
115
116float FFT(byte N,float Frequency)
117{
118byte data[8]={1,2,4,8,16,32,64,128};
119int a,c1,f,o,x;
120a=N;
121
122 for(int i=0;i<8;i++) //calculating the levels
123 { if(data[i]<=a){o=i;} }
124 o=7;
125byte in_ps[data[o]]={}; //input for sequencing
126float out_r[data[o]]={}; //real part of transform
127float out_im[data[o]]={}; //imaginory part of transform
128
129x=0;
130 for(int b=0;b<o;b++) // bit reversal
131 {
132 c1=data[b];
133 f=data[o]/(c1+c1);
134 for(int j=0;j<c1;j++)
135 {
136 x=x+1;
137 in_ps[x]=in_ps[j]+f;
138 }
139 }
140
141 for(int i=0;i<data[o];i++) // update input array as per bit reverse order
142 {
143 if(in_ps[i]<a)
144 {out_r[i]=in[in_ps[i]];}
145 if(in_ps[i]>a)
146 {out_r[i]=in[in_ps[i]-a];}
147 }
148
149int i10,i11,n1;
150float e,c,s,tr,ti;
151
152 for(int i=0;i<o;i++) //fft
153 {
154 i10=data[i]; // overall values of sine cosine
155 i11=data[o]/data[i+1]; // loop with similar sine cosine
156 e=6.283/data[i+1];
157 e=0-e;
158 n1=0;
159
160 for(int j=0;j<i10;j++)
161 {
162 c=cos(e*j);
163 s=sin(e*j);
164 n1=j;
165
166 for(int k=0;k<i11;k++)
167 {
168 tr=c*out_r[i10+n1]-s*out_im[i10+n1];
169 ti=s*out_r[i10+n1]+c*out_im[i10+n1];
170
171 out_r[n1+i10]=out_r[n1]-tr;
172 out_r[n1]=out_r[n1]+tr;
173
174 out_im[n1+i10]=out_im[n1]-ti;
175 out_im[n1]=out_im[n1]+ti;
176
177 n1=n1+i10+i10;
178 }
179 }
180 }
181
182//---> here onward out_r contains amplitude and our_in conntains frequency (Hz)
183 for(int i=0;i<data[o-1];i++) // getting amplitude from compex number
184 {
185 out_r[i]=sqrt((out_r[i]*out_r[i])+(out_im[i]*out_im[i])); // to increase the speed delete sqrt
186 out_im[i]=(i*Frequency)/data[o];
187 /*
188 Serial.print(out_im[i],2); Serial.print("Hz");
189 Serial.print("\ "); // uncomment to print freuency bin
190 Serial.println(out_r[i]);
191 */
192 }
193
194x=0; // peak detection
195 for(int i=1;i<data[o-1]-1;i++)
196 {
197 if(out_r[i]>out_r[i-1] && out_r[i]>out_r[i+1])
198 {in_ps[x]=i; //in_ps array used for storage of peak number
199 x=x+1;}
200 }
201
202s=0;
203c=0;
204 for(int i=0;i<x;i++) // re arraange as per magnitude
205 {
206 for(int j=c;j<x;j++)
207 {
208 if(out_r[in_ps[i]]<out_r[in_ps[j]])
209 {s=in_ps[i];
210 in_ps[i]=in_ps[j];
211 in_ps[j]=s;}
212 }
213 c=c+1;
214 }
215
216 for(int i=0;i<5;i++) // updating f_peak array (global variable)with descending order
217 {
218 f_peaks[i]=(out_im[in_ps[i]-1]*out_r[in_ps[i]-1]+out_im[in_ps[i]]*out_r[in_ps[i]]+out_im[in_ps[i]+1]*out_r[in_ps[i]+1])
219 /(out_r[in_ps[i]-1]+out_r[in_ps[i]]+out_r[in_ps[i]+1]);
220 }
221}
222
223//------------------------------------------------------------------------------------//

Chord detection program

arduino

1//---------------------------------------------------------------------------//
2int in[128];
3byte NoteV[13]={8,23,40,57,76,96,116,138,162,187,213,241,255};
4float f_peaks[8]; // top 8 frequencies peaks in descending order
5//---------------------------------------------------------------------------//
6
7
8void setup() {
9Serial.begin(250000);
10}
11
12
13void loop()
14{
15 Chord_det();
16
17}
18
19
20//-----------------------------Chord Detection Function----------------------------------------------//
21// Documentation on Chord_detection:https://www.instructables.com/member/abhilash_patel/instructables/
22// Code Written By: Abhilash Patel
23// Contact: abhilashpatel121@gmail.com
24// this code written for arduino Nano board (should also work for UNO) or better board
25// this code won't work for any board having RAM less than 2kb,
26// More accurate detection can be carried out on more powerful borad by increasing sample size
27
28
29
30void Chord_det()
31{
32 long unsigned int a1,b,a2;
33 float a;
34 float sum1=0,sum2=0;
35 float sampling;
36 a1=micros();
37 for(int i=0;i<128;i++)
38 {
39 a=analogRead(A7)-500; //rough zero shift
40 //utilising time between two sample for windowing & amplitude calculation
41 sum1=sum1+a; //to average value
42 sum2=sum2+a*a; // to RMS value
43 a=a*(sin(i*3.14/128)*sin(i*3.14/128)); // Hann window
44 in[i]=4*a; // scaling for float to int conversion
45 delayMicroseconds(195); // based on operation frequency range
46 }
47b=micros();
48sum1=sum1/128; //average amplitude
49sum2=sqrt(sum2/128); //RMS amplitude
50sampling= 128000000/(b-a1); // real time sampling frequency
51
52//for very low or no amplitude, this code wont start
53//it takes very small aplitude of sound to initiate for value sum2-sum1>3,
54//change sum2-sum1 threshold based on requirement
55if(sum2-sum1>3){
56 FFT(128,sampling); //EasyFFT based optimised FFT code
57
58
59 for(int i=0;i<12;i++){in[i]=0;}
60
61int j=0,k=0; //below loop will convert frequency value to note
62 for(int i=0; i<8;i++)
63 {
64 if(f_peaks[i]>1040){f_peaks[i]=0;}
65 if(f_peaks[i]>=65.4 && f_peaks[i]<=130.8) {f_peaks[i]=255*((f_peaks[i]/65.4)-1);}
66 if(f_peaks[i]>=130.8 && f_peaks[i]<=261.6) {f_peaks[i]=255*((f_peaks[i]/130.8)-1);}
67 if(f_peaks[i]>=261.6 && f_peaks[i]<=523.25){f_peaks[i]=255*((f_peaks[i]/261.6)-1);}
68 if(f_peaks[i]>=523.25 && f_peaks[i]<=1046) {f_peaks[i]=255*((f_peaks[i]/523.25)-1);}
69 if(f_peaks[i]>=1046 && f_peaks[i]<=2093) {f_peaks[i]=255*((f_peaks[i]/1046)-1);}
70 if(f_peaks[i]>255){f_peaks[i]=254;}
71 j=1;k=0;
72 while(j==1)
73 {
74 if(f_peaks[i]<=NoteV[k]){f_peaks[i]=k;j=0;}
75 k++; // a note with max peaks (harmonic) with aplitude priority is selected
76 if(k>15){j=0;}
77 }
78
79 if(f_peaks[i]==12){f_peaks[i]=0;}
80 k=f_peaks[i];
81 in[k]=in[k]+(8-i);
82 }
83
84k=0;j=0;
85 for(int i=0;i<12;i++)
86 {
87 if(k<in[i]){k=in[i];j=i;} //Max value detection
88 }
89
90 for(int i=0;i<8;i++)
91 {
92 in[12+i]=in[i];
93 }
94
95for (int i=0;i<12;i++)
96{
97 in[20+i]=in[i]*in[i+4]*in[i+7];
98 in[32+i]=in[i]*in[i+3]*in[i+7]; //all chord check
99}
100
101
102for (int i=0;i<24;i++)
103{
104in[i]=in[i+20];
105if(k<in[i]){k=in[i];j=i;} // picking chord with max possiblity
106}
107char chord_out;
108int chord=j;
109if(chord>11){chord=chord-12;chord_out='m';} //Mojor-minor check
110else{chord_out=' ';}
111
112// Here "chord" variable has value of detected chord,
113// 0-11 defines all majot chord from C,C#,D,D#,.. B
114//12-23 defines all minor chord from Cm,C#m,Dm,D#m,.. Bm
115
116
117 a2=micros();
118 k=chord;
119 if(k==0){Serial.print('C');Serial.println(chord_out);}
120 if(k==1){Serial.print('C');Serial.print('#');Serial.println(chord_out);}
121 if(k==2){Serial.print('D');Serial.println(chord_out);}
122 if(k==3){Serial.print('D');Serial.print('#');Serial.println(chord_out);}
123 if(k==4){Serial.print('E');Serial.println(chord_out);}
124 if(k==5){Serial.print('F');Serial.println(chord_out);}
125 if(k==6){Serial.print('F');Serial.print('#');Serial.println(chord_out);}
126 if(k==7){Serial.print('G');Serial.println(chord_out);}
127 if(k==8){Serial.print('G');Serial.print('#');Serial.println(chord_out);}
128 if(k==9){Serial.print('A');Serial.println(chord_out);}
129 if(k==10){Serial.print('A');Serial.print('#');Serial.println(chord_out);}
130 if(k==11){Serial.print('B');Serial.println(chord_out);}
131 }
132
133}
134
135//-----------------------------FFT Function----------------------------------------------//
136// Documentation on EasyFFT:https://www.instructables.com/member/abhilash_patel/instructables/
137// EasyFFT code optimised for 128 sample size to reduce mamory consumtion
138
139float FFT(byte N,float Frequency)
140{
141byte data[8]={1,2,4,8,16,32,64,128};
142int a,c1,f,o,x;
143a=N;
144
145 for(int i=0;i<8;i++) //calculating the levels
146 { if(data[i]<=a){o=i;} }
147 o=7;
148byte in_ps[data[o]]={}; //input for sequencing
149float out_r[data[o]]={}; //real part of transform
150float out_im[data[o]]={}; //imaginory part of transform
151
152x=0;
153 for(int b=0;b<o;b++) // bit reversal
154 {
155 c1=data[b];
156 f=data[o]/(c1+c1);
157 for(int j=0;j<c1;j++)
158 {
159 x=x+1;
160 in_ps[x]=in_ps[j]+f;
161 }
162 }
163
164 for(int i=0;i<data[o];i++) // update input array as per bit reverse order
165 {
166 if(in_ps[i]<a)
167 {out_r[i]=in[in_ps[i]];}
168 if(in_ps[i]>a)
169 {out_r[i]=in[in_ps[i]-a];}
170 }
171
172
173int i10,i11,n1;
174float e,c,s,tr,ti;
175
176 for(int i=0;i<o;i++) //fft
177 {
178 i10=data[i]; // overall values of sine cosine
179 i11=data[o]/data[i+1]; // loop with similar sine cosine
180 e=6.283/data[i+1];
181 e=0-e;
182 n1=0;
183
184 for(int j=0;j<i10;j++)
185 {
186 c=cos(e*j);
187 s=sin(e*j);
188 n1=j;
189
190 for(int k=0;k<i11;k++)
191 {
192 tr=c*out_r[i10+n1]-s*out_im[i10+n1];
193 ti=s*out_r[i10+n1]+c*out_im[i10+n1];
194
195 out_r[n1+i10]=out_r[n1]-tr;
196 out_r[n1]=out_r[n1]+tr;
197
198 out_im[n1+i10]=out_im[n1]-ti;
199 out_im[n1]=out_im[n1]+ti;
200
201 n1=n1+i10+i10;
202 }
203 }
204 }
205
206/*
207for(int i=0;i<data[o];i++)
208{
209Serial.print(out_r[i]);
210Serial.print("\ "); // uncomment to print RAW o/p
211Serial.print(out_im[i]); Serial.println("i");
212}
213*/
214
215//---> here onward out_r contains amplitude and our_in conntains frequency (Hz)
216 for(int i=0;i<data[o-1];i++) // getting amplitude from compex number
217 {
218 out_r[i]=sqrt((out_r[i]*out_r[i])+(out_im[i]*out_im[i])); // to increase the speed delete sqrt
219 out_im[i]=(i*Frequency)/data[o];
220 /*
221 Serial.print(out_im[i],2); Serial.print("Hz");
222 Serial.print("\ "); // uncomment to print freuency bin
223 Serial.println(out_r[i]);
224 */
225 }
226
227x=0; // peak detection
228 for(int i=1;i<data[o-1]-1;i++)
229 {
230 if(out_r[i]>out_r[i-1] && out_r[i]>out_r[i+1])
231 {in_ps[x]=i; //in_ps array used for storage of peak number
232 x=x+1;}
233 }
234
235s=0;
236c=0;
237 for(int i=0;i<x;i++) // re arraange as per magnitude
238 {
239 for(int j=c;j<x;j++)
240 {
241 if(out_r[in_ps[i]]<out_r[in_ps[j]])
242 {s=in_ps[i];
243 in_ps[i]=in_ps[j];
244 in_ps[j]=s;}
245 }
246 c=c+1;
247 }
248
249 for(int i=0;i<8;i++) // updating f_peak array (global variable)with descending order
250 {
251 // f_peaks[i]=out_im[in_ps[i]];Serial.println(f_peaks[i]);
252 f_peaks[i]=(out_im[in_ps[i]-1]*out_r[in_ps[i]-1]+out_im[in_ps[i]]*out_r[in_ps[i]]+out_im[in_ps[i]+1]*out_r[in_ps[i]+1])
253 /(out_r[in_ps[i]-1]+out_r[in_ps[i]]+out_r[in_ps[i]+1]);
254 // Serial.println(f_peaks[i]);
255 }
256}
257
258//------------------------------------------------------------------------------------//

Note detection program

arduino

This program will print the name of the detected note. Refer to the comment in the code for clarification.

1//---------------------------------------------------------------------------//
2int in[128];
3byte NoteV[13]={8,23,40,57,76,96,116,138,162,187,213,241,255}; //data for note detection based on frequency
4float f_peaks[5]; // top 5 frequencies peaks in descending order
5int Mic_pin;
6//---------------------------------------------------------------------------//
7
8
9void setup() {
10Serial.begin(250000);
11Mic_pin = A7; // change as per Microphone pin
12}
13
14
15void loop()
16{
17 Tone_det();
18
19}
20
21
22//-----------------------------Tone Detection Function----------------------------------------------//
23// Documentation on Tone_detection:https://www.instructables.com/member/abhilash_patel/instructables/
24// Code Written By: Abhilash Patel
25// Contact: abhilashpatel121@gmail.com
26// This code written for arduino Nano board (should also work for UNO)
27// This code won't work for any board having RAM less than 2kb,
28
29
30
31void Tone_det()
32{ long unsigned int a1,b,a2;
33 float a;
34 float sum1=0,sum2=0;
35 float sampling;
36 a1=micros();
37 for(int i=0;i<128;i++)
38 {
39 a=analogRead(Mic_pin)-500; //rough zero shift
40 //utilising time between two sample for windowing & amplitude calculation
41 sum1=sum1+a; //to average value
42 sum2=sum2+a*a; // to RMS value
43 a=a*(sin(i*3.14/128)*sin(i*3.14/128)); // Hann window
44 in[i]=10*a; // scaling for float to int conversion
45 delayMicroseconds(195); // based on operation frequency range
46 }
47b=micros();
48
49sum1=sum1/128; // Average amplitude
50sum2=sqrt(sum2/128); // RMS
51sampling= 128000000/(b-a1); // real time sampling frequency
52
53//for very low or no amplitude, this code wont start
54//it takes very small aplitude of sound to initiate for value sum2-sum1>3,
55//change sum2-sum1 threshold based on requirement
56if(sum2-sum1>3){
57 FFT(128,sampling);
58 //EasyFFT based optimised FFT code,
59 //this code updates f_peaks array with 5 most dominent frequency in descending order
60
61 for(int i=0;i<12;i++){in[i]=0;} // utilising in[] array for further calculation
62
63int j=0,k=0; //below loop will convert frequency value to note
64 for(int i=0; i<5;i++)
65 {
66 if(f_peaks[i]>1040){f_peaks[i]=0;}
67 if(f_peaks[i]>=65.4 && f_peaks[i]<=130.8) {f_peaks[i]=255*((f_peaks[i]/65.4)-1);}
68 if(f_peaks[i]>=130.8 && f_peaks[i]<=261.6) {f_peaks[i]=255*((f_peaks[i]/130.8)-1);}
69 if(f_peaks[i]>=261.6 && f_peaks[i]<=523.25){f_peaks[i]=255*((f_peaks[i]/261.6)-1);}
70 if(f_peaks[i]>=523.25 && f_peaks[i]<=1046) {f_peaks[i]=255*((f_peaks[i]/523.25)-1);}
71 if(f_peaks[i]>=1046 && f_peaks[i]<=2093) {f_peaks[i]=255*((f_peaks[i]/1046)-1);}
72 if(f_peaks[i]>255){f_peaks[i]=254;}
73 j=1;k=0;
74
75 while(j==1)
76 {
77 if(f_peaks[i]<NoteV[k]){f_peaks[i]=k;j=0;}
78 k++; // a note with max peaks (harmonic) with aplitude priority is selected
79 if(k>15){j=0;}
80 }
81
82 if(f_peaks[i]==12){f_peaks[i]=0;}
83 k=f_peaks[i];
84 in[k]=in[k]+(5-i);
85 }
86
87k=0;j=0;
88 for(int i=0;i<12;i++)
89 {
90 if(k<in[i]){k=in[i];j=i;} //Max value detection
91 }
92 // Note print
93 // if you need to use note value for some application, use of note number recomendded
94 // where, 0=c;1=c#,2=D;3=D#;.. 11=B;
95 //a2=micros(); // time check
96 k=j;
97 if(k==0) {Serial.println('C');}
98 if(k==1) {Serial.print('C');Serial.println('#');}
99 if(k==2) {Serial.println('D');}
100 if(k==3) {Serial.print('D');Serial.println('#');}
101 if(k==4) {Serial.println('E');}
102 if(k==5) {Serial.println('F');}
103 if(k==6) {Serial.print('F');Serial.println('#');}
104 if(k==7) {Serial.println('G');}
105 if(k==8) {Serial.print('G');Serial.println('#');}
106 if(k==9) {Serial.println('A');}
107 if(k==10){Serial.print('A');Serial.println('#');}
108 if(k==11){Serial.println('B');}
109 }
110}
111
112//-----------------------------FFT Function----------------------------------------------//
113// Documentation on EasyFFT:https://www.instructables.com/member/abhilash_patel/instructables/
114// EasyFFT code optimised for 128 sample size to reduce mamory consumtion
115
116float FFT(byte N,float Frequency)
117{
118byte data[8]={1,2,4,8,16,32,64,128};
119int a,c1,f,o,x;
120a=N;
121
122 for(int i=0;i<8;i++) //calculating the levels
123 { if(data[i]<=a){o=i;} }
124 o=7;
125byte in_ps[data[o]]={}; //input for sequencing
126float out_r[data[o]]={}; //real part of transform
127float out_im[data[o]]={}; //imaginory part of transform
128
129x=0;
130 for(int b=0;b<o;b++) // bit reversal
131 {
132 c1=data[b];
133 f=data[o]/(c1+c1);
134 for(int j=0;j<c1;j++)
135 {
136 x=x+1;
137 in_ps[x]=in_ps[j]+f;
138 }
139 }
140
141 for(int i=0;i<data[o];i++) // update input array as per bit reverse order
142 {
143 if(in_ps[i]<a)
144 {out_r[i]=in[in_ps[i]];}
145 if(in_ps[i]>a)
146 {out_r[i]=in[in_ps[i]-a];}
147 }
148
149int i10,i11,n1;
150float e,c,s,tr,ti;
151
152 for(int i=0;i<o;i++) //fft
153 {
154 i10=data[i]; // overall values of sine cosine
155 i11=data[o]/data[i+1]; // loop with similar sine cosine
156 e=6.283/data[i+1];
157 e=0-e;
158 n1=0;
159
160 for(int j=0;j<i10;j++)
161 {
162 c=cos(e*j);
163 s=sin(e*j);
164 n1=j;
165
166 for(int k=0;k<i11;k++)
167 {
168 tr=c*out_r[i10+n1]-s*out_im[i10+n1];
169 ti=s*out_r[i10+n1]+c*out_im[i10+n1];
170
171 out_r[n1+i10]=out_r[n1]-tr;
172 out_r[n1]=out_r[n1]+tr;
173
174 out_im[n1+i10]=out_im[n1]-ti;
175 out_im[n1]=out_im[n1]+ti;
176
177 n1=n1+i10+i10;
178 }
179 }
180 }
181
182//---> here onward out_r contains amplitude and our_in conntains frequency (Hz)
183 for(int i=0;i<data[o-1];i++) // getting amplitude from compex number
184 {
185 out_r[i]=sqrt((out_r[i]*out_r[i])+(out_im[i]*out_im[i])); // to increase the speed delete sqrt
186 out_im[i]=(i*Frequency)/data[o];
187 /*
188 Serial.print(out_im[i],2); Serial.print("Hz");
189 Serial.print("\ "); // uncomment to print freuency bin
190 Serial.println(out_r[i]);
191 */
192 }
193
194x=0; // peak detection
195 for(int i=1;i<data[o-1]-1;i++)
196 {
197 if(out_r[i]>out_r[i-1] && out_r[i]>out_r[i+1])
198 {in_ps[x]=i; //in_ps array used for storage of peak number
199 x=x+1;}
200 }
201
202s=0;
203c=0;
204 for(int i=0;i<x;i++) // re arraange as per magnitude
205 {
206 for(int j=c;j<x;j++)
207 {
208 if(out_r[in_ps[i]]<out_r[in_ps[j]])
209 {s=in_ps[i];
210 in_ps[i]=in_ps[j];
211 in_ps[j]=s;}
212 }
213 c=c+1;
214 }
215
216 for(int i=0;i<5;i++) // updating f_peak array (global variable)with descending order
217 {
218 f_peaks[i]=(out_im[in_ps[i]-1]*out_r[in_ps[i]-1]+out_im[in_ps[i]]*out_r[in_ps[i]]+out_im[in_ps[i]+1]*out_r[in_ps[i]+1])
219 /(out_r[in_ps[i]-1]+out_r[in_ps[i]]+out_r[in_ps[i]+1]);
220 }
221}
222
223//------------------------------------------------------------------------------------//

Chord detection program

arduino

1//---------------------------------------------------------------------------//
2int in[128];
3byte NoteV[13]={8,23,40,57,76,96,116,138,162,187,213,241,255};
4float f_peaks[8]; // top 8 frequencies peaks in descending order
5//---------------------------------------------------------------------------//
6
7
8void setup() {
9Serial.begin(250000);
10}
11
12
13void loop()
14{
15 Chord_det();
16
17}
18
19
20//-----------------------------Chord Detection Function----------------------------------------------//
21// Documentation on Chord_detection:https://www.instructables.com/member/abhilash_patel/instructables/
22// Code Written By: Abhilash Patel
23// Contact: abhilashpatel121@gmail.com
24// this code written for arduino Nano board (should also work for UNO) or better board
25// this code won't work for any board having RAM less than 2kb,
26// More accurate detection can be carried out on more powerful borad by increasing sample size
27
28
29
30void Chord_det()
31{
32 long unsigned int a1,b,a2;
33 float a;
34 float sum1=0,sum2=0;
35 float sampling;
36 a1=micros();
37 for(int i=0;i<128;i++)
38 {
39 a=analogRead(A7)-500; //rough zero shift
40 //utilising time between two sample for windowing & amplitude calculation
41 sum1=sum1+a; //to average value
42 sum2=sum2+a*a; // to RMS value
43 a=a*(sin(i*3.14/128)*sin(i*3.14/128)); // Hann window
44 in[i]=4*a; // scaling for float to int conversion
45 delayMicroseconds(195); // based on operation frequency range
46 }
47b=micros();
48sum1=sum1/128; //average amplitude
49sum2=sqrt(sum2/128); //RMS amplitude
50sampling= 128000000/(b-a1); // real time sampling frequency
51
52//for very low or no amplitude, this code wont start
53//it takes very small aplitude of sound to initiate for value sum2-sum1>3,
54//change sum2-sum1 threshold based on requirement
55if(sum2-sum1>3){
56 FFT(128,sampling); //EasyFFT based optimised FFT code
57
58
59 for(int i=0;i<12;i++){in[i]=0;}
60
61int j=0,k=0; //below loop will convert frequency value to note
62 for(int i=0; i<8;i++)
63 {
64 if(f_peaks[i]>1040){f_peaks[i]=0;}
65 if(f_peaks[i]>=65.4 && f_peaks[i]<=130.8) {f_peaks[i]=255*((f_peaks[i]/65.4)-1);}
66 if(f_peaks[i]>=130.8 && f_peaks[i]<=261.6) {f_peaks[i]=255*((f_peaks[i]/130.8)-1);}
67 if(f_peaks[i]>=261.6 && f_peaks[i]<=523.25){f_peaks[i]=255*((f_peaks[i]/261.6)-1);}
68 if(f_peaks[i]>=523.25 && f_peaks[i]<=1046) {f_peaks[i]=255*((f_peaks[i]/523.25)-1);}
69 if(f_peaks[i]>=1046 && f_peaks[i]<=2093) {f_peaks[i]=255*((f_peaks[i]/1046)-1);}
70 if(f_peaks[i]>255){f_peaks[i]=254;}
71 j=1;k=0;
72 while(j==1)
73 {
74 if(f_peaks[i]<=NoteV[k]){f_peaks[i]=k;j=0;}
75 k++; // a note with max peaks (harmonic) with aplitude priority is selected
76 if(k>15){j=0;}
77 }
78
79 if(f_peaks[i]==12){f_peaks[i]=0;}
80 k=f_peaks[i];
81 in[k]=in[k]+(8-i);
82 }
83
84k=0;j=0;
85 for(int i=0;i<12;i++)
86 {
87 if(k<in[i]){k=in[i];j=i;} //Max value detection
88 }
89
90 for(int i=0;i<8;i++)
91 {
92 in[12+i]=in[i];
93 }
94
95for (int i=0;i<12;i++)
96{
97 in[20+i]=in[i]*in[i+4]*in[i+7];
98 in[32+i]=in[i]*in[i+3]*in[i+7]; //all chord check
99}
100
101
102for (int i=0;i<24;i++)
103{
104in[i]=in[i+20];
105if(k<in[i]){k=in[i];j=i;} // picking chord with max possiblity
106}
107char chord_out;
108int chord=j;
109if(chord>11){chord=chord-12;chord_out='m';} //Mojor-minor check
110else{chord_out=' ';}
111
112// Here "chord" variable has value of detected chord,
113// 0-11 defines all majot chord from C,C#,D,D#,.. B
114//12-23 defines all minor chord from Cm,C#m,Dm,D#m,.. Bm
115
116
117 a2=micros();
118 k=chord;
119 if(k==0){Serial.print('C');Serial.println(chord_out);}
120 if(k==1){Serial.print('C');Serial.print('#');Serial.println(chord_out);}
121 if(k==2){Serial.print('D');Serial.println(chord_out);}
122 if(k==3){Serial.print('D');Serial.print('#');Serial.println(chord_out);}
123 if(k==4){Serial.print('E');Serial.println(chord_out);}
124 if(k==5){Serial.print('F');Serial.println(chord_out);}
125 if(k==6){Serial.print('F');Serial.print('#');Serial.println(chord_out);}
126 if(k==7){Serial.print('G');Serial.println(chord_out);}
127 if(k==8){Serial.print('G');Serial.print('#');Serial.println(chord_out);}
128 if(k==9){Serial.print('A');Serial.println(chord_out);}
129 if(k==10){Serial.print('A');Serial.print('#');Serial.println(chord_out);}
130 if(k==11){Serial.print('B');Serial.println(chord_out);}
131 }
132
133}
134
135//-----------------------------FFT Function----------------------------------------------//
136// Documentation on EasyFFT:https://www.instructables.com/member/abhilash_patel/instructables/
137// EasyFFT code optimised for 128 sample size to reduce mamory consumtion
138
139float FFT(byte N,float Frequency)
140{
141byte data[8]={1,2,4,8,16,32,64,128};
142int a,c1,f,o,x;
143a=N;
144
145 for(int i=0;i<8;i++) //calculating the levels
146 { if(data[i]<=a){o=i;} }
147 o=7;
148byte in_ps[data[o]]={}; //input for sequencing
149float out_r[data[o]]={}; //real part of transform
150float out_im[data[o]]={}; //imaginory part of transform
151
152x=0;
153 for(int b=0;b<o;b++) // bit reversal
154 {
155 c1=data[b];
156 f=data[o]/(c1+c1);
157 for(int j=0;j<c1;j++)
158 {
159 x=x+1;
160 in_ps[x]=in_ps[j]+f;
161 }
162 }
163
164 for(int i=0;i<data[o];i++) // update input array as per bit reverse order
165 {
166 if(in_ps[i]<a)
167 {out_r[i]=in[in_ps[i]];}
168 if(in_ps[i]>a)
169 {out_r[i]=in[in_ps[i]-a];}
170 }
171
172
173int i10,i11,n1;
174float e,c,s,tr,ti;
175
176 for(int i=0;i<o;i++) //fft
177 {
178 i10=data[i]; // overall values of sine cosine
179 i11=data[o]/data[i+1]; // loop with similar sine cosine
180 e=6.283/data[i+1];
181 e=0-e;
182 n1=0;
183
184 for(int j=0;j<i10;j++)
185 {
186 c=cos(e*j);
187 s=sin(e*j);
188 n1=j;
189
190 for(int k=0;k<i11;k++)
191 {
192 tr=c*out_r[i10+n1]-s*out_im[i10+n1];
193 ti=s*out_r[i10+n1]+c*out_im[i10+n1];
194
195 out_r[n1+i10]=out_r[n1]-tr;
196 out_r[n1]=out_r[n1]+tr;
197
198 out_im[n1+i10]=out_im[n1]-ti;
199 out_im[n1]=out_im[n1]+ti;
200
201 n1=n1+i10+i10;
202 }
203 }
204 }
205
206/*
207for(int i=0;i<data[o];i++)
208{
209Serial.print(out_r[i]);
210Serial.print("\ "); // uncomment to print RAW o/p
211Serial.print(out_im[i]); Serial.println("i");
212}
213*/
214
215//---> here onward out_r contains amplitude and our_in conntains frequency (Hz)
216 for(int i=0;i<data[o-1];i++) // getting amplitude from compex number
217 {
218 out_r[i]=sqrt((out_r[i]*out_r[i])+(out_im[i]*out_im[i])); // to increase the speed delete sqrt
219 out_im[i]=(i*Frequency)/data[o];
220 /*
221 Serial.print(out_im[i],2); Serial.print("Hz");
222 Serial.print("\ "); // uncomment to print freuency bin
223 Serial.println(out_r[i]);
224 */
225 }
226
227x=0; // peak detection
228 for(int i=1;i<data[o-1]-1;i++)
229 {
230 if(out_r[i]>out_r[i-1] && out_r[i]>out_r[i+1])
231 {in_ps[x]=i; //in_ps array used for storage of peak number
232 x=x+1;}
233 }
234
235s=0;
236c=0;
237 for(int i=0;i<x;i++) // re arraange as per magnitude
238 {
239 for(int j=c;j<x;j++)
240 {
241 if(out_r[in_ps[i]]<out_r[in_ps[j]])
242 {s=in_ps[i];
243 in_ps[i]=in_ps[j];
244 in_ps[j]=s;}
245 }
246 c=c+1;
247 }
248
249 for(int i=0;i<8;i++) // updating f_peak array (global variable)with descending order
250 {
251 // f_peaks[i]=out_im[in_ps[i]];Serial.println(f_peaks[i]);
252 f_peaks[i]=(out_im[in_ps[i]-1]*out_r[in_ps[i]-1]+out_im[in_ps[i]]*out_r[in_ps[i]]+out_im[in_ps[i]+1]*out_r[in_ps[i]+1])
253 /(out_r[in_ps[i]-1]+out_r[in_ps[i]]+out_r[in_ps[i]+1]);
254 // Serial.println(f_peaks[i]);
255 }
256}
257
258//------------------------------------------------------------------------------------//

Downloadable files

Connection

The signal pin of the module should be connected to any analog pin of the arduino. it was necessary to make changes to the code accordingly.

Connection




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.

Easy remote control

Remotely control an LED with ease. Devices and components Arduino Nano 33 BLE with headers Box 525 Resistors precision 1% - 17 values Breadb...