Índice:
2025 Autor: John Day | [email protected]. Última modificação: 2025-01-13 06:58
Surpreenda seus amigos e familiares com este projeto que detecta a nota tocada por um instrumento. Este projeto exibirá a frequência aproximada, bem como a nota musical tocada em um teclado eletrônico, aplicativo de piano ou qualquer outro instrumento.
Detalhes
Para este projeto, a saída analógica do detector do módulo de som é enviada para a entrada analógica A0 do Arduino Uno. O sinal analógico é amostrado e quantizado (digitalizado). Código de autocorrelação, ponderação e sintonia é usado para encontrar a frequência fundamental usando os primeiros 3 períodos. A frequência fundamental aproximada é então comparada às frequências nas oitavas 3, 4 e 5 para determinar a frequência de nota musical mais próxima. Finalmente, a nota adivinhada para a frequência mais próxima é impressa na tela.
Nota: Este instrutível se concentra apenas em como construir o projeto. Para obter mais informações sobre os detalhes e justificativas do projeto, visite este link: Mais informações
Suprimentos
- (1) Arduino Uno (ou Genuino Uno)
- (1) Módulo de detecção de som de alta sensibilidade do sensor de microfone DEVMO compatível
- (1) Placa de ensaio sem solda
- (1) Cabo USB-A para B
- Fios de ligação
- Fonte musical (piano, teclado ou app paino com alto-falantes)
- (1) Computador ou laptop
Etapa 1: construir o hardware para o detector de notas musicais
Usando um Arduino Uno, fios de conexão, uma placa de ensaio sem solda e um Módulo de detecção de som de alta sensibilidade com sensor de microfone DEVMO (ou similar) construa o circuito mostrado nesta imagem
Etapa 2: programe o detector de notas musicais
No IDE do Arduino, adicione o código a seguir.
gistfile1.txt
/* |
Nome do arquivo / esboço: MusicalNoteDetector |
Versão nº: v1.0 criada em 7 de junho de 2020 |
Autor Original: Clyde A. Lettsome, PhD, PE, MEM |
Descrição: Este código / esboço exibe a frequência aproximada, bem como a nota musical tocada em um teclado eletrônico ou aplicativo de piano. Para este projeto, a saída analógica do |
O detector de módulo de som é enviado para a entrada analógica A0 do Arduino Uno. O sinal analógico é amostrado e quantizado (digitalizado). Código de autocorrelação, ponderação e ajuste é usado para |
encontre a frequência fundamental usando os primeiros 3 períodos. A frequência fundamental aproximada é então comparada com as frequências nas oitavas 3, 4 e 5 de alcance para determinar o musical mais próximo |
frequência de nota. Finalmente, a nota adivinhada para a frequência mais próxima é impressa na tela. |
Licença: Este programa é um software livre; você pode redistribuí-lo e / ou modificá-lo sob os termos da GNU General Public License (GPL) versão 3, ou qualquer posterior |
versão de sua escolha, conforme publicada pela Free Software Foundation. |
Notas: Copyright (c) 2020 de C. A. Lettsome Services, LLC |
Para obter mais informações, visite |
*/ |
# define SAMPLES 128 // Max 128 para Arduino Uno. |
#define SAMPLING_FREQUENCY 2048 // Fs = Baseado em Nyquist, deve ser 2 vezes a maior frequência esperada. |
# define OFFSETSAMPLES 40 // usado para fins de calibração |
# define TUNER -3 // Ajuste até que C3 seja 130,50 |
float samplingPeriod; |
microssegundos longos sem sinal; |
int X [AMOSTRAS]; // cria um vetor de AMOSTRAS de tamanho para manter valores reais |
float autoCorr [AMOSTRAS]; // cria vetor de AMOSTRAS de tamanho para manter valores imaginários |
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]; // cria vetor de deslocamento |
int avgOffSet; // cria vetor de deslocamento |
int i, k, periodEnd, periodBegin, período, ajustador, noteLocation, octaveRange; |
float maxValue, minValue; |
soma longa; |
limite interno = 0; |
int numOfCycles = 0; |
float signalFrequency, signalFrequency2, signalFrequency3, signalFrequencyGuess, total; |
byte estado_máquina = 0; |
int samplesPerPeriod = 0; |
void setup () |
{ |
Serial.begin (115200); // Taxa Baud 115200 para o Monitor Serial |
} |
void loop () |
{ |
//***************************************************************** |
// Seção de Calabração |
//***************************************************************** |
Serial.println ("Calabrando. Por favor, não toque nenhuma nota durante a calabração."); |
para (i = 0; i <OFFSETSAMPLES; i ++) |
{ |
offSet = analogRead (0); // Lê o valor do pino analógico 0 (A0), quantiza-o e salva-o como um termo real. |
//Serial.println(offSet); // use isso para ajustar o módulo de detecção de som para aproximadamente metade ou 512 quando nenhum som é reproduzido. |
sumOffSet = sumOffSet + offSet ; |
} |
samplesPerPeriod = 0; |
maxValue = 0; |
//***************************************************************** |
// Prepare-se para aceitar a entrada de A0 |
//***************************************************************** |
avgOffSet = round (sumOffSet / OFFSETSAMPLES); |
Serial.println ("Contagem regressiva."); |
atraso (1000); // pausa por 1 segundo |
Serial.println ("3"); |
atraso (1000); // pausa por 1 segundo |
Serial.println ("2"); |
atraso (1000); // pausa para 1 |
Serial.println ("1"); |
atraso (1000); // pausa por 1 segundo |
Serial.println ("Toque sua nota!"); |
atraso (250); // pausa por 1/4 de segundo para tempo de reação |
//***************************************************************** |
// Colete amostras de AMOSTRAS de A0 com período de amostragem de Período de amostragem |
//***************************************************************** |
samplingPeriod = 1.0 / SAMPLING_FREQUENCY; // Período em microssegundos |
para (i = 0; i <AMOSTRAS; i ++) |
{ |
microSegundos = micros (); // Retorna o número de microssegundos desde que a placa Arduino começou a executar o script atual. |
X = analogRead (0); // Lê o valor do pino analógico 0 (A0), quantiza-o e salva-o como um termo real. |
/ * tempo de espera restante entre as amostras, se necessário, em segundos * / |
while (micros () <(microSeconds + (samplingPeriod * 1000000))) |
{ |
// não faça nada, apenas espere |
} |
} |
//***************************************************************** |
// Função de autocorrelação |
//***************************************************************** |
para (i = 0; i <AMOSTRAS; i ++) // i = atraso |
{ |
soma = 0; |
for (k = 0; k <AMOSTRAS - i; k ++) // Corresponder o sinal ao sinal atrasado |
{ |
soma = soma + (((X [k]) - avgOffSet) * ((X [k + i]) - avgOffSet)); // X [k] é o sinal e X [k + i] é a versão atrasada |
} |
autoCorr = soma / AMOSTRAS; |
// Máquina de estado de detecção de primeiro pico |
if (state_machine == 0 && i == 0) |
{ |
thresh = autoCorr * 0,5; |
estado_máquina = 1; |
} |
else if (state_machine == 1 && i> 0 && thresh 0) // state_machine = 1, encontre 1 período para usar o primeiro 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; |
estado_máquina = 2; |
numOfCycles = 1; |
samplesPerPeriod = (periodBegin - 0); |
período = 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, encontre 2 períodos para o 1º e 2º 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; |
estado_máquina = 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, encontre 3 períodos para o 1º, 2º e 3º 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; |
estado_máquina = 4; |
numOfCycles = 3; |
samplesPerPeriod = (periodEnd - 0); |
signalFrequency3 = ((numOfCycles * SAMPLING_FREQUENCY) / (samplesPerPeriod)) - ajustador; // f = (3 * fs) / (3 * N) |
} |
} |
//***************************************************************** |
// Análise de resultados |
//***************************************************************** |
if (samplesPerPeriod == 0) |
{ |
Serial.println ("Hmm….. Não tenho certeza. Você está tentando me enganar?"); |
} |
outro |
{ |
// prepara a função de ponderação |
total = 0; |
if (signalFrequency! = 0) |
{ |
total = 1; |
} |
if (signalFrequency2! = 0) |
{ |
total = total + 2; |
} |
if (signalFrequency3! = 0) |
{ |
total = total + 3; |
} |
// calcule a frequência usando a função de ponderação |
signalFrequencyGuess = ((1 / total) * signalFrequency) + ((2 / total) * signalFrequency2) + ((3 / total) * signalFrequency3); // encontre uma frequência ponderada |
Serial.print ("A nota que você tocou é aproximadamente"); |
Serial.print (signalFrequencyGuess); // Imprime a estimativa de frequência. |
Serial.println ("Hz."); |
// encontre a faixa de oitava com base na suposição |
octaveRange = 3; |
while (! (signalFrequencyGuess> = storedNoteFreq [0] -7 && signalFrequencyGuess <= storedNoteFreq [11] +7)) |
{ |
para (i = 0; i <12; i ++) |
{ |
storedNoteFreq = 2 * storedNoteFreq ; |
} |
octaveRange ++; |
} |
// Encontre a nota mais próxima |
minValue = 10000000; |
noteLocation = 0; |
para (i = 0; i <12; i ++) |
{ |
if (minValue> abs (signalFrequencyGuess-storedNoteFreq )) |
{ |
minValue = abs (signalFrequencyGuess-storedNoteFreq ); |
noteLocation = i; |
} |
} |
// Imprima a nota |
Serial.print ("Acho que você jogou"); |
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); |
} |
//***************************************************************** |
//Pare aqui. Aperte o botão reset no Arduino para reiniciar |
//***************************************************************** |
enquanto (1); |
} |
visualizar rawgistfile1.txt hospedado com ❤ por GitHub
Etapa 3: configurar o detector de notas musicais
Conecte o Arduino Uno ao PC com o código escrito ou carregado no IDE do Arduino. Compile e envie o código para o Arduino. Coloque o circuito perto da fonte de música. Nota: No vídeo de introdução, eu uso um aplicativo instalado no tablet em conjunto com os alto-falantes do PC como minha fonte de música. Aperte o botão de reset na placa Arduino e toque uma nota na fonte da música. Após alguns segundos, o detector de notas musicais exibirá a nota tocada e sua frequência.