极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 15407|回复: 2

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

[复制链接]
发表于 2016-1-27 10:05:24 | 显示全部楼层 |阅读模式


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


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

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

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


看了谱子眼花缭乱。。。简单介绍下:

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

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

举个例子:
n61f4,n71f4,n10f34,n71f4,n10f3,n30f3代表以下音符。

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. }
复制代码
头文件中,我将每个音对应的频率设置成为一个数组,方便解析。

至此,左右的工作的都完成了,只需要将简谱输入成我刚才的格式就可以播放音乐啦,当然还是比较麻烦,但比上次的效率高了很多,上次2个小时大概输入了1半,这次半个小时就输入了整首歌。

现在附上程序的地址,想要库的可以去下载哦:
https://github.com/rainbowyu/LD_ArduinoLib/tree/V1.02
如果喜欢观看类似科技新奇事物,以及了解创客圈最新资讯,或者您对Arduino有所耳闻,可以关注我们微信公众号,一定会带给您最新的资讯,最实用的教程,以及创客最新的玩意。




回复

使用道具 举报

发表于 2016-1-27 12:37:46 | 显示全部楼层
准备自己弄一个.
已关注...
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-1-27 13:18:30 | 显示全部楼层
zhanghuanqian 发表于 2016-1-27 12:37
准备自己弄一个.
已关注...

音乐放不上来 有点可惜
回复 支持 反对

使用道具 举报

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

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-15 03:08 , Processed in 0.037099 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表