цифровая электроника
вычислительная техника
встраиваемые системы

 

Определитель нот на основе Arduino своими руками

Автор: Mike(admin) от 26-06-2020, 08:35

Устройство для распознавания нот на Arduino


Зачастую начинающим музыкантам не хватает навыков в определении той или иной прозвучавшей ноты, особенно это касается задач подбора композиций на слух. В этом деле на помощь может прийти устройство распознавания нот, которое можно собрать самостоятельно.


Определитель нот на основе Arduino своими руками

Данный проект позволяет создать устройство на основе Arduino, которое отображает приблизительную частоту, а также музыкальную ноту звука, полученного от микрофонного датчика.


Схема этого проекта очень проста и состоит из Arduino и подключенного к нему модуля микрофона.


Определитель нот на основе Arduino своими руками

Здесь аналоговый выход звукового модуля подключается к аналоговому входу A0 платы Arduino Uno. Аналоговый сигнал дискретизируется и квантуется (оцифровывается). Автокорреляция, взвешивание и настройка в коде используются для нахождения основной частоты с использованием первых 3 периодов. Затем приблизительная основная частота сравнивается с частотами в октавах 3, 4 и 5 для определения ближайшей частоты музыкальной ноты. Наконец угаданная нота для ближайшей частоты выводится на экран.


Для того, чтобы устройство работало, загрузите приведенный далее код в Arduino.



#define SAMPLES 128             //Максимально 128 для Arduino Uno
#define SAMPLING_FREQUENCY 2048 //Fs = на основе Найквиста, должно быть в 2 раза больше ожидаемой частоты.
#define OFFSETSAMPLES 40  //используется для калибровки
#define TUNER -3    //Отрегулируйте, пока C3 не станет 130.50

float samplingPeriod;
unsigned long microSeconds;

int X[SAMPLES]; //создать вектор размером SAMPLES для хранения реальных значений
float autoCorr[SAMPLES]; //создать вектор размером SAMPLES для хранения мнимых значений
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]; //создать вектор смещения
int avgOffSet; //создать вектор смещения

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);
}

void loop()
{ 
  //*****************************************************************
  //Секция калибровки
  //*****************************************************************
  Serial.println("Calabrating. Please do not play any notes during calabration.");
  for (i = 0; i < OFFSETSAMPLES; i++)
  {
    offSet[i] = analogRead(0); //Считывает значение с аналогового контакта 0 (A0), квантует его и сохраняет как реальный термин.
    //Serial.println(offSet[i]); //используйте это, чтобы настроить модуль обнаружения звука примерно на половину или 512, когда звук не воспроизводится.
    sumOffSet = sumOffSet + offSet[i];
  }
  samplesPerPeriod = 0;
  maxValue = 0;
  
  //*****************************************************************
  //Подготовка приема входного сигнала от A0
  //*****************************************************************
  avgOffSet = round(sumOffSet / OFFSETSAMPLES);
  Serial.println("Counting down.");
  delay(1000);
  Serial.println("3");
  delay(1000);
  Serial.println("2");
  delay(1000);
  Serial.println("1");
  delay(1000);
  Serial.println("Play your note!");
  delay(250);

  //*****************************************************************
  //Соберем выборки SAMPLES из A0 с периодом выборки samplingPeriod
  //*****************************************************************
  samplingPeriod = 1.0 / SAMPLING_FREQUENCY; //Период в микросекундах
  for (i = 0; i < SAMPLES; i++)
  {
    microSeconds = micros();    //Возвращает количество микросекунд с момента запуска текущего скрипта  Arduino.
    X[i] = analogRead(0); //Считывает значение с аналогового контакта 0 (A0), квантует его и сохраняет как реальный термин.
    
    /*оставшееся время ожидания между выборками, если необходимо, в секундах */
    while (micros() < (microSeconds + (samplingPeriod * 1000000)))
    {
      //ничего не делаем, просто ждем
    }
  }

  //*****************************************************************
  //Автокорреляционная функция
  //*****************************************************************

  for (i = 0; i < SAMPLES; i++) //i=адержка
  {
    sum = 0;
    for (k = 0; k < SAMPLES - i; k++) //соответствие сигнала с задержанным сигналом
    {
      sum = sum + (((X[k]) - avgOffSet) * ((X[k + i]) - avgOffSet)); //X [k] - это сигнал, а X [k + i] - версия с задержкой
    }
    autoCorr[i] = sum / SAMPLES;

    // конечный автомат обнаружения пика
    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, найти 1 период для использования первого цикла
    {
      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)
    {
      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)
    {
      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)
    }
  }

  //*****************************************************************
  //Анализ результатов
  //*****************************************************************
  if (samplesPerPeriod == 0)
  {
    Serial.println("Hmm..... I am not sure");
  }
  else
  { 
    //подготовить весовую функцию
    total = 0;
    if (signalFrequency !=0)
    {
      total = 1;
    }
    if(signalFrequency2 !=0)
    {
      total = total + 2;
    }
    if (signalFrequency3 !=0)
    {
      total = total + 3;
    }

    //рассчитать частоту с помощью весовой функции
    signalFrequencyGuess = ((1/total) * signalFrequency) + ((2/total) * signalFrequency2) + ((3/total) * signalFrequency3); //найти взвешенную частоту
    Serial.print("The note you played is approximately ");
    Serial.print(signalFrequencyGuess);     //Вывести предполагаемую частоту.
    Serial.println(" Hz.");

    //найти диапазон октав на основе догадки
    octaveRange=3;
    while (!(signalFrequencyGuess >= storedNoteFreq[0]-7 && signalFrequencyGuess <= storedNoteFreq[11]+7 ))
    {
      for(i = 0; i < 12; i++)
      {
        storedNoteFreq[i] = 2 * storedNoteFreq[i];
      }
      octaveRange++;
    }
    
    //Найти ближайшую ноту
    minValue = 10000000;
    noteLocation = 0;
    for (i = 0; i < 12; i++)
    {
      if(minValue> abs(signalFrequencyGuess-storedNoteFreq[i]))
      {
        minValue = abs(signalFrequencyGuess-storedNoteFreq[i]);
        noteLocation = i;
      }
    }
    
    //Вывести ноту
    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);
  }
  //*****************************************************************
  //Остановка выполнения здесь. Нажмите кнопку сброса на Arduino, чтобы перезагрузить
  //*****************************************************************
  while (1);
}



© digitrode.ru


Теги: Arduino, датчик звука




Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Комментарии:

Оставить комментарий