把乐谱播出来,自制Arduino简谱播放库 您所在的位置:网站首页 乐谱播放乐谱 把乐谱播出来,自制Arduino简谱播放库

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

2023-07-17 11:17| 来源: 网络整理| 查看: 265

文章到最后也没有完成整个天空之城歌曲。因为实在是太麻烦了。。

于是今天就苦思冥想,想借此机会整理出一个简谱的播放程序。借此来播放所有简谱写成的歌曲。既能提高复用性,也很有趣。先来看看成果吧。

 

那么既然要写库,我们来一步步分析我们要完成的工作吧:

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++解析字符串比较麻烦,所以设计的时候每个音符都用‘,’隔开,这样就方便解析。

解析函数如下。

void MELODY::playMelody(char *Melody,int playSpeed){ const char *d = " ,"; char *p; char cgy[10]; 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[i]=*(p+i); } for (int j=4;j int time; switch(note[j]){ 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[0]=='n'){ switch (note[1]){ 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[0]=='s'){ switch (note[1]){ 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[3]=='f'){ switch (note[2]){ 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[3]=='s'){ switch (note[2]){ case '1':thisNote2=5; break; case '2':thisNote2=6; break; case '3':thisNote2=7; break; case '4':thisNote2=8; break; } } if (note[1]=='0'){ thisNote2=0; thisNote1=0; } tone(this->pin, notefr[thisNote2][thisNote1],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); } }

头文件中,我将每个音对应的频率设置成为一个数组,方便解析。

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

现在附上程序的地址,想要库的可以去下载哦:

https://github.com/rainbowyu/LD_ArduinoLib/tree/V1.02



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有