Musical Note Detector

Introduction: Musical Note Detector

About: See https://clydelettsome.com/blog/bio/ for bio

Amaze your friends and family with this project that detects the note played by an instrument. This project will display the approximate frequency as well as the musical note played on an electronic keyboard, piano app or any other instrument.

Details

For this project, the analog output from the sound module detector is sent to the A0 analog input of the Arduino Uno. The analog signal is sampled and quantized (digitized). Autocorrelation, weighting and tuning code is used to find fundamental frequency using the first 3 periods. The approximate fundamental frequency is then compared to frequencies in octaves 3, 4, and 5 range to determine the closest musical note frequency. Finally the guessed note for the closest frequency is printed to the screen.

Note: This instructable only focuses on how to build the project. For more information about the details and design justifications, please visit this link: More Information

Supplies

  • (1) Arduino Uno (or Genuino Uno)
  • (1) DEVMO Microphone Sensor High Sensitivity Sound Detection Module Compatible
  • (1) Solderless Breadboard
  • (1) USB-A to B Cable
  • Jumper wires
  • Musical source (piano, keyboard or paino app with speakers)
  • (1) Computer or laptop

Step 1: Construct the Hardware for the Musical Note Detector

Using an Arduino Uno, connection wires, a solderless breadboard and a DEVMO Microphone Sensor High Sensitivity Sound Detection Module (or similar) construct the circuit shown in this image

Step 2: Program the Musical Note Detector

In the Arduino IDE, add the following code.

gistfile1.txt

