你們也知道,聖誕節就快到了,讓arduino完成一個比較應景的案例也會讓節日過得更有意思。
arduino可以做的放歌案例除了用語音模塊外,比較簡單成本最低的就是利用蜂鳴器了,只要控制好頻率和節拍,蜂鳴器也可以演奏音樂的,但你要準備的東西也會複雜一點。
一、聖誕歌簡譜
簡譜不理解是什麼??!!
很簡單,你小學時候的音樂課本上面的譜子就是了,下面這張是Jingle Bells的簡譜,歌詞是中文版不用太認真,調子是一樣的。
簡譜上除了中文,其他的應該都看不懂了吧
敲黑板,這個地方要好好理解一下,後面要用到!!!
- 音符節奏分爲一拍、半拍、1/4拍、1/8拍,我們規定一拍音符的時間爲1,那麼半拍爲0.5,1/4拍爲0.25;1/8拍爲0.125……,爲每個音符賦予這樣的拍子播放出來,音樂的整個基調就出來了。
- 確定簡譜的調,在簡譜歌名左下角(即整個簡譜的左上角)處,有個
1 = F 4/4
,這裏的F
表示這個曲子是 F 調的,下面會講到,而4/4
表示曲子是四四拍的 - 音符(簡譜中文字上面對應的每個數字)類型說明,舉個栗子,Jingle Bells簡譜第一小段音符 5 3 2 1 5 0 5 5
1)這裏面5、3、2、1可以看到數字下面都帶着下劃線,這表示每個音符對應的節拍爲0.5拍,即音符5爲0.5拍,音符3爲0.5拍,音符2和音符1也是0.5拍,0也是0.5拍,仔細看最後兩個音符,兩個5下面都是2條下劃線,這表示0.25拍,這樣就好理解了,每個音符都看成1拍,每帶一條下劃線就當前拍數除以2,這樣一條下劃線是1/2=0.5拍,兩條下劃線是1/2/2=0.25拍,ok吧;然後把這一小段音符 5 3 2 1 5 0 5 5的節拍數加起來是0.5+0.5+0.5+0.5+1+0.5+0.25+0.25 = 4拍,這樣就跟簡譜標的四四拍是吻合的了,也說明你這一小段的節拍數沒算錯,記住Jingle Bells簡譜中每一小段都是4拍(這段話對照着簡譜去理解)
2)聰明的你可能還發現有些音符還帶有小點點,當音符不帶點時,表示中音,例如第一小段中的音符3,表示中音音符3;當音符下面帶小點時,表示低音,例如第一小段中的音符5,表示低音音符5;同樣的,當音符上面帶小點時,表示高音音符,這在Jingle Bells簡譜中沒有出現高音
3)音符後邊帶小數點的話,表示該音符節拍+0.5拍(音符帶下劃線的話,在計算完下劃線節拍的基礎上+0.5拍);音符後邊帶 - 的,音符+1拍(計算完下劃線節拍的基礎上+1拍)
4)有的兩個連續的音符上面帶弧線,表示連音,可以稍微改下連音後面那個音的頻率,比如減少或增加一些數值(需自己調試),這樣表現會更流暢,其實不做處理,影響也不大
二、音符頻率對照表
簡單瞭解簡譜後,該怎麼把音符轉換成蜂鳴器對應頻率呢,看看音符頻率表
低音
音符→ 音調↓ |
1# | 2# | 3# | 4# | 5# | 6# | 7# |
A | 221 | 248 | 278 | 294 | 330 | 371 | 416 |
B | 248 | 278 | 294 | 330 | 371 | 416 | 467 |
C | 131 | 147 | 165 | 175 | 196 | 221 | 248 |
D | 147 | 165 | 175 | 196 | 221 | 248 | 278 |
E | 165 | 175 | 196 | 221 | 248 | 278 | 312 |
F | 175 | 196 | 221 | 234 | 262 | 294 | 330 |
G | 196 | 221 | 234 | 262 | 294 | 330 | 371 |
音符→ 音調↓ |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
A | 441 | 495 | 556 | 589 | 661 | 742 | 833 |
B | 495 | 556 | 624 | 661 | 742 | 833 | 935 |
C | 262 | 294 | 330 | 350 | 393 | 441 | 495 |
D | 294 | 330 | 350 | 393 | 441 | 495 | 556 |
E | 330 | 350 | 393 | 441 | 495 | 556 | 624 |
F | 350 | 393 | 441 | 495 | 556 | 624 | 661 |
G | 393 | 441 | 495 | 556 | 624 | 661 | 742 |
音符→ 音調↓ |
1# | 2# | 3# | 4# | 5# | 6# | 7# |
A | 882 | 990 | 1112 | 1178 | 1322 | 1484 | 1665 |
B | 990 | 1112 | 1178 | 1322 | 1484 | 1665 | 1869 |
C | 525 | 589 | 661 | 700 | 786 | 882 | 990 |
D | 589 | 661 | 700 | 786 | 882 | 990 | 1112 |
E | 661 | 700 | 786 | 882 | 990 | 1112 | 1284 |
F | 700 | 786 | 882 | 935 | 1049 | 1178 | 1322 |
G | 786 | 882 | 990 | 1049 | 1178 | 1322 | 1484 |
三、案例編程
看明白簡譜和頻率表就開始寫程序啦,首先宏定義曲目要用到的音符頻率
//中音NTF 0爲空拍
#define NTF0 -1
#define NTF1 350
#define NTF2 393
#define NTF3 441
#define NTF4 495
#define NTF5 556
#define NTF6 624
#define NTF7 661
//高音NTFH
#define NTFH1 700
#define NTFH2 786
#define NTFH3 882
#define NTFH4 935
#define NTFH5 965
#define NTFH6 996
#define NTFH7 1023
//低音NTFL
#define NTFL1 175
#define NTFL2 196
#define NTFL3 221
#define NTFL4 234
#define NTFL5 262
#define NTFL6 294
#define NTFL7 330
按照Jingle Bells簡譜定義音符數組
//音符頻率數組
int tune[]=
{
NTF3,NTF3,NTF3,NTF3,NTF3,NTF3,
NTF3,NTF5,NTF1,NTF2,NTF3,NTF0,
NTF4,NTF4,NTF4,NTF4,NTF4,NTF3,NTF3,NTF3,NTF3,
NTF5,NTF5,NTF4,NTF2,NTF1,NTF0,
NTFL5,NTF3,NTF2,NTF1,NTFL5,NTF0,NTFL5,NTFL5,
NTFL5,NTF3,NTF2,NTF1,NTFL6,NTF0,
NTFL6,NTF4,NTF3,NTF2,NTFL7,NTF0,
NTF5,NTF5,NTF4,NTF2,NTF3,NTF1,NTF0,
NTFL5,NTF3,NTF2,NTF1,NTFL5,NTF0,
NTFL5,NTF3,NTF2,NTF1,NTFL6,NTF0,NTFL6,
NTFL6,NTF4,NTF3,NTF2,NTF5,NTF5,NTF5,NTF5,
NTF6,NTF5,NTF4,NTF2,NTF1,NTF0
};
按簡譜定義音符節拍數組
//音符節拍數組
float durt[]=
{
0.5,0.5,1,0.5,0.5,1,
0.5,0.5,0.75,0.25,1.5,0.5,
0.5,0.5,1,0.5,0.5,0.5,0.5,0.25,0.25,
0.5,0.5,0.5,0.5,1.5,0.5,
0.5,0.5,0.5,0.5,1,0.5,0.25,0.25,
0.5,0.5,0.5,0.5,1,1,
0.5,0.5,0.5,0.5,1,1,
0.5,0.5,0.5,0.5,1,0.75,0.25,
0.5,0.5,0.5,0.5,1,1,
0.5,0.5,0.5,0.5,1,0.5,0.5,
0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,
0.5,0.5,0.5,0.5,0.75,0.25
};
接着是主題函數部分
//定義蜂鳴器引腳,音符長度變量
int buzzer_pin = 9;
int length;
//setup函數,初始化引腳,計算長度
void setup()
{
pinMode(buzzer_pin, OUTPUT);
length = sizeof(tune)/sizeof(tune[0]);
}
//loop函數
void loop()
{
//for循環演奏曲子
for(int x=0;x<length;x++)
{
tone(buzzer_pin, tune[x]);
delay(500*durt[x]); //這裏的500爲控制每個音符的時長來定曲子的節奏
noTone(buzzer_pin);
}
delay(500); //開始下一輪循環的時間間隔
}
程序對於蜂鳴器單獨演奏曲子來說簡短有效,對於在曲子演奏中間要一邊做點其他事情,可能就有些些麻煩了
四、案例擴展
聖誕節案例肯定不能少了聖誕樹不是嘛,這種聖誕樹小裝飾在某寶上十幾二十塊就能買到帶帶燈的,我手頭就搞了一個出來,店家給的蚊帳布一樣的原材料要自己動手,也是花了不少時間搞,主要是燈帶的固定很是頭疼,下面看一下效果圖,還是可以接受的
商家隨機給的顏色布料,粉色的,老夫也是無奈,還有圈上彩燈後的效果,還怪不錯的咧
是吧,節日一擺上那是很有氣氛了,還給了些聖誕鞋小掛飾什麼的,沒給弄上去,兩節5號電池供電,常亮狀態,爲了能達到彩燈控制效果,我把線剪了接到arduino控制的繼電器上面,通過繼電器控燈,程序也寫了,以最原始的方式,另外加了OLED屏幕的節日祝福顯示,具體代碼如下
#include <U8glib.h>
//引腳定義
int buzzer_0 = 5;
int relay_0 = 12;
U8GLIB_SSD1306_128X64 u8g_0(U8G_I2C_OPT_NONE);
//setup函數
void setup() {
pinMode(buzzer_0, OUTPUT);
pinMode(relay_0, OUTPUT);
//OLED屏幕顯示 Merry Christmas
u8g_0.firstPage();
do {
u8g_0.setFont(u8g_font_9x18);
u8g_0.drawStr(5, 30, "Merry");
u8g_0.drawStr(40, 55, "Christmas");
} while (u8g_0.nextPage());
}
//loop函數
void loop() {
tone(buzzer_0, 441, 0); // T1-1
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 441, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 441, 0);
delay(0);
delay(480);
noTone(buzzer_0);
digitalWrite(relay_0, HIGH);
tone(buzzer_0, 441, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 441, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 441, 0);
delay(0);
delay(230);
noTone(buzzer_0);
digitalWrite(relay_0, LOW);
tone(buzzer_0, 441, 0); // T1-2
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 556, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 350, 0);
delay(0);
delay(355);
noTone(buzzer_0);
digitalWrite(relay_0, HIGH);
tone(buzzer_0, 393, 0);
delay(0);
delay(125);
noTone(buzzer_0);
tone(buzzer_0, 441, 0);
delay(0);
delay(750);
noTone(buzzer_0);
tone(buzzer_0, -1, 0);
delay(0);
delay(230);
noTone(buzzer_0);
digitalWrite(relay_0, LOW);
tone(buzzer_0, 495, 0); // T1-3
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 495, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 495, 0);
delay(0);
delay(500);
noTone(buzzer_0);
tone(buzzer_0, 495, 0);
delay(0);
delay(230);
noTone(buzzer_0);
digitalWrite(relay_0, HIGH);
tone(buzzer_0, 495, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 441, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 441, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 441, 0);
delay(0);
delay(125);
noTone(buzzer_0);
tone(buzzer_0, 441, 0);
delay(0);
delay(105);
noTone(buzzer_0);
digitalWrite(relay_0, LOW);
tone(buzzer_0, 556, 0); // T1-4
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 556, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 495, 0);
delay(0);
delay(230);
noTone(buzzer_0);
digitalWrite(relay_0, HIGH);
tone(buzzer_0, 393, 0);
delay(0);
delay(250);
noTone(buzzer_0);
tone(buzzer_0, 350, 0);
delay(0);
delay(750);
noTone(buzzer_0);
tone(buzzer_0, -1, 0);
delay(0);
delay(230);
noTone(buzzer_0);
... //此處省略200+行重複代碼
delay(1500); //循環演奏的時間間隔
}
上述代碼只取了完整曲子的1/3段,因爲每個音符都是以tone函數,delay函數,noTone函數的形式在循環,4個小段加起來30個音符左右,還有OLED顯示和繼電器控制的程序前前後後140行代碼左右,全曲82個音符總370行代碼,也是敲得心累,上面把後邊200+行的重複函數代碼省略了,只是頻率跟延時不同。
案例擴展實現了簡介控制聖誕樹燈光閃爍,其實個人覺得接PWM引腳做成呼吸燈效果會更棒,也不用聽繼電器在滴答滴答叫,這個就看你們個人的idea了,分享到這,下回見~