5343浏览
查看: 5343|回复: 8

[进阶] 把乐谱播出来,自制简谱播放库

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


那么既然要写库,我们来一步步分析我们要完成的工作吧:
1、如何把简谱用程序里的数据表示出来。
2、表示出来的数据如何解析。
3、解析出来的数据如何播放。

来看看我分解出来的三个问题,可以看到第三个问题非常简单,之前的文章已经解决了这个问题。一个普通的UNO+一个小喇叭+Arduino的tone函数就可以搞定。

一、那么先来解决第一个问题:
如何把简谱用程序里的数据表示出来。
这里设计到我们如何来设计这个存储数据的结构的问题。一开始我是想兼容现有的乐谱的格式的。现有的乐谱格式倒是有一些,但是却没找到相关的资料,所以也没办法兼容了。
所以我只能做一个没办法的办法,自己设计这个数据。
Arduino使用的是C++语言,数据处理相比较起高级语言是比较麻烦的,而且芯片本身速度也比较慢,所以我选择的是最简单的方案,用字符串来存储。
那么问题来了,我们有哪些数据需要保存?先来看看简谱。

640-52.jpeg
看了谱子眼花缭乱。。。简单介绍下:

数字代表音调 1234567分别代表do re mi fa sol la xi
数字下面的点代表下降一个8度
数字上面的点代表上升一个8度
和数字同样的横线 “-”代表延长
数字下方有横线,代表8分音符。2个横线代表16分音符。
数字前面的#代表这个音调要升半调。

可以看见乐谱上的信息很多,那么我们要一一记录这些信息,最终我设计的数据是这样的。
640-20.png

举个例子:
n61f4,n71f4,n10f34,n71f4,n10f3,n30f3代表以下音符。
640-21.png
n代表没有#号,如果有#,则用s代替。
6代表la
1f代表6下面有1个点,若上面有一个点则用1s代替
4代表这个音符是8分音符
1代表全音符
2代表2分音符
3代表4分音符
4代表8分音符
5代表16分音符
6代表32分音符
可以看见第三个音符后面有个点,这个叫延长符号,及要延长他本身一半的时间,他是一个4分音符,点就代表要延长8分音符的时间。所以f后面有2个数字34,这样即可延长时间。
这样就基本把简谱表示出来了。