/*
File/Sketch Name: MusicalNoteDetector
Version No.: v1.0 Created 7 June, 2020
Original Author: Clyde A. Lettsome, PhD, PE, MEM
Description: This code/sketch displays the approximate frequency as well as the musical note played on an electronic keyboard or piano app. For this project, the analog output from the
sound module detector is sent to the A0 analog input of the Arduino Uno. The analog signal is sampled and quantized (digitized). Autocorrelation, weighting and tuning code is used to
find fundamental frequency using the first 3 periods. The approximate fundamental frequency is then compared to frequencies in octaves 3, 4, and 5 range to determine the closest musical
note frequency. Finally the guessed note for the closest frequency is printed to the screen.
License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL) version 3, or any later
version of your choice, as published by the Free Software Foundation.
Notes: Copyright (c) 2020 by C. A. Lettsome Services, LLC
For more information visit https://clydelettsome.com/blog/2020/06/07/my-weekend-project-musical-note-detector-using-an-arduino/
*/
#define SAMPLES 128 //Max 128 for Arduino Uno.
#define SAMPLING_FREQUENCY 2048 //Fs = Based on Nyquist, must be 2 times the highest expected frequency.
#define OFFSETSAMPLES 40 //used for calabrating purposes
#define TUNER -3 //Adjust until C3 is 130.50
float samplingPeriod;
unsigned long microSeconds;
int X[SAMPLES]; //create vector of size SAMPLES to hold real values
float autoCorr[SAMPLES]; //create vector of size SAMPLES to hold imaginary values
float storedNoteFreq[12] = {130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185, 196, 207.65, 220, 233.08, 246.94};
int sumOffSet = 0;
int offSet[OFFSETSAMPLES]; //create offset vector
int avgOffSet; //create offset vector
int i, k, periodEnd, periodBegin, period, adjuster, noteLocation, octaveRange;
float maxValue, minValue;
long sum;
int thresh = 0;
int numOfCycles = 0;
float signalFrequency, signalFrequency2, signalFrequency3, signalFrequencyGuess, total;
byte state_machine = 0;
int samplesPerPeriod = 0;
void setup()
{
Serial.begin(115200); //115200 Baud rate for the Serial Monitor
}
void loop()
{
//*****************************************************************
//Calabration Section
//*****************************************************************
Serial.println("Calabrating. Please do not play any notes during calabration.");
for (i = 0; i < OFFSETSAMPLES; i++)
{
offSet[i] = analogRead(0); //Reads the value from analog pin 0 (A0), quantize it and save it as a real term.
//Serial.println(offSet[i]); //use this to adjust the sound detection module to approximately half or 512 when no sound is played.
sumOffSet = sumOffSet + offSet[i];
}
samplesPerPeriod = 0;
maxValue = 0;
//*****************************************************************
//Prepare to accept input from A0
//*****************************************************************
avgOffSet = round(sumOffSet / OFFSETSAMPLES);
Serial.println("Counting down.");
delay(1000); //pause for 1 seconds
Serial.println("3");
delay(1000); //pause for 1 seconds
Serial.println("2");
delay(1000); //pause for 1
Serial.println("1");
delay(1000); //pause for 1 seconds
Serial.println("Play your note!");
delay(250); //pause for 1/4 second for reaction time
//*****************************************************************
//Collect SAMPLES samples from A0 with sample period of samplingPeriod
//*****************************************************************
samplingPeriod = 1.0 / SAMPLING_FREQUENCY; //Period in microseconds
for (i = 0; i < SAMPLES; i++)
{
microSeconds = micros(); //Returns the number of microseconds since the Arduino board began running the current script.
X[i] = analogRead(0); //Reads the value from analog pin 0 (A0), quantize it and save it as a real term.
/*remaining wait time between samples if necessary in seconds */
while (micros() < (microSeconds + (samplingPeriod * 1000000)))
{
//do nothing just wait
}
}
//*****************************************************************
//Autocorrelation Function
//*****************************************************************
for (i = 0; i < SAMPLES; i++) //i=delay
{
sum = 0;
for (k = 0; k < SAMPLES - i; k++) //Match signal with delayed signal
{
sum = sum + (((X[k]) - avgOffSet) * ((X[k + i]) - avgOffSet)); //X[k] is the signal and X[k+i] is the delayed version
}
autoCorr[i] = sum / SAMPLES;
// First Peak Detect State Machine
if (state_machine==0 && i == 0)
{
thresh = autoCorr[i] * 0.5;
state_machine = 1;
}
else if (state_machine == 1 && i>0 && thresh < autoCorr[i] && (autoCorr[i]-autoCorr[i-1])>0) //state_machine=1, find 1 period for using first cycle
{
maxValue = autoCorr[i];
}
else if (state_machine == 1&& i>0 && thresh < autoCorr[i-1] && maxValue == autoCorr[i-1] && (autoCorr[i]-autoCorr[i-1])<=0)
{
periodBegin = i-1;
state_machine = 2;
numOfCycles = 1;
samplesPerPeriod = (periodBegin - 0);
period = samplesPerPeriod;
adjuster = TUNER+(50.04 * exp(-0.102 * samplesPerPeriod));
signalFrequency = ((SAMPLING_FREQUENCY) / (samplesPerPeriod))-adjuster; // f = fs/N
}
else if (state_machine == 2 && i>0 && thresh < autoCorr[i] && (autoCorr[i]-autoCorr[i-1])>0) //state_machine=2, find 2 periods for 1st and 2nd cycle
{
maxValue = autoCorr[i];
}
else if (state_machine == 2&& i>0 && thresh < autoCorr[i-1] && maxValue == autoCorr[i-1] && (autoCorr[i]-autoCorr[i-1])<=0)
{
periodEnd = i-1;
state_machine = 3;
numOfCycles = 2;
samplesPerPeriod = (periodEnd - 0);
signalFrequency2 = ((numOfCycles*SAMPLING_FREQUENCY) / (samplesPerPeriod))-adjuster; // f = (2*fs)/(2*N)
maxValue = 0;
}
else if (state_machine == 3 && i>0 && thresh < autoCorr[i] && (autoCorr[i]-autoCorr[i-1])>0) //state_machine=3, find 3 periods for 1st, 2nd and 3rd cycle
{
maxValue = autoCorr[i];
}
else if (state_machine == 3&& i>0 && thresh < autoCorr[i-1] && maxValue == autoCorr[i-1] && (autoCorr[i]-autoCorr[i-1])<=0)
{
periodEnd = i-1;
state_machine = 4;
numOfCycles = 3;
samplesPerPeriod = (periodEnd - 0);
signalFrequency3 = ((numOfCycles*SAMPLING_FREQUENCY) / (samplesPerPeriod))-adjuster; // f = (3*fs)/(3*N)
}
}
//*****************************************************************
//Result Analysis
//*****************************************************************
if (samplesPerPeriod == 0)
{
Serial.println("Hmm..... I am not sure. Are you trying to trick me?");
}
else
{
//prepare the weighting function
total = 0;
if (signalFrequency !=0)
{
total = 1;
}
if(signalFrequency2 !=0)
{
total = total + 2;
}
if (signalFrequency3 !=0)
{
total = total + 3;
}
//calculate the frequency using the weighting function
signalFrequencyGuess = ((1/total) * signalFrequency) + ((2/total) * signalFrequency2) + ((3/total) * signalFrequency3); //find a weighted frequency
Serial.print("The note you played is approximately ");
Serial.print(signalFrequencyGuess); //Print the frequency guess.
Serial.println(" Hz.");
//find octave range based on the guess
octaveRange=3;
while (!(signalFrequencyGuess >= storedNoteFreq[0]-7 && signalFrequencyGuess <= storedNoteFreq[11]+7 ))
{
for(i = 0; i < 12; i++)
{
storedNoteFreq[i] = 2 * storedNoteFreq[i];
}
octaveRange++;
}
//Find the closest note
minValue = 10000000;
noteLocation = 0;
for (i = 0; i < 12; i++)
{
if(minValue> abs(signalFrequencyGuess-storedNoteFreq[i]))
{
minValue = abs(signalFrequencyGuess-storedNoteFreq[i]);
noteLocation = i;
}
}
//Print the note
Serial.print("I think you played ");
if(noteLocation==0)
{
Serial.print("C");
}
else if(noteLocation==1)
{
Serial.print("C#");
}
else if(noteLocation==2)
{
Serial.print("D");
}
else if(noteLocation==3)
{
Serial.print("D#");
}
else if(noteLocation==4)
{
Serial.print("E");
}
else if(noteLocation==5)
{
Serial.print("F");
}
else if(noteLocation==6)
{
Serial.print("F#");
}
else if(noteLocation==7)
{
Serial.print("G");
}
else if(noteLocation==8)
{
Serial.print("G#");
}
else if(noteLocation==9)
{
Serial.print("A");
}
else if(noteLocation==10)
{
Serial.print("A#");
}
else if(noteLocation==11)
{
Serial.print("B");
}
Serial.println(octaveRange);
}
//*****************************************************************
//Stop here. Hit reset button on Arduino to restart
//*****************************************************************
while (1);
}
view rawgistfile1.txt hosted with ❤ by GitHub

Step 3: Setup the Musical Note Detector

    Connect the Arduino Uno to the PC with the code written or loaded in the Arduino IDE. Compile and upload the code to the Arduino. Place the circuit close to the music source. Note: In the introduction video, I use an app installed on tablet in conjunction with PC speakers as my music source. Hit the reset button on the Arduino Board and then play a note on the music source. After a few seconds, the Musical Note Detector will display the note played and its frequency.

    Be the First to Share

      Recommendations

      • Maps Challenge

        Maps Challenge
      • Fandom Contest

        Fandom Contest
      • DIY Summer Camp Contest

        DIY Summer Camp Contest

      Comments