bubing120 发表于 2016-1-27 10:05:24

把乐谱播出来,自制简谱播放库



关注我们微信的童鞋可能还记得,昨天我发了一章关于UNO+喇叭就可以播放歌曲的文章。
文章到最后也没有完成整个天空之城歌曲。因为实在是太麻烦了。。于是今天就苦思冥想,想借此机会整理出一个简谱的播放程序。借此来播放所有简谱写成的歌曲。既能提高复用性,也很有趣

那么既然要写库,我们来一步步分析我们要完成的工作吧:1、如何把简谱用程序里的数据表示出来。2、表示出来的数据如何解析。3、解析出来的数据如何播放。
来看看我分解出来的三个问题,可以看到第三个问题非常简单,之前的文章已经解决了这个问题。一个普通的UNO+一个小喇叭+Arduino的tone函数就可以搞定。
一、那么先来解决第一个问题:如何把简谱用程序里的数据表示出来。这里设计到我们如何来设计这个存储数据的结构的问题。一开始我是想兼容现有的乐谱的格式的。现有的乐谱格式倒是有一些,但是却没找到相关的资料,所以也没办法兼容了。所以我只能做一个没办法的办法,自己设计这个数据。Arduino使用的是C++语言,数据处理相比较起高级语言是比较麻烦的,而且芯片本身速度也比较慢,所以我选择的是最简单的方案,用字符串来存储。那么问题来了,我们有哪些数据需要保存?先来看看简谱。
https://mmbiz.qlogo.cn/mmbiz/DAfPnd05rAgr0GyXicJZFqBhNtgFCarC5sDudiaaM3ATKicuSNt4ErAJffYOrthEa6ZtLfECibx9O4aDzRld2mHj2Q/0?wx_fmt=png
看了谱子眼花缭乱。。。简单介绍下:
数字代表音调 1234567分别代表do re mi fa sol la xi数字下面的点代表下降一个8度数字上面的点代表上升一个8度和数字同样的横线 “-”代表延长数字下方有横线,代表8分音符。2个横线代表16分音符。数字前面的#代表这个音调要升半调。
可以看见乐谱上的信息很多,那么我们要一一记录这些信息,最终我设计的数据是这样的。https://mmbiz.qlogo.cn/mmbiz/DAfPnd05rAiaDVEfUaIN21TSrmozKviatVWjGyTLyjlvnxPfffHD48BB8RZKTde2NnKJU87bp3N1ib9G0eNzhL8VQ/0?wx_fmt=png
举个例子:n61f4,n71f4,n10f34,n71f4,n10f3,n30f3代表以下音符。https://mmbiz.qlogo.cn/mmbiz/DAfPnd05rAiaDVEfUaIN21TSrmozKviatVMOdDgPU1HyF3KQZ6y4cMBsk7RiaN08plczpV6I2M5mibfds0ZHiaGOApA/0?wx_fmt=png
n代表没有#号,如果有#,则用s代替。6代表la1f代表6下面有1个点,若上面有一个点则用1s代替4代表这个音符是8分音符1代表全音符
2代表2分音符3代表4分音符4代表8分音符5代表16分音符6代表32分音符可以看见第三个音符后面有个点,这个叫延长符号,及要延长他本身一半的时间,他是一个4分音符,点就代表要延长8分音符的时间。所以f后面有2个数字34,这样即可延长时间。这样就基本把简谱表示出来了。
二、我们再来解决第二个问题解析我们表示的数据。C++解析字符串比较麻烦,所以设计的时候每个音符都用‘,’隔开,这样就方便解析。解析函数如下。
void MELODY::playMelody(char *Melody,int playSpeed){
      const char *d = " ,";
      char *p;
      char cgy;
      int noteDuration=0;
      int i,j;
      uint8_t thisNote1=0,thisNote2=0;
      p = strtok(Melody,d);
      sprintf(cgy, "%s", p);
      while(p)
      {
                char note[]="0000000000";
                noteDuration=0;
                for (i=0;*(p+i)!='\0';i++){
                        note=*(p+i);
                }
                for (int j=4;j<i;j++){
                        int time;
                        switch(note){
                              case '1':time=1;
                              break;
                              case '2':time=2;
                              break;
                              case '3':time=4;
                              break;
                              case '4':time=8;
                              break;
                              case '5':time=16;
                              break;
                              case '6':time=32;
                              break;
                              
                              case '7':time=6;
                              break;
                        }
                        noteDuration += playSpeed/(time);               
                }
                if (this->debug)
                        this->serial->println(noteDuration);
                if (note=='n'){
                        switch (note){
                              case '1':thisNote1=0;
                              break;
                              case '2':thisNote1=2;
                              break;
                              case '3':thisNote1=4;
                              break;
                              case '4':thisNote1=5;
                              break;
                              case '5':thisNote1=7;
                              break;
                              case '6':thisNote1=9;
                              break;
                              case '7':thisNote1=11;
                              break;
                        }
                }
                else if (note=='s'){
                        switch (note){
                              case '1':thisNote1=1;
                              break;
                              case '2':thisNote1=3;
                              break;
                              case '4':thisNote1=6;
                              break;
                              case '5':thisNote1=8;
                              break;
                              case '6':thisNote1=10;
                              break;
                        }
                }
               
                if (note=='f'){
                        switch (note){
                              case '0':thisNote2=4;
                              break;
                              case '1':thisNote2=3;
                              break;
                              case '2':thisNote2=2;
                              break;
                              case '3':thisNote2=1;
                              break;
                              case '4':thisNote2=0;
                              break;
                        }
                }
                else if (note=='s'){
                        switch (note){
                              case '1':thisNote2=5;
                              break;
                              case '2':thisNote2=6;
                              break;
                              case '3':thisNote2=7;
                              break;
                              case '4':thisNote2=8;
                              break;
                        }
                }
                if (note=='0'){
                        thisNote2=0;
                        thisNote1=0;
                }
                tone(this->pin, notefr,noteDuration);
                     
                int pauseBetweenNotes = noteDuration*1.1;
                delay(pauseBetweenNotes);
               
                noTone(this->pin);
                if (this->debug)
                        this->serial->println(cgy);
                p=strtok(NULL,d);
                sprintf(cgy, "%s", p);
      }
}头文件中,我将每个音对应的频率设置成为一个数组,方便解析。https://mmbiz.qlogo.cn/mmbiz/DAfPnd05rAiaDVEfUaIN21TSrmozKviatVYIdq2wPZBwFyhMRB5ewpSGNDKkYQHJ8WcPFibqpWaGfC3Au9icOWRBhA/0?wx_fmt=png
至此,左右的工作的都完成了,只需要将简谱输入成我刚才的格式就可以播放音乐啦,当然还是比较麻烦,但比上次的效率高了很多,上次2个小时大概输入了1半,这次半个小时就输入了整首歌。
现在附上程序的地址,想要库的可以去下载哦:https://github.com/rainbowyu/LD_ArduinoLib/tree/V1.02如果喜欢观看类似科技新奇事物,以及了解创客圈最新资讯,或者您对Arduino有所耳闻,可以关注我们微信公众号,一定会带给您最新的资讯,最实用的教程,以及创客最新的玩意。

https://mmbiz.qlogo.cn/mmbiz/DAfPnd05rAjB48NqRlBgGDnPWLRdtl0Q9aEG6lo94JInmQPLzzIrNMZ6bwWeftbBcAFtnr3sd62D2h2KbBQ1nA/640?wx_fmt=png

zhanghuanqian 发表于 2016-1-27 12:37:46

准备自己弄一个.
已关注...

bubing120 发表于 2016-1-27 13:18:30

zhanghuanqian 发表于 2016-1-27 12:37 static/image/common/back.gif
准备自己弄一个.
已关注...

音乐放不上来 有点可惜
页: [1]
查看完整版本: 把乐谱播出来,自制简谱播放库