蜂鳴器音樂播放實驗
首先應該了解一下蜂鳴器音樂播放的原理,在這里我只講一下電磁式蜂鳴器驅動原理(還有一種是壓電式蜂鳴器):
電磁式蜂鳴器驅動原理:
蜂鳴器發聲原理是電流通過電磁線圈,使電磁圈產生磁場來驅動振動膜發聲的。因此需要一定的電流才能驅動它,而單片機I/O引腳輸出的電壓較小。單片機輸出的TTLK電平基本驅動不了蜂鳴器,因需要增加一個放大電路。這里用三極管作為放大電路。下面是原理圖:
我這里的J8端是跟芯片的P1^5端口相連,當P1^5輸出高電平時,三極管截止,蜂鳴器不發聲,反之,輸出低電平時,蜂鳴器發聲。
而要驅動蜂鳴器能像唱歌一樣的發聲,其實只要使蜂鳴器發出頻率和持續時間不同的聲音即可。周期等於頻率的倒數,所以可以通過頻率知道這段的時間,所以可以通過調用延時函數或者定時器實現,同樣的發聲的持續時間也可以通過延時函數實現,所以讓蜂鳴器唱歌最關鍵的就是知道要延長多少時間!
用單片機來演奏音樂,,只要搞清楚兩個概念就好了,分別是“音調”和“節拍”。
音調表示一個音符該唱的頻率。
節拍表示一個音符該唱多少時間。
這里有兩種方法來實現該功能:
(1)查表法
這個方法復雜的地方在於你要找出每個音符相對應的頻率(根據音符;頻率對照表找),然后根據公式轉換為相應的時間(取半周期),然后通過延時函數實現。最后編程實現。
先上代碼好了:
1 /*************************************************** 2 實驗名稱:用蜂鳴器播放生日快樂歌 3 實驗說明:不使用定時器中斷,所有頻率完全用延時實現 4 實驗時間: 2014/12/5 5 ***************************************************/ 6 7 #include<reg51.h> 8 #define uchar unsigned char 9 #define uint unsigned int 10 11 sbit BEEP=P1^5;//蜂鳴器接芯片的P1^5端口 12 13 /*生日快樂歌的音符頻率表,不同頻率由不同的延時來決定音符頻率明顯大於數組里面的值,但是因為是8位寄 15 存器,所以最多只能放511,但是有的頻率大於511,所以只能在延時函數中乘上相應的值來接近實際樂譜頻率*/ 17 uchar code SONG_TONE[]= 18 {212,212,190,212,159,169,212,212,190,212,142,159,212, 19 212,106,126,159,169,190,119,119,126,159,142,159,0}; 20 21 //生日快樂歌節拍表,節拍決定每個音符的演奏長短,只是取個系數,並非准確的值 22 uchar code SONG_LONG[]= 23 {9,3,12,12,12,24,9,3,12,12,12,24,9, 24 3,12,12,12,12,12,9,3,12,12,12,24,0}; 25 26 //延時 27 void DelayMS(uint x) 28 { 29 uchar t; 30 while(x--) 31 for(t=0;t<120;t++); 32 } 33 34 //播放函數 35 void PlayMusic() 36 { 37 uint i=0,j,k; 38 while(SONG_LONG[i]!=0||SONG_TONE[i]!=0) 39 { 40 /*播放各個音符,SONG_LONG為拍子長度,一個節拍大概為 400ms-500ms,這里的節拍又受下面一個音符的影響,所以只能根據大概的來取值*/ 41 for(j=0;j<SONG_LONG[i]*26;j++) 42 { 43 BEEP=~BEEP; 44 //SONG_TONE延時表決定了每個音符的頻率 45 for(k=0;k<SONG_TONE[i]/3;k++); 46 } 47 48 DelayMS(50); 49 i++; 50 } 51 } 52 53 void main() 54 { 55 BEEP=0; 56 while(1) 57 { 58 PlayMusic();//播放生日快樂 59 DelayMS(500);//播放完后暫停一段時間 60 } 61 }
整個流程是這樣的:
首先根據生日快樂歌的樂譜將各個音調轉換為相應的頻率。
比如:左邊是生日快樂歌樂譜,右邊是音符頻率轉換表
這里先來了解一下樂譜的一點知識,左邊樂譜數字下面有點說明是低音,沒點說明就是普通的,數字上面有點就是高音,而5的低音就是4.5,高音是5.5,其他音符也是相應的道理。
樂譜的左上方有寫“1=F”,而一般的樂譜都是C調,就是“1=C”,注意,樂譜里面的1234567(哆啦咪發索拉西多)相對應的不是ABCDEFG而是CDEFGAB!所以這里規定是F調的話,
那么就說明2就要唱G,3要唱A,……7要唱E,所以這里的低音5對應的應該是低音的1.5!!!!就是所謂的要相應的左移或者右移。如果還是不明白的話,看下面:
1原本對應的應該是C,4原本應該對應的是F,對吧?
然后現在1對應的變成F了,就相當於對應了4,對吧?
那么1.5對應的是什么?
4.5咯!
那2對應的是什么?
5唄!
那么好了咯,低音5是4.5,是不是就是等於1.5?所以半周期就是1803µs。
至於為什么是根據半周期算,那是因為單片機是通過循環對蜂鳴器接的端口置位,復位來使發聲的,所以就是半周期。因為我用的是無源的蜂鳴器,有源的蜂鳴器就是全周期了。
然后就是按照上述道理,一個個轉換,並用延時函數實現,因為每個音符的轉換頻率都不一樣,要么使用多個延時函數一個個實現准確的音調頻率,但是這樣太煩,而且單片機本身就不是專門
弄來唱歌的。我們不應該為難他們,所以自己將就一下就算了。所以延時函數為了適應每個音調都有差不多的頻率,這個就靠自己計算了,而且不同的歌那個值還不一樣,所以這就是這個問題
的難點。
接下來的就是那個唱多久的問題,一般的歌曲默認的一個節拍是400ms-500ms。
同樣的看樂譜左上方,它有寫“3/4”,意思是以四分音符為節拍,每一個小結有三拍。
而在每個數字下面有一條橫線,那時間就是那個節拍的時間乘上0.5,有兩條就乘上0.25,三條就乘以0.125。。。音樂的基本知識就別為難我了,我是個音樂白痴。。所以我就這么理解了。。
哈哈哈哈哈~
而至於節拍轉換為頻率,也是有相應的表的,見下:
同樣的也是通過延時函數來實現,當然也是會有誤差的。而至於延時函數怎么寫,見[51單片機學習筆記ONE]
編程思想的話挺簡單的,就是先將音符頻率和所要唱的時間轉換好,放到兩個數組里面。然后在主程序里面,通過延時達到相應頻率,唱完一遍,停一會,接着唱就好了。
(2)用工具轉碼並用計時器實現
先上代碼好了:
1 /*說明************************************************************************** 2 曲譜存貯格式 unsigned char code MusicName{音高,音長,音高,音長...., 0,0}; 末尾:0,0 表示結束(Important) 3 4 音高由三位數字組成: 5 個位是表示 1~7 這七個音符 6 十位是表示音符所在的音區:1-低音,2-中音,3-高音; 7 百位表示這個音符是否要升半音: 0-不升,1-升半音。 8 9 音長最多由三位數字組成: 10 個位表示音符的時值,其對應關系是: 11 |數值(n): |0 |1 |2 |3 | 4 | 5 | 6 12 |幾分音符: |1 |2 |4 |8 |16 |32 |64 音符=2^n 13 十位表示音符的演奏效果(0-2): 0-普通,1-連音,2-頓音 14 百位是符點位: 0-無符點,1-有符點 15 16 調用演奏子程序的格式 17 Play(樂曲名,調號,升降八度,演奏速度); 18 |樂曲名 : 要播放的樂曲指針,結尾以(0,0)結束; 19 |調號(0-11) : 是指樂曲升多少個半音演奏; 20 |升降八度(1-3) : 1:降八度, 2:不升不降, 3:升八度; 21 |演奏速度(1-12000): 值越大速度越快; 22 23 ***************************************************************************/ 24 #ifndef __SOUNDPLAY_H_REVISION_FIRST__ 25 #define __SOUNDPLAY_H_REVISION_FIRST__ 26 27 #include <REG51.H> 28 29 //************************************************************************** 30 31 #define SYSTEM_OSC 11059200//12000000 //定義晶振頻率12000000HZ 32 #define SOUND_SPACE 4/5 //定義普通音符演奏的長度分率,//每4分音符間隔 33 sbit BeepIO = P1^5; //定義輸出管腳 34 35 unsigned int code FreTab[12] = { 262,277,294,311,330,349,369,392,415,440,466,494 }; //原始頻率表 36 unsigned char code SignTab[7] = { 0,2,4,5,7,9,11 }; //1~7在頻率表中的位置 37 unsigned char code LengthTab[7]= { 1,2,4,8,16,32,64 }; 38 unsigned char Sound_Temp_TH0,Sound_Temp_TL0; //音符定時器初值暫存 39 unsigned char Sound_Temp_TH1,Sound_Temp_TL1; //音長定時器初值暫存 40 //************************************************************************** 41 void InitialSound(void) 42 { 43 BeepIO = 1; 44 Sound_Temp_TH1 = (65535-(1/1200)*SYSTEM_OSC)/256; // 計算TL1應裝入的初值 (10ms的初裝值) 45 Sound_Temp_TL1 = (65535-(1/1200)*SYSTEM_OSC)%256; // 計算TH1應裝入的初值 46 TH1 = Sound_Temp_TH1; 47 TL1 = Sound_Temp_TL1; 48 TMOD |= 0x11; 49 ET0 = 1; 50 ET1 = 0; 51 TR0 = 0; 52 TR1 = 0; 53 EA = 1; 54 } 55 56 void BeepTimer0(void) interrupt 1 //音符發生中斷 57 { 58 BeepIO = !BeepIO; 59 TH0 = Sound_Temp_TH0; 60 TL0 = Sound_Temp_TL0; 61 } 62 //************************************************************************** 63 void Play(unsigned char *Sound,unsigned char Signature,unsigned Octachord,unsigned int Speed) 64 { 65 unsigned int NewFreTab[12]; //新的頻率表 66 unsigned char i,j; 67 unsigned int Point,LDiv,LDiv0,LDiv1,LDiv2,LDiv4,CurrentFre,Temp_T,SoundLength; 68 unsigned char Tone,Length,SL,SH,SM,SLen,XG,FD; 69 for(i=0;i<12;i++) // 根據調號及升降八度來生成新的頻率表 70 { 71 j = i + Signature; 72 if(j > 11) 73 { 74 j = j-12; 75 NewFreTab[i] = FreTab[j]*2; 76 } 77 else 78 NewFreTab[i] = FreTab[j]; 79 80 if(Octachord == 1) 81 NewFreTab[i]>>=2; 82 else if(Octachord == 3) 83 NewFreTab[i]<<=2; 84 } 85 86 SoundLength = 0; 87 while(Sound[SoundLength] != 0x00) //計算歌曲長度 88 { 89 SoundLength+=2; 90 } 91 92 Point = 0; 93 Tone = Sound[Point]; 94 Length = Sound[Point+1]; // 讀出第一個音符和它時時值 95 96 LDiv0 = 12000/Speed; // 算出1分音符的長度(幾個10ms) 97 LDiv4 = LDiv0/4; // 算出4分音符的長度 98 LDiv4 = LDiv4-LDiv4*SOUND_SPACE; // 普通音最長間隔標准 99 TR0 = 0; 100 TR1 = 1; 101 while(Point < SoundLength) 102 { 103 SL=Tone%10; //計算出音符 104 SM=Tone/10%10; //計算出高低音 105 SH=Tone/100; //計算出是否升半 106 CurrentFre = NewFreTab[SignTab[SL-1]+SH]; //查出對應音符的頻率 107 if(SL!=0) 108 { 109 if (SM==1) CurrentFre >>= 2; //低音 110 if (SM==3) CurrentFre <<= 2; //高音 111 Temp_T = 65536-(50000/CurrentFre)*10/(12000000/SYSTEM_OSC);//計算計數器初值 112 Sound_Temp_TH0 = Temp_T/256; 113 Sound_Temp_TL0 = Temp_T%256; 114 TH0 = Sound_Temp_TH0; 115 TL0 = Sound_Temp_TL0 + 12; //加12是對中斷延時的補償 116 } 117 SLen=LengthTab[Length%10]; //算出是幾分音符 118 XG=Length/10%10; //算出音符類型(0普通1連音2頓音) 119 FD=Length/100; 120 LDiv=LDiv0/SLen; //算出連音音符演奏的長度(多少個10ms) 121 if (FD==1) 122 LDiv=LDiv+LDiv/2; 123 if(XG!=1) 124 if(XG==0) //算出普通音符的演奏長度 125 if (SLen<=4) 126 LDiv1=LDiv-LDiv4; 127 else 128 LDiv1=LDiv*SOUND_SPACE; 129 else 130 LDiv1=LDiv/2; //算出頓音的演奏長度 131 else 132 LDiv1=LDiv; 133 if(SL==0) LDiv1=0; 134 LDiv2=LDiv-LDiv1; //算出不發音的長度 135 if (SL!=0) 136 { 137 TR0=1; 138 for(i=LDiv1;i>0;i--) //發規定長度的音 139 { 140 while(TF1==0); 141 TH1 = Sound_Temp_TH1; 142 TL1 = Sound_Temp_TL1; 143 TF1=0; 144 } 145 } 146 if(LDiv2!=0) 147 { 148 TR0=0; BeepIO=1; 149 for(i=LDiv2;i>0;i--) //音符間的間隔 150 { 151 while(TF1==0); 152 TH1 = Sound_Temp_TH1; 153 TL1 = Sound_Temp_TL1; 154 TF1=0; 155 } 156 } 157 Point+=2; 158 Tone=Sound[Point]; 159 Length=Sound[Point+1]; 160 } 161 BeepIO = 1; 162 } 163 //************************************************************************** 164 #endif 165 166 167 168 //揮着翅膀的女孩 169 unsigned char code Music_Girl[]={ 0x17,0x02, 0x17,0x03, 0x18,0x03, 0x19,0x02, 0x15,0x03, 170 0x16,0x03, 0x17,0x03, 0x17,0x03, 0x17,0x03, 0x18,0x03, 171 0x19,0x02, 0x16,0x03, 0x17,0x03, 0x18,0x02, 0x18,0x03, 172 0x17,0x03, 0x15,0x02, 0x18,0x03, 0x17,0x03, 0x18,0x02, 173 0x10,0x03, 0x15,0x03, 0x16,0x02, 0x15,0x03, 0x16,0x03, 174 0x17,0x02, 0x17,0x03, 0x18,0x03, 0x19,0x02, 0x1A,0x03, 175 0x1B,0x03, 0x1F,0x03, 0x1F,0x03, 0x17,0x03, 0x18,0x03, 176 0x19,0x02, 0x16,0x03, 0x17,0x03, 0x18,0x03, 0x17,0x03, 177 0x18,0x03, 0x1F,0x03, 0x1F,0x02, 0x16,0x03, 0x17,0x03, 178 0x18,0x03, 0x17,0x03, 0x18,0x03, 0x20,0x03, 0x20,0x02, 179 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x20,0x03, 0x21,0x03, 180 0x20,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x1F,0x03, 181 0x1B,0x03, 0x19,0x03, 0x19,0x03, 0x15,0x03, 0x1A,0x66, 182 0x1A,0x03, 0x19,0x03, 0x15,0x03, 0x15,0x03, 0x17,0x03, 183 0x16,0x66, 0x17,0x04, 0x18,0x04, 0x18,0x03, 0x19,0x03, 184 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x20,0x03, 0x21,0x03, 185 0x20,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x1F,0x03, 186 0x1B,0x03, 0x19,0x03, 0x19,0x03, 0x15,0x03, 0x1A,0x66, 187 0x1A,0x03, 0x19,0x03, 0x19,0x03, 0x1F,0x03, 0x1B,0x03, 188 0x1F,0x00, 0x1A,0x03, 0x1A,0x03, 0x1A,0x03, 0x1B,0x03, 189 0x1B,0x03, 0x1A,0x03, 0x19,0x03, 0x19,0x02, 0x17,0x03, 190 0x15,0x17, 0x15,0x03, 0x16,0x03, 0x17,0x03, 0x18,0x03, 191 0x17,0x04, 0x18,0x0E, 0x18,0x03, 0x17,0x04, 0x18,0x0E, 192 0x18,0x66, 0x17,0x03, 0x18,0x03, 0x17,0x03, 0x18,0x03, 193 0x20,0x03, 0x20,0x02, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 194 0x20,0x03, 0x21,0x03, 0x20,0x03, 0x1F,0x03, 0x1B,0x03, 195 0x1F,0x66, 0x1F,0x04, 0x1B,0x0E, 0x1B,0x03, 0x19,0x03, 196 0x19,0x03, 0x15,0x03, 0x1A,0x66, 0x1A,0x03, 0x19,0x03, 197 0x15,0x03, 0x15,0x03, 0x17,0x03, 0x16,0x66, 0x17,0x04, 198 0x18,0x04, 0x18,0x03, 0x19,0x03, 0x1F,0x03, 0x1B,0x03, 199 0x1F,0x66, 0x20,0x03, 0x21,0x03, 0x20,0x03, 0x1F,0x03, 200 0x1B,0x03, 0x1F,0x66, 0x1F,0x03, 0x1B,0x03, 0x19,0x03, 201 0x19,0x03, 0x15,0x03, 0x1A,0x66, 0x1A,0x03, 0x19,0x03, 202 0x19,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x00, 0x18,0x02, 203 0x18,0x03, 0x1A,0x03, 0x19,0x0D, 0x15,0x03, 0x15,0x02, 204 0x18,0x66, 0x16,0x02, 0x17,0x02, 0x15,0x00, 0x00,0x00}; 205 //同一首歌 206 unsigned char code Music_Same[]={ 0x0F,0x01, 0x15,0x02, 0x16,0x02, 0x17,0x66, 0x18,0x03, 207 0x17,0x02, 0x15,0x02, 0x16,0x01, 0x15,0x02, 0x10,0x02, 208 0x15,0x00, 0x0F,0x01, 0x15,0x02, 0x16,0x02, 0x17,0x02, 209 0x17,0x03, 0x18,0x03, 0x19,0x02, 0x15,0x02, 0x18,0x66, 210 0x17,0x03, 0x19,0x02, 0x16,0x03, 0x17,0x03, 0x16,0x00, 211 0x17,0x01, 0x19,0x02, 0x1B,0x02, 0x1B,0x70, 0x1A,0x03, 212 0x1A,0x01, 0x19,0x02, 0x19,0x03, 0x1A,0x03, 0x1B,0x02, 213 0x1A,0x0D, 0x19,0x03, 0x17,0x00, 0x18,0x66, 0x18,0x03, 214 0x19,0x02, 0x1A,0x02, 0x19,0x0C, 0x18,0x0D, 0x17,0x03, 215 0x16,0x01, 0x11,0x02, 0x11,0x03, 0x10,0x03, 0x0F,0x0C, 216 0x10,0x02, 0x15,0x00, 0x1F,0x01, 0x1A,0x01, 0x18,0x66, 217 0x19,0x03, 0x1A,0x01, 0x1B,0x02, 0x1B,0x03, 0x1B,0x03, 218 0x1B,0x0C, 0x1A,0x0D, 0x19,0x03, 0x17,0x00, 0x1F,0x01, 219 0x1A,0x01, 0x18,0x66, 0x19,0x03, 0x1A,0x01, 0x10,0x02, 220 0x10,0x03, 0x10,0x03, 0x1A,0x0C, 0x18,0x0D, 0x17,0x03, 221 0x16,0x00, 0x0F,0x01, 0x15,0x02, 0x16,0x02, 0x17,0x70, 222 0x18,0x03, 0x17,0x02, 0x15,0x03, 0x15,0x03, 0x16,0x66, 223 0x16,0x03, 0x16,0x02, 0x16,0x03, 0x15,0x03, 0x10,0x02, 224 0x10,0x01, 0x11,0x01, 0x11,0x66, 0x10,0x03, 0x0F,0x0C, 225 0x1A,0x02, 0x19,0x02, 0x16,0x03, 0x16,0x03, 0x18,0x66, 226 0x18,0x03, 0x18,0x02, 0x17,0x03, 0x16,0x03, 0x19,0x00, 227 0x00,0x00 }; 228 //兩只蝴蝶 229 unsigned char code Music_Two[] ={ 0x17,0x03, 0x16,0x03, 0x17,0x01, 0x16,0x03, 0x17,0x03, 230 0x16,0x03, 0x15,0x01, 0x10,0x03, 0x15,0x03, 0x16,0x02, 231 0x16,0x0D, 0x17,0x03, 0x16,0x03, 0x15,0x03, 0x10,0x03, 232 0x10,0x0E, 0x15,0x04, 0x0F,0x01, 0x17,0x03, 0x16,0x03, 233 0x17,0x01, 0x16,0x03, 0x17,0x03, 0x16,0x03, 0x15,0x01, 234 0x10,0x03, 0x15,0x03, 0x16,0x02, 0x16,0x0D, 0x17,0x03, 235 0x16,0x03, 0x15,0x03, 0x10,0x03, 0x15,0x03, 0x16,0x01, 236 0x17,0x03, 0x16,0x03, 0x17,0x01, 0x16,0x03, 0x17,0x03, 237 0x16,0x03, 0x15,0x01, 0x10,0x03, 0x15,0x03, 0x16,0x02, 238 0x16,0x0D, 0x17,0x03, 0x16,0x03, 0x15,0x03, 0x10,0x03, 239 0x10,0x0E, 0x15,0x04, 0x0F,0x01, 0x17,0x03, 0x19,0x03, 240 0x19,0x01, 0x19,0x03, 0x1A,0x03, 0x19,0x03, 0x17,0x01, 241 0x16,0x03, 0x16,0x03, 0x16,0x02, 0x16,0x0D, 0x17,0x03, 242 0x16,0x03, 0x15,0x03, 0x10,0x03, 0x10,0x0D, 0x15,0x00, 243 0x19,0x03, 0x19,0x03, 0x1A,0x03, 0x1F,0x03, 0x1B,0x03, 244 0x1B,0x03, 0x1A,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x03, 245 0x16,0x0D, 0x17,0x01, 0x17,0x03, 0x17,0x03, 0x19,0x03, 246 0x1A,0x02, 0x1A,0x02, 0x10,0x03, 0x17,0x0D, 0x16,0x03, 247 0x16,0x01, 0x17,0x03, 0x19,0x03, 0x19,0x03, 0x17,0x03, 248 0x19,0x02, 0x1F,0x02, 0x1B,0x03, 0x1A,0x03, 0x1A,0x0E, 249 0x1B,0x04, 0x17,0x02, 0x1A,0x03, 0x1A,0x03, 0x1A,0x0E, 250 0x1B,0x04, 0x1A,0x03, 0x19,0x03, 0x17,0x03, 0x16,0x03, 251 0x17,0x0D, 0x16,0x03, 0x17,0x03, 0x19,0x01, 0x19,0x03, 252 0x19,0x03, 0x1A,0x03, 0x1F,0x03, 0x1B,0x03, 0x1B,0x03, 253 0x1A,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x03, 0x16,0x03, 254 0x17,0x01, 0x17,0x03, 0x17,0x03, 0x19,0x03, 0x1A,0x02, 255 0x1A,0x02, 0x10,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x01, 256 0x17,0x03, 0x19,0x03, 0x19,0x03, 0x17,0x03, 0x19,0x03, 257 0x1F,0x02, 0x1B,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04, 258 0x17,0x02, 0x1A,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04, 259 0x17,0x16, 0x1A,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04, 260 0x1A,0x03, 0x19,0x03, 0x17,0x03, 0x16,0x03, 0x0F,0x02, 261 0x10,0x03, 0x15,0x00, 0x00,0x00 }; 262 263 unsigned char code Music_birth[]={0x0F,0x03,0x0F,0x03,0x10,0x02,0x0F,0x02,0x15,0x02,0x11,0x01,0x0F,0x03, 264 0x0F,0x03,0x10,0x02,0x0F,0x02,0x16,0x02,0x15,0x01,0x0F,0x03,0x0F,0x03, 265 0x19,0x02,0x17,0x02,0x15,0x02,0x11,0x0C,0x10,0x02,0x18,0x03,0x18,0x03, 266 0x17,0x02,0x15,0x02,0x16,0x02,0x17,0x01,0x0F,0x02,0x0F,0x03,0x10,0x03, 267 0x0F,0x02,0x15,0x02,0x11,0x01,0x0F,0x03,0x0F,0x03,0x10,0x02,0x0F,0x02, 268 0x16,0x02,0x15,0x01,0x0F,0x03,0x0F,0x03,0x19,0x02,0x17,0x02,0x15,0x02, 269 0x11,0x0C,0x10,0x02,0x18,0x03,0x18,0x03,0x17,0x02,0x15,0x02,0x16,0x02, 270 0x10,0x01,0x00,0x00 }; 271 272 void main() 273 { 274 275 InitialSound(); 276 277 278 while(1) 279 { 280 Play(Music_Girl,0,2,350); 281 Play(Music_Same,0,2,350); 282 Play(Music_Two,0,2,350); 283 Play(Music_birth,0,2,350); 284 } 285 }
這個代碼明顯的來自網絡。哈哈。原諒我,這個我就不說明了。哎,還是再說一下步驟吧。。(具體我也不是很懂,不同這個真的很通用,所以還是放到筆記上了。。)
一開始就用MUSICENCODE這個將樂譜轉換為相應的代碼,然后根據音調和音區生成一個新的頻率表。所謂的音區就是降調,平調,升調,這個可以根據轉換的代碼相應位得知。
再計算歌曲的長度,用while就好,用來設置一首歌唱完之后再來一遍還是執行其他動作。
再取出數組數據的奇數位,那個就是表示音長的。然后根據這個計算一個音調唱多久,並將初值賦給定時器。關於定時器的使用,之后會放出。
一個音調唱多久最好的方法就是設置一個基本時長,因為這個例子中,一個四分節拍大概是400ms-500ms,然后有的是1/4拍(100ms),有的2/4拍(200ms),所以就設置一個
50ms的定時器,調用的時候設置一下參數就好了。這樣做的好處就是方便。
接着調用函數讓它唱歌就好
比較一下這兩個方法的優缺點,第一種方法轉碼太復雜,不過聲音很好,分辨率高。第二種方法通用!不同的歌,你只要用工具轉一下碼,然后重新用個數組保存這些值,然后再通過
Play(樂曲名,調號,升降八度,演奏速度)這個函數調用就好了,不過用這種方法實現的效果音質不是很理想。其實最主要的是這種方法對音樂知識不理解也沒關系,而第一種方法還是
需要一定的知識來獲得正確的音調頻率和音長。