原文:https://circuitdigest.com/microcontroller-projects/arduino-uno-guitar-tuner
ARDUINO项目
经过**伊曼纽尔·奥登拉德(Emmanuel Odunlade)** 2018年9月17日
基于Arduino的吉他调音器
嗨,大家好,在过去的几周里,我一直在努力与对吉他的热爱重新建立联系。弹吉他是几年前萨克斯管接手之前我放松的方式。回到吉他上,经过3年很少弹奏和弦之后,我发现除其他外,我不再知道每根琴弦应该如何发音,用我朋友的话说,“我的听力不再调音”,因此,如果没有后来下载的键盘或移动应用程序的帮助,我将无法对吉他进行调音。直到几天前的数周,当我身上的制造商变得充满动力时,我决定建立一个基于Arduino的Guitar Tuner。在今天的教程中,我将分享如何构建自己的DIY Arduino Guitar Tuner。
在转向电子产品之前,了解构建背后的原理很重要。字母表示有7种主要音符;A,B,C,D,E,F,G,通常以另一个A结束,该A总是比第一个A高八度。在音乐中,这些音符有多个版本,例如第一个A和最后一个A。彼此之间的区别在于彼此之间的差异,而彼此之间则以音调的特征之一为特征。音高定义为声音的响度或低度,并由声音的频率表示。由于这些音符的频率是已知的,因此对于我们来说,确定吉他是否已调音,我们只需要将特定弦的音符的频率与该弦所代表的音符的实际频率进行比较即可。
这7个音符的频率为:
A = 27.50Hz
B = 30.87Hz
C = 16.35赫兹
D = 18.35赫兹
E = 20.60赫兹
F = 21.83Hz
G = 24.50赫兹
这些音符的每个变体始终保持等于FxM的音高,其中F是频率,M是非零整数。因此,对于如前所述的最后一个A,它比第一个A高八度。
27.50 x 2 = 55Hz。
吉他(Lead / Box吉他)通常有6个弦,在开弦上用音符E,A,D,G,B,E表示。和往常一样,最后一个E比第一个E高八度。我们将设计吉他调音器,以帮助使用这些音符的频率来调音吉他。
根据标准吉他调音,下表中显示了每个琴弦的音符和相应的频率。
弦乐 | 频率 | 符号 |
---|---|---|
1(E) | 329.63赫兹 | E4 |
2(B) | 246.94赫兹 | B3 |
3(G) | 196.00赫兹 | G3 |
4(深) | 146.83赫兹 | D3 |
5(A) | 110.00赫兹 | A2 |
6(E) | 82.41赫兹 | E2 |
该项目的流程很简单; 我们将吉他产生的声音信号转换为一个频率,然后与被调弦的确切频率值进行比较。值相关时,会通过LED通知吉他手。
频率检测/转换涉及三个主要阶段:
产生的声音信号对于Arduino的ADC来说太弱了,因此我们需要放大该信号。放大后,为了使信号保持在Arduino ADC可以识别的范围内以防止信号削波,我们对信号电压进行了补偿。偏移后,信号随后传递到Arduino ADC,在此处进行采样并获得该声音的频率。
构建此项目需要以下组件;
如下面的吉他调音器电路图所示,连接组件。
按钮的连接没有上拉/下拉电阻,因为将使用Arduino内置的上拉电阻。这是为了确保电路尽可能简单。
该吉他调音器项目的代码背后的算法很简单。要调弦,吉他手可以通过按下相应的按钮来选择弦,然后弹奏开放的弦。声音由放大级收集,并传递到Arduino ADC。频率被解码和比较。当来自琴弦的输入频率小于指定频率时,对于该琴弦,黄色LED之一会亮起,指示应将琴弦拧紧。当测得的频率大于该灯串的规定频率时,另一个LED点亮。当频率在该弦的规定范围内时,绿色LED会亮起以指导吉他手。
最后给出了完整的Arduino代码,在这里我们简要解释了代码的重要部分。
我们首先创建一个数组来保存开关。
int buttonarray[] = {13,12,11,10,9,8}; //[E2,A2,D3,G3,B3,E4]
接下来,我们创建一个数组来保存每个字符串的相应频率。
float freqarray[] = {82.41,110.00,146.83,196.00,246.94,329.63}; //全部以Hz为单位
完成此操作后,我们将声明连接LED的引脚以及将用于从ADC获取频率的其他变量。
int lowerLed = 7;
int HigherLed = 6;
int justRight = 5;
#define LENGTH 512
byte rawData[LENGTH];
int count;
接下来是*void setup()*函数。
在这里,我们首先从Arduino上为开关所连接的每个引脚启用内部上拉功能开始。之后,我们将连接LED的引脚设置为输出,并启动串行监视器以显示数据。
void setup()
{
for(int i = 0; i <= 5; i ++)
{
pinMode(buttonarray[i],INPUT_PULLUP);
}
pinMode(lowerLed,OUTPUT);
pinMode(higherLed,OUTPUT);
pinMode(justRight,OUTPUT);
Serial.begin(115200);
}
接下来是空循环功能,我们实现频率检测和比较。
void loop(){
if(count <LENGTH)
{
count ++;
rawData[count] = AnalogRead(A0)>> 2;
}
else {
sum = 0;
pd_state = 0;
int period = 0;
for(i = 0; i <len; i ++)
{
//自动纠正
sum_old = sum;
sum = 0;
for(k = 0; k <len-i; k ++)sum + =(rawData[k] -128)*(rawData[k + i] -128)/ 256;
// Serial.println(sum);
//峰值检测状态机,
if(pd_state == 2 &&(sum-sum_old)<= 0)
{
period = i;
pd_state = 3;
}
if(pd_state == 1 &&(sum> thresh)&&(sum-sum_old)> 0)pd_state = 2;
if(!i){
thresh = sum * 0.5;
pd_state = 1;
}
}
//以Hz标识的频率
if(thresh > 100) {
freq_per = sample_freq / period;
Serial.println(freq_per);
for(INT S = 0; S <= 5; S ++)
{
if(digitalRead(buttonarray[I])== HIGH)
{
if(freq_per - freqarray[I] <0)
{
digitalWrite(lowerLed,HIGH);
}
else if(freq_per-freqarray[i]> 10)
{
digitalWrite(higherLed,HIGH);
}
else
{
digitalWrite(justRight,HIGH);
}
}
}
}
count = 0;
}
}
下面给出了带有演示视频的完整代码。将代码上传到Arduino开发板并随意删除。
代码
int buttonarray[] = {
13, 12, 11, 10, 9, 8}; // [E2, A2, D3, G3, B3, E4]
// each pin represents a guitar string
// next we create and array with frequencies matching each of the strings above
// such that when 13 is selected the freq matching the note e is selected).
float freqarray[] = {
82.41, 110.00, 146.83, 196.00, 246.94, 329.63};//sll in Hz
int lowerLed = 7;
int higherLed = 6;
int justRight = 5;
#define LENGTH 512
byte rawData[LENGTH];
int count = 0;
// Sample Frequency in kHz
const float sample_freq = 8919;
int len = sizeof(rawData);
int i,k;
long sum, sum_old;
int thresh = 0;
float freq_per = 0;
byte pd_state = 0;
void setup(){
for (int i=0; i<=5; i++)
{
pinMode(buttonarray[i], INPUT_PULLUP);
}
pinMode(lowerLed, OUTPUT);
pinMode(higherLed, OUTPUT);
pinMode(justRight, OUTPUT);
Serial.begin(115200);
}
void loop(){
if (count < LENGTH)
{
count++;
rawData[count] = analogRead(A0)>>2;
}
else {
sum = 0;
pd_state = 0;
int period = 0;
for(i=0; i < len; i++)
{
// Autocorrelation
sum_old = sum;
sum = 0;
for(k=0; k < len-i; k++) sum += (rawData[k]-128)*(rawData[k+i]-128)/256;
// Serial.println(sum);
// Peak Detect State Machine
if (pd_state == 2 && (sum-sum_old) <=0)
{
period = i;
pd_state = 3;
}
if (pd_state == 1 && (sum > thresh) && (sum-sum_old) > 0) pd_state = 2;
if (!i) {
thresh = sum * 0.5;
pd_state = 1;
}
}
// Frequency identified in Hz
if (thresh >100) {
freq_per = sample_freq/period;
Serial.println(freq_per);
for (int s=0; s<=5; s++)
{
if (digitalRead(buttonarray[i])== HIGH)
{
if (freq_per - freqarray[i] < 0)
{
digitalWrite(lowerLed, HIGH);
}
else if(freq_per - freqarray[i] > 10)
{
digitalWrite(higherLed, HIGH);
}
else
{
digitalWrite(justRight, HIGH);
}
}
}
}
count = 0;
}
}