二、我们再来解决第二个问题
解析我们表示的数据。
C++解析字符串比较麻烦,所以设计的时候每个音符都用‘,’隔开,这样就方便解析。
解析函数如下。

  1. void MELODY::playMelody(char *Melody,int playSpeed){
  2.         const char *d = " ,";
  3.         char *p;
  4.         char cgy[10];
  5.         int noteDuration=0;
  6.         int i,j;
  7.         uint8_t thisNote1=0,thisNote2=0;
  8.         p = strtok(Melody,d);
  9.         sprintf(cgy, "%s", p);
  10.         while(p)
  11.         {
  12.                 char note[]="0000000000";
  13.                 noteDuration=0;
  14.                 for (i=0;*(p+i)!='\0';i++){
  15.                         note=*(p+i);
  16.                 }
  17.                 for (int j=4;j<i;j++){
  18.                         int time;
  19.                         switch(note[j]){
  20.                                 case '1':time=1;
  21.                                 break;
  22.                                 case '2':time=2;
  23.                                 break;
  24.                                 case '3':time=4;
  25.                                 break;
  26.                                 case '4':time=8;
  27.                                 break;
  28.                                 case '5':time=16;
  29.                                 break;
  30.                                 case '6':time=32;
  31.                                 break;
  32.                               
  33.                                 case '7':time=6;
  34.                                 break;
  35.                         }
  36.                         noteDuration += playSpeed/(time);               
  37.                 }
  38.                 if (this->debug)
  39.                         this->serial->println(noteDuration);
  40.                 if (note[0]=='n'){
  41.                         switch (note[1]){
  42.                                 case '1':thisNote1=0;
  43.                                 break;
  44.                                 case '2':thisNote1=2;
  45.                                 break;
  46.                                 case '3':thisNote1=4;
  47.                                 break;
  48.                                 case '4':thisNote1=5;
  49.                                 break;
  50.                                 case '5':thisNote1=7;
  51.                                 break;
  52.                                 case '6':thisNote1=9;
  53.                                 break;
  54.                                 case '7':thisNote1=11;
  55.                                 break;
  56.                         }
  57.                 }
  58.                 else if (note[0]=='s'){
  59.                         switch (note[1]){
  60.                                 case '1':thisNote1=1;
  61.                                 break;
  62.                                 case '2':thisNote1=3;
  63.                                 break;
  64.                                 case '4':thisNote1=6;
  65.                                 break;
  66.                                 case '5':thisNote1=8;
  67.                                 break;
  68.                                 case '6':thisNote1=10;
  69.                                 break;
  70.                         }
  71.                 }
  72.                
  73.                 if (note[3]=='f'){
  74.                         switch (note[2]){
  75.                                 case '0':thisNote2=4;
  76.                                 break;
  77.                                 case '1':thisNote2=3;
  78.                                 break;
  79.                                 case '2':thisNote2=2;
  80.                                 break;
  81.                                 case '3':thisNote2=1;
  82.                                 break;
  83.                                 case '4':thisNote2=0;
  84.                                 break;
  85.                         }
  86.                 }
  87.                 else if (note[3]=='s'){
  88.                         switch (note[2]){
  89.                                 case '1':thisNote2=5;
  90.                                 break;
  91.                                 case '2':thisNote2=6;
  92.                                 break;
  93.                                 case '3':thisNote2=7;
  94.                                 break;
  95.                                 case '4':thisNote2=8;
  96.                                 break;
  97.                         }
  98.                 }
  99.                 if (note[1]=='0'){
  100.                         thisNote2=0;
  101.                         thisNote1=0;
  102.                 }
  103.                 tone(this->pin, notefr[thisNote2][thisNote1],noteDuration);
  104.                        
  105.                 int pauseBetweenNotes = noteDuration*1.1;
  106.                 delay(pauseBetweenNotes);
  107.                
  108.                 noTone(this->pin);
  109.                 if (this->debug)
  110.                         this->serial->println(cgy);
  111.                 p=strtok(NULL,d);
  112.                 sprintf(cgy, "%s", p);
  113.         }
  114. }
复制代码


头文件中,我将每个音对应的频率设置成为一个数组,方便解析。
640-53.jpeg
至此,左右的工作的都完成了,只需要将简谱输入成我刚才的格式就可以播放音乐啦,当然还是比较麻烦,但比上次的效率高了很多,上次2个小时大概输入了1半,这次半个小时就输入了整首歌。

现在附上程序的地址,想要库的可以去下载哦:
https://github.com/rainbowyu/LD_ArduinoLib/tree/V1.02



大连林海  初级技神

发表于 2016-1-27 10:48:01

效率高了
回复

使用道具 举报

dsweiliang  初级技神

发表于 2016-1-27 10:54:18

不明觉厉
回复

使用道具 举报

何处不江南  初级技匠
 楼主|

发表于 2016-1-27 11:06:23


一直高产  哈哈
回复

使用道具 举报

大连林海  初级技神

发表于 2016-1-27 14:20:27


不明觉厉
回复

使用道具 举报

孙毅  初级技匠

发表于 2016-1-27 23:59:33

请教一个问题,能否给一个 音调和 频率的对照表啊?
比如  1 2 3 4 5 6 7, 低 1 2 3 4 5 6 7 ,高音 1 2 3 4 5 6 7 分别对应的频率是什么
回复

使用道具 举报

何处不江南  初级技匠
 楼主|

发表于 2016-1-28 10:21:25

孙毅 发表于 2016-1-27 23:59
请教一个问题,能否给一个 音调和 频率的对照表啊?
比如  1 2 3 4 5 6 7, 低 1 2 3 4 5 6 7 ,高音 1 2 3 ...

库里面已经给出 你可以下载自行查看
回复

使用道具 举报

吹口琴的钢铁侠  初级技匠

发表于 2016-1-28 19:54:45

还需要一个直接从简谱输出对应音符和持续时间的程序啊(逃
回复

使用道具 举报

何处不江南  初级技匠
 楼主|

发表于 2016-1-29 11:57:36

吹口琴的钢铁侠 发表于 2016-1-28 19:54
还需要一个直接从简谱输出对应音符和持续时间的程序啊(逃

简谱一般都是图。。  那就复杂了。。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2022 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail