Inhoudsopgave:
2025 Auteur: John Day | [email protected]. Laatst gewijzigd: 2025-01-13 06:57
Verbaas je vrienden en familie met dit project dat de door een instrument gespeelde noot detecteert. Dit project zal de geschatte frequentie weergeven, evenals de muzieknoot die wordt gespeeld op een elektronisch toetsenbord, piano-app of een ander instrument.
Details
Voor dit project wordt de analoge uitgang van de geluidsmodule-detector naar de A0 analoge ingang van de Arduino Uno gestuurd. Het analoge signaal wordt gesampled en gekwantiseerd (gedigitaliseerd). Autocorrelatie, weging en afstemmingscode wordt gebruikt om de grondfrequentie te vinden met behulp van de eerste 3 perioden. De geschatte grondfrequentie wordt vervolgens vergeleken met frequenties in het bereik van de octaven 3, 4 en 5 om de dichtstbijzijnde muzieknootfrequentie te bepalen. Ten slotte wordt de geraden noot voor de dichtstbijzijnde frequentie op het scherm afgedrukt.
Opmerking: deze instructable richt zich alleen op het bouwen van het project. Ga voor meer informatie over de details en ontwerprechtvaardigingen naar deze link: Meer informatie
Benodigdheden
- (1) Arduino Uno (of Genuino Uno)
- (1) DEVMO-microfoonsensor Compatibel met geluidsdetectiemodule met hoge gevoeligheid
- (1) Soldeerloze Breadboard
- (1) USB-A naar B-kabel
- Doorverbindingsdraden
- Muzikale bron (piano, keyboard of paino app met speakers)
- (1) Computer of laptop
Stap 1: Construeer de hardware voor de muzieknootdetector
Gebruik een Arduino Uno, verbindingsdraden, een soldeerloze breadboard en een DEVMO-microfoonsensor High Sensitivity Sound Detection Module (of vergelijkbaar) om het circuit te bouwen dat in deze afbeelding wordt getoond
Stap 2: Programmeer de muzieknootdetector
Voeg in de Arduino IDE de volgende code toe.
gistfile1.txt
/* |
Bestands-/schetsnaam: MusicalNoteDetector |
Versie nr.: v1.0 Gemaakt op 7 juni 2020 |
Oorspronkelijke auteur: Clyde A. Lettsome, PhD, PE, MEM |
Beschrijving: deze code/schets geeft de frequentie bij benadering weer, evenals de muzieknoot die wordt gespeeld op een elektronisch toetsenbord of piano-app. Voor dit project is de analoge uitgang van de |
geluidsmodule detector wordt verzonden naar de A0 analoge ingang van de Arduino Uno. Het analoge signaal wordt gesampled en gekwantiseerd (gedigitaliseerd). Autocorrelatie, weging en afstemmingscode wordt gebruikt om: |
vind de grondfrequentie met behulp van de eerste 3 perioden. De geschatte grondfrequentie wordt vervolgens vergeleken met frequenties in het bereik van octaven 3, 4 en 5 om de dichtstbijzijnde musical te bepalen |
noot frequentie. Ten slotte wordt de geraden noot voor de dichtstbijzijnde frequentie op het scherm afgedrukt. |
Licentie: Dit programma is gratis software; u kunt het opnieuw distribueren en/of wijzigen onder de voorwaarden van de GNU General Public License (GPL) versie 3, of een latere |
versie van uw keuze, zoals gepubliceerd door de Free Software Foundation. |
Opmerkingen: Copyright (c) 2020 door C. A. Lettsome Services, LLC |
Ga voor meer informatie naar |
*/ |
#define MONSTERS 128 //Max 128 voor Arduino Uno. |
#define SAMPLING_FREQUENCY 2048 //Fs = Gebaseerd op Nyquist, moet 2 keer de hoogst verwachte frequentie zijn. |
#define OFFSETSAMPLES 40 //gebruikt voor kalibratiedoeleinden |
#define TUNER -3 //Aanpassen tot C3 130.50. is |
float bemonsteringPeriode; |
niet-ondertekende lange microSeconden; |
int X[VOORBEELDEN]; // maak een vector met de grootte SAMPLES om echte waarden vast te houden |
float autoCorr[VOORBEELDEN]; // maak een vector met de grootte SAMPLES om denkbeeldige waarden vast te houden |
float opgeslagenNoteFreq [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]; // maak offset vector |
int avgOffSet; // maak offset vector |
int i, k, periodEnd, periodBegin, period, adjuster, noteLocation, octaveRange; |
float maxValue, minValue; |
lange som; |
int drempel = 0; |
int aantalCycles = 0; |
float signaalFrequentie, signaalFrequentie2, signaalFrequentie3, signaalFrequentieGuess, totaal; |
byte state_machine = 0; |
int samplesPerPeriode = 0; |
ongeldige setup() |
{ |
Serieel.begin(115200); //115200 Baudrate voor de seriële monitor |
} |
lege lus() |
{ |
//***************************************************************** |
// Kalibratie Sectie |
//***************************************************************** |
Serial.println("Kalaberen. Speel geen noten tijdens het kalibreren."); |
voor (i = 0; i < OFFSETSAMPLES; i++) |
{ |
offSet = analoogRead(0); // Leest de waarde van analoge pin 0 (A0), kwantiseert deze en slaat deze op als een echte term. |
//Serial.println(offSet); // gebruik dit om de geluidsdetectiemodule in te stellen op ongeveer de helft of 512 als er geen geluid wordt afgespeeld. |
sumOffSet = sumOffSet + offSet; |
} |
samplesPerPeriode = 0; |
maxWaarde = 0; |
//***************************************************************** |
// Bereid je voor om invoer van A0. te accepteren |
//***************************************************************** |
avgOffSet = round(sumOffSet / OFFSETSAMPLES); |
Serial.println("Aftellen."); |
vertraging (1000); // pauze voor 1 seconden |
Serieel.println("3"); |
vertraging (1000); // pauze voor 1 seconden |
Serieel.println("2"); |
vertraging (1000); // pauze voor 1 |
Serieel.println("1"); |
vertraging (1000); // pauze voor 1 seconden |
Serial.println("Speel je notitie af!"); |
vertraging (250); // pauzeer 1/4 seconde voor reactietijd |
//***************************************************************** |
// Verzamel SAMPLES-samples van A0 met sampleperiode van samplingPeriod |
//***************************************************************** |
samplingPeriod = 1.0 / SAMPLING_FREQUENCY; //Periode in microseconden |
voor (i = 0; i < MONSTERS; i++) |
{ |
microSeconden = micros(); // Geeft het aantal microseconden terug sinds het Arduino-bord het huidige script begon uit te voeren. |
X = analoog lezen(0); // Leest de waarde van analoge pin 0 (A0), kwantiseert deze en slaat deze op als een echte term. |
/* resterende wachttijd tussen samples indien nodig in seconden */ |
while (micros() < (microSeconden + (samplingPeriod * 1000000))) |
{ |
//niets doen, gewoon wachten |
} |
} |
//***************************************************************** |
// Autocorrelatiefunctie |
//***************************************************************** |
voor (i = 0; i < MONSTERS; i++) //i=vertraging |
{ |
som = 0; |
for (k = 0; k <SAMPLES - i; k++) // Match signaal met vertraagd signaal |
{ |
som = som + (((X[k]) - avgOffSet) * ((X[k + i]) - avgOffSet)); //X[k] is het signaal en X[k+i] is de vertraagde versie |
} |
autoCorr = som / MONSTERS; |
// First Peak Detect State Machine |
if (state_machine==0 && i == 0) |
{ |
thresh = autoCorr * 0,5; |
staat_machine = 1; |
} |
else if (state_machine == 1 && i>0 && thresh 0) //state_machine=1, zoek 1 periode voor het gebruik van de eerste cyclus |
{ |
maxValue = autoCorr; |
} |
else if (state_machine == 1&& i>0 && thresh < autoCorr[i-1] && maxValue == autoCorr[i-1] && (autoCorr-autoCorr[i-1])<=0) |
{ |
periodeBegin = i-1; |
staat_machine = 2; |
aantalCycles = 1; |
samplesPerPeriod = (periodeBegin - 0); |
periode = samplesPerPeriode; |
regelaar = TUNER+(50.04 * exp(-0.102 * samplesPerPeriod)); |
signalFrequency = ((SAMPLING_FREQUENCY) / (samplesPerPeriod))-regelaar; // f = fs/N |
} |
else if (state_machine == 2 && i>0 && thresh 0) //state_machine=2, zoek 2 perioden voor de 1e en 2e cyclus |
{ |
maxValue = autoCorr; |
} |
else if (state_machine == 2&& i>0 && thresh < autoCorr[i-1] && maxValue == autoCorr[i-1] && (autoCorr-autoCorr[i-1])<=0) |
{ |
periodeEnd = i-1; |
staat_machine = 3; |
aantalCycles = 2; |
samplesPerPeriod = (periodEnd - 0); |
signalFrequency2 = ((numOfCycles*SAMPLING_FREQUENCY) / (samplesPerPeriod))-aanpasser; // f = (2*fs)/(2*N) |
maxWaarde = 0; |
} |
else if (state_machine == 3 && i>0 && thresh 0) //state_machine=3, zoek 3 perioden voor de 1e, 2e en 3e cyclus |
{ |
maxValue = autoCorr; |
} |
else if (state_machine == 3&& i>0 && thresh < autoCorr[i-1] && maxValue == autoCorr[i-1] && (autoCorr-autoCorr[i-1])<=0) |
{ |
periodeEnd = i-1; |
staat_machine = 4; |
aantalCycles = 3; |
samplesPerPeriod = (periodEnd - 0); |
signalFrequency3 = ((numOfCycles*SAMPLING_FREQUENCY) / (samplesPerPeriod))-aanpasser; // f = (3*fs)/(3*N) |
} |
} |
//***************************************************************** |
//Resultaatanalyse |
//***************************************************************** |
if (steekproevenPerPeriode == 0) |
{ |
Serial.println("Hmm… ik weet het niet zeker. Probeer je me te misleiden?"); |
} |
anders |
{ |
// bereid de weegfunctie voor |
totaal = 0; |
if (signaalfrequentie !=0) |
{ |
totaal = 1; |
} |
if (signaalfrequentie2 !=0) |
{ |
totaal = totaal + 2; |
} |
if (signaalfrequentie3 !=0) |
{ |
totaal = totaal + 3; |
} |
// bereken de frequentie met behulp van de weegfunctie |
signaalfrequentieGuess = ((1/totaal) * signaalfrequentie) + ((2/totaal) * signaalfrequentie2) + ((3/totaal) * signaalfrequentie3); // zoek een gewogen frequentie |
Serial.print("De noot die je speelde is ongeveer "); |
Serial.print(signaalFrequencyGuess); // Druk de schatting van de frequentie af. |
Serial.println ("Hz."); |
// vind octaafbereik op basis van de gok |
octaafBereik=3; |
while (!(signalFrequencyGuess >= opgeslagenNoteFreq[0]-7 && signalFrequencyGuess <= opgeslagenNoteFreq[11]+7)) |
{ |
voor(i = 0; ik < 12; i++) |
{ |
opgeslagenNoteFreq = 2 * opgeslagenNoteFreq; |
} |
octaveRange++; |
} |
// Vind de dichtstbijzijnde notitie |
minWaarde = 10000000; |
opmerkingLocatie = 0; |
voor (i = 0; ik < 12; i++) |
{ |
if(minValue> abs(signalFrequencyGuess-storedNoteFreq)) |
{ |
minValue = abs(signaalFrequencyGuess-storedNoteFreq); |
opmerkingLocatie = ik; |
} |
} |
// Druk de notitie af |
Serial.print("Ik denk dat je gespeeld hebt "); |
if(noteLocation==0) |
{ |
Serial.print("C"); |
} |
anders if(noteLocation==1) |
{ |
Serieel.print("C#"); |
} |
anders if(noteLocation==2) |
{ |
Serial.print("D"); |
} |
anders if(noteLocation==3) |
{ |
Serieel.print("D#"); |
} |
anders if(noteLocation==4) |
{ |
Serial.print("E"); |
} |
anders if(noteLocation==5) |
{ |
Serieel.print("F"); |
} |
anders if(noteLocation==6) |
{ |
Serieel.print("F#"); |
} |
anders if(noteLocation==7) |
{ |
Serial.print("G"); |
} |
anders if(noteLocation==8) |
{ |
Serial.print("G#"); |
} |
anders if(noteLocation==9) |
{ |
Serial.print("A"); |
} |
anders if(noteLocation==10) |
{ |
Serial.print("A#"); |
} |
anders if(noteLocation==11) |
{ |
Serial.print("B"); |
} |
Serial.println(octaafbereik); |
} |
//***************************************************************** |
//Stop hier. Druk op de reset-knop op Arduino om opnieuw op te starten |
//***************************************************************** |
terwijl (1); |
} |
bekijk rawgistfile1.txt gehost met ❤ door GitHub
Stap 3: Stel de muzieknootdetector in
Verbind de Arduino Uno met de pc met de code geschreven of geladen in de Arduino IDE. Compileer en upload de code naar de Arduino. Plaats het circuit dicht bij de muziekbron. Opmerking: in de introductievideo gebruik ik een app die op een tablet is geïnstalleerd in combinatie met pc-luidsprekers als mijn muziekbron. Druk op de resetknop op het Arduino-bord en speel vervolgens een notitie op de muziekbron. Na een paar seconden geeft de muzieknootdetector de gespeelde noot en de frequentie ervan weer.