Tabla de contenido:
2025 Autor: John Day | [email protected]. Última modificación: 2025-01-13 06:57
Sorprenda a sus amigos y familiares con este proyecto que detecta la nota tocada por un instrumento. Este proyecto mostrará la frecuencia aproximada, así como la nota musical tocada en un teclado electrónico, aplicación de piano o cualquier otro instrumento.
Detalles
Para este proyecto, la salida analógica del detector del módulo de sonido se envía a la entrada analógica A0 del Arduino Uno. La señal analógica se muestrea y cuantifica (digitaliza). El código de autocorrelación, ponderación y sintonía se utiliza para encontrar la frecuencia fundamental utilizando los primeros 3 períodos. Luego, la frecuencia fundamental aproximada se compara con las frecuencias en el rango de octavas 3, 4 y 5 para determinar la frecuencia de nota musical más cercana. Finalmente, la nota adivinada para la frecuencia más cercana se imprime en la pantalla.
Nota: Este instructivo solo se enfoca en cómo construir el proyecto. Para obtener más información sobre los detalles y las justificaciones del diseño, visite este enlace: Más información
Suministros
- (1) Arduino Uno (o Genuino Uno)
- (1) Módulo de detección de sonido de alta sensibilidad con sensor de micrófono DEVMO Compatible
- (1) Placa de pruebas sin soldadura
- (1) Cable USB-A a B
- Cables de puente
- Fuente musical (piano, teclado o aplicación paino con altavoces)
- (1) Computadora o laptop
Paso 1: construya el hardware para el detector de notas musicales
Usando un Arduino Uno, cables de conexión, una placa de prueba sin soldadura y un Módulo de detección de sonido de alta sensibilidad con sensor de micrófono DEVMO (o similar) construya el circuito que se muestra en esta imagen
Paso 2: programe el detector de notas musicales
En el IDE de Arduino, agregue el siguiente código.
gistfile1.txt
/* |
Nombre de archivo / boceto: MusicalNoteDetector |
Número de versión: v1.0 Creado el 7 de junio de 2020 |
Autor original: Clyde A. Lettsome, PhD, PE, MEM |
Descripción: este código / boceto muestra la frecuencia aproximada, así como la nota musical tocada en un teclado electrónico o aplicación de piano. Para este proyecto, la salida analógica del |
El detector de módulo de sonido se envía a la entrada analógica A0 del Arduino Uno. La señal analógica se muestrea y cuantifica (digitaliza). El código de autocorrelación, ponderación y ajuste se utiliza para |
encuentre la frecuencia fundamental utilizando los primeros 3 períodos. Luego, la frecuencia fundamental aproximada se compara con las frecuencias en el rango de octavas 3, 4 y 5 para determinar la frecuencia musical más cercana. |
nota la frecuencia. Finalmente, la nota adivinada para la frecuencia más cercana se imprime en la pantalla. |
Licencia: este programa es software gratuito; puede redistribuirlo y / o modificarlo bajo los términos de la Licencia Pública General GNU (GPL) versión 3, o cualquier posterior |
versión de su elección, según lo publicado por la Free Software Foundation. |
Notas: Copyright (c) 2020 de C. A. Lettsome Services, LLC |
Para obtener más información, visite |
*/ |
#define SAMPLES 128 // Max 128 para Arduino Uno. |
#define SAMPLING_FREQUENCY 2048 // Fs = Basado en Nyquist, debe ser 2 veces la frecuencia más alta esperada. |
#define OFFSETSAMPLES 40 // utilizado con fines de calabrating |
#define TUNER -3 // Ajustar hasta que C3 sea 130.50 |
período de muestreo flotante; |
microSegundos largos sin firmar; |
int X [MUESTRAS]; // crea un vector de tamaño MUESTRAS para contener valores reales |
flotar autoCorr [MUESTRAS]; // crea un vector de tamaño MUESTRAS para contener valores imaginarios |
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]; // crear vector de compensación |
int avgOffSet; // crear vector de compensación |
int i, k, periodEnd, periodBegin, period, ajustador, noteLocation, octaveRange; |
float maxValue, minValue; |
suma larga |
int thresh = 0; |
int numOfCycles = 0; |
flotar signalFrequency, signalFrequency2, signalFrequency3, signalFrequencyGuess, total; |
byte state_machine = 0; |
int samplesPerPeriod = 0; |
configuración vacía () |
{ |
Serial.begin (115200); // 115200 Velocidad en baudios para el monitor en serie |
} |
bucle vacío () |
{ |
//***************************************************************** |
// Sección de Calabration |
//***************************************************************** |
Serial.println ("Calabrating. Por favor, no toque ninguna nota durante la calabration."); |
para (i = 0; i <MUESTRAS DESPLAZADAS; i ++) |
{ |
offSet = analogRead (0); // Lee el valor del pin analógico 0 (A0), lo cuantifica y lo guarda como término real. |
//Serial.println(offSet); // use esto para ajustar el módulo de detección de sonido a aproximadamente la mitad o 512 cuando no se reproduce ningún sonido. |
sumOffSet = sumOffSet + offSet ; |
} |
samplesPerPeriod = 0; |
maxValue = 0; |
//***************************************************************** |
// Prepárese para aceptar la entrada de A0 |
//***************************************************************** |
avgOffSet = round (sumOffSet / OFFSETSAMPLES); |
Serial.println ("Cuenta regresiva"); |
retraso (1000); // pausa por 1 segundo |
Serial.println ("3"); |
retraso (1000); // pausa por 1 segundo |
Serial.println ("2"); |
retraso (1000); // pausa por 1 |
Serial.println ("1"); |
retraso (1000); // pausa por 1 segundo |
Serial.println ("¡Toca tu nota!"); |
retraso (250); // pausa durante 1/4 de segundo para el tiempo de reacción |
//***************************************************************** |
// Recolectar MUESTRAS muestras de A0 con período de muestreo de período de muestreo |
//***************************************************************** |
SamplePeriod = 1.0 / SAMPLING_FREQUENCY; // Periodo en microsegundos |
para (i = 0; i <MUESTRAS; i ++) |
{ |
microSegundos = micros (); // Devuelve el número de microsegundos desde que la placa Arduino comenzó a ejecutar el script actual. |
X = analogRead (0); // Lee el valor del pin analógico 0 (A0), lo cuantifica y lo guarda como término real. |
/ * tiempo de espera restante entre muestras si es necesario en segundos * / |
while (micros () <(microsegundos + (período de muestreo * 1000000))) |
{ |
// no hagas nada solo espera |
} |
} |
//***************************************************************** |
// Función de autocorrelación |
//***************************************************************** |
para (i = 0; i <MUESTRAS; i ++) // i = retraso |
{ |
suma = 0; |
for (k = 0; k <SAMPLES - i; k ++) // Emparejar señal con señal retardada |
{ |
suma = suma + (((X [k]) - avgOffSet) * ((X [k + i]) - avgOffSet)); // X [k] es la señal y X [k + i] es la versión retrasada |
} |
autoCorr = suma / MUESTRAS; |
// Primera máquina de estado de detección de picos |
si (state_machine == 0 && i == 0) |
{ |
umbral = autoCorr * 0,5; |
state_machine = 1; |
} |
else if (state_machine == 1 && i> 0 && thresh 0) // state_machine = 1, encuentra 1 período para usar el primer ciclo |
{ |
maxValue = autoCorr ; |
} |
else if (state_machine == 1 && i> 0 && thresh <autoCorr [i-1] && maxValue == autoCorr [i-1] && (autoCorr -autoCorr [i-1]) <= 0) |
{ |
periodBegin = i-1; |
state_machine = 2; |
numOfCycles = 1; |
samplesPerPeriod = (periodBegin - 0); |
period = samplesPerPeriod; |
ajustador = TUNER + (50.04 * exp (-0.102 * samplesPerPeriod)); |
signalFrequency = ((SAMPLING_FREQUENCY) / (samplesPerPeriod)) - ajustador; // f = fs / N |
} |
else if (state_machine == 2 && i> 0 && thresh 0) // state_machine = 2, encuentra 2 períodos para el 1er y 2do ciclo |
{ |
maxValue = autoCorr ; |
} |
else if (state_machine == 2 && i> 0 && thresh <autoCorr [i-1] && maxValue == autoCorr [i-1] && (autoCorr -autoCorr [i-1]) <= 0) |
{ |
periodEnd = i-1; |
state_machine = 3; |
numOfCycles = 2; |
samplesPerPeriod = (periodEnd - 0); |
SignalFrequency2 = ((numOfCycles * SAMPLING_FREQUENCY) / (samplesPerPeriod)) - ajustador; // f = (2 * fs) / (2 * N) |
maxValue = 0; |
} |
else if (state_machine == 3 && i> 0 && thresh 0) // state_machine = 3, encuentre 3 períodos para el 1er, 2do y 3er ciclo |
{ |
maxValue = autoCorr ; |
} |
else if (state_machine == 3 && i> 0 && thresh <autoCorr [i-1] && maxValue == autoCorr [i-1] && (autoCorr -autoCorr [i-1]) <= 0) |
{ |
periodEnd = i-1; |
state_machine = 4; |
numOfCycles = 3; |
samplesPerPeriod = (periodEnd - 0); |
signalFrequency3 = ((numOfCycles * SAMPLING_FREQUENCY) / (samplesPerPeriod)) - ajustador; // f = (3 * fs) / (3 * N) |
} |
} |
//***************************************************************** |
//Análisis de resultados |
//***************************************************************** |
si (samplesPerPeriod == 0) |
{ |
Serial.println ("Hmm….. No estoy seguro. ¿Estás tratando de engañarme?"); |
} |
demás |
{ |
// preparar la función de ponderación |
total = 0; |
si (señalFrecuencia! = 0) |
{ |
total = 1; |
} |
si (señalFrecuencia2! = 0) |
{ |
total = total + 2; |
} |
si (señalFrecuencia3! = 0) |
{ |
total = total + 3; |
} |
// calcula la frecuencia usando la función de ponderación |
señalFrecuenciaGuess = ((1 / total) * señalFrecuencia) + ((2 / total) * señalFrecuencia2) + ((3 / total) * señalFrecuencia3); // encontrar una frecuencia ponderada |
Serial.print ("La nota que tocó es aproximadamente"); |
Serial.print (signalFrequencyGuess); // Imprime la estimación de frecuencia. |
Serial.println ("Hz."); |
// encuentra el rango de octava basado en la suposición |
octaveRange = 3; |
while (! (signalFrequencyGuess> = storedNoteFreq [0] -7 && signalFrequencyGuess <= storedNoteFreq [11] +7)) |
{ |
para (i = 0; i <12; i ++) |
{ |
storedNoteFreq = 2 * storedNoteFreq ; |
} |
octaveRange ++; |
} |
// Encuentra la nota más cercana |
minValue = 10000000; |
noteLocation = 0; |
para (i = 0; i <12; i ++) |
{ |
if (minValue> abs (signalFrequencyGuess-storedNoteFreq )) |
{ |
minValue = abs (signalFrequencyGuess-storedNoteFreq ); |
noteLocation = i; |
} |
} |
// Imprime la nota |
Serial.print ("Creo que jugaste"); |
si (noteLocation == 0) |
{ |
Serial.print ("C"); |
} |
else if (noteLocation == 1) |
{ |
Serial.print ("C #"); |
} |
más si (noteLocation == 2) |
{ |
Serial.print ("D"); |
} |
más si (noteLocation == 3) |
{ |
Serial.print ("D #"); |
} |
más si (noteLocation == 4) |
{ |
Serial.print ("E"); |
} |
más si (noteLocation == 5) |
{ |
Serial.print ("F"); |
} |
más si (noteLocation == 6) |
{ |
Serial.print ("F #"); |
} |
más si (noteLocation == 7) |
{ |
Serial.print ("G"); |
} |
más si (noteLocation == 8) |
{ |
Serial.print ("G #"); |
} |
más si (noteLocation == 9) |
{ |
Serial.print ("A"); |
} |
más si (noteLocation == 10) |
{ |
Serial.print ("A #"); |
} |
más si (noteLocation == 11) |
{ |
Serial.print ("B"); |
} |
Serial.println (octaveRange); |
} |
//***************************************************************** |
//Deténgase aquí. Presione el botón de reinicio en Arduino para reiniciar |
//***************************************************************** |
mientras (1); |
} |
ver rawgistfile1.txt alojado con ❤ por GitHub
Paso 3: configura el detector de notas musicales
Conecte el Arduino Uno a la PC con el código escrito o cargado en el IDE de Arduino. Compile y cargue el código en Arduino. Coloque el circuito cerca de la fuente de música. Nota: En el video de introducción, utilizo una aplicación instalada en la tableta junto con los parlantes de la PC como fuente de música. Presione el botón de reinicio en la placa Arduino y luego toque una nota en la fuente de música. Después de unos segundos, el detector de notas musicales mostrará la nota tocada y su frecuencia.