单⽚机通过蜂鸣器播放任意⾳乐代码实现(2):⾳乐单⽚机代码⾃动⽣成单⽚机通过蜂鸣器播放任意⾳乐代码实现(2):⾳乐的单⽚机代码⾃动⽣成
上⼀节我们已经构建了基于51单⽚机的蜂鸣器⾳乐播放环境,接下来只需要⼿动或者⾃动添加⾳乐代码便能实现不同⾳乐的播放。当然,第⼆节将告诉你如何⾃动⽣成这些⾳乐代码。
周笔畅 笔记
所⽤软件
MuseScore 3;⽤于转换mid⽂件为musicxml⽂件
C/C++ IDE(本⽂为Visual Studio 2019)
1.⽣成⾳乐对应的MID⽂件
可以直接下载mid格式⾳乐⽂件,也可以将某段⾳乐转换为mid⽂件。最好是直接使⽤mid格式⾳乐⽂件,可以⼤⼤节省时间。在本例中使⽤的便是现成的mid⽂件。如果你需要将任意⾳乐转为mid⽂件,可以参考⽹络上的教程,本⽂不再赘诉。下图为本⽂所⽤的mid格式⽂件:
2.将MID⽂件转为MUSICXML⽂件
直接将mid⽂件拖拽进或是添加进MuseScore 3,打开后如下图所⽰:
点击“⽂件”→“导出”,按照下图设置输出⽂件:
3.提取MUSICXML⽂件关键字,⾃动⽣成⾳乐代码
该部分代码由C语⾔实现,主要功能是寻musicxml格式⽂件中有关⾳⾼、⾳长的关键字,该部分代码如下:
#include<iostream>
#include<stdlib.h>
int zifuchange(char x);
char word[11]={0};//⾳符、节拍数组
int yinfushu=0;//⾳符个数统计
int error=0;//错误计数
someday iuint flag=0;
int jiepai=0;
int print=0;
int divisions=1;
int jiepaishichang=1;
int main()
int main()
{
int i=0;
int x;
int j;
FILE *fp;//⽂件指针
fp =fopen("D:\\(34拍)丁⾹花(旋律).musicxml","r");//以只读⽅式打开⽂件if(fp==NULL)
printf("打开⽂件失败!\n");
else
{
printf("请输⼊每分钟节拍数:");
scanf("%d",&jiepai);
for(i=0;;i++)
{
fseek(fp,i,SEEK_SET);
word[0]=fgetc(fp);
if(word[0]==EOF)
break;
//printf("%c",word[0]);
fseek(fp,i+1,SEEK_SET);
word[1]=fgetc(fp);
if(word[1]==EOF)
break;
//printf("%c",word[1]);
fseek(fp,i+2,SEEK_SET);
word[2]=fgetc(fp);
if(word[2]==EOF)
break;
//printf("%c",word[2]);
fseek(fp,i+3,SEEK_SET);
word[3]=fgetc(fp);
if(word[3]==EOF)
break;
//printf("%c",word[3]);
fseek(fp,i+4,SEEK_SET);
word[4]=fgetc(fp);
if(word[4]==EOF)
break;
//printf("%c",word[4]);
fseek(fp,i+5,SEEK_SET);
word[5]=fgetc(fp);
if(word[5]==EOF)
break;
/
/printf("%c",word[5]);
fseek(fp,i+6,SEEK_SET);
word[6]=fgetc(fp);
if(word[6]==EOF)
break;
//printf("%c",word[5]);
fseek(fp,i+7,SEEK_SET);
提示音word[7]=fgetc(fp);
if(word[7]==EOF)
break;
//printf("%c",word[5]);
fseek(fp,i+8,SEEK_SET);
word[8]=fgetc(fp);
if(word[8]==EOF)
break;
//printf("%c",word[5]);
fseek(fp,i+9,SEEK_SET);
word[9]=fgetc(fp);
if(word[9]==EOF)
break;
//printf("%c",word[5]);
mc热狗
fseek(fp,i+10,SEEK_SET);
fseek(fp,i+10,SEEK_SET);
word[10]=fgetc(fp);
if(word[10]==EOF)
break;
//printf("%c",word[5]);
if(word[0]=='<'&&word[1]=='d'&&word[2]=='i'&&word[3]=='v'&&word[4]=='i'&&word[5]=='s'
&&word[6]=='i'&&word[7]=='o'&&word[8]=='n'&&word[9]=='s'&&word[10]=='>')//判断此处连续11个字符是否为<divisions> {
for(j=0;;j++)
{
fseek(fp,i+11+j,SEEK_SET);
if(fgetc(fp)=='<')
break;
else
{
if(j==0)
{
fseek(fp,i+11+j,SEEK_SET);
divisions=zifuchange(fgetc(fp));
}
else
{
fseek(fp,i+11+j,SEEK_SET);
divisions=10*divisions+zifuchange(fgetc(fp));
}
}
}
printf("%d,",divisions);
break;
}
}
while(1)//解析⾳符
{
fseek(fp,i,SEEK_SET);
word[0]=fgetc(fp);
if(word[0]==EOF)
break;
//printf("%c",word[0]);
fseek(fp,i+1,SEEK_SET);
word[1]=fgetc(fp);
if(word[1]==EOF)
break;
//printf("%c",word[1]);
fseek(fp,i+2,SEEK_SET);
word[2]=fgetc(fp);
if(word[2]==EOF)
break;
//printf("%c",word[2]);
fseek(fp,i+3,SEEK_SET);
word[3]=fgetc(fp);
if(word[3]==EOF)
break;
//printf("%c",word[3]);
fseek(fp,i+4,SEEK_SET);
word[4]=fgetc(fp);
if(word[4]==EOF)
break;
//printf("%c",word[4]);
fseek(fp,i+5,SEEK_SET);
刘若英唱后来为什么哭
word[5]=fgetc(fp);
if(word[5]==EOF)
break;
//printf("%c",word[5]);
if(word[0]=='<'&&word[1]=='s'&&word[2]=='t'&&word[3]=='e'&&word[4]=='p'&&word[5]=='>')//判断此处连续六个字符是否为<step> {
fseek(fp,i+6,SEEK_SET);
word[6]=fgetc(fp);
switch(word[6])
{
case'C':word[6]='1';break;
case'D':word[6]='2';break;
林永健主演的电视剧case'E':word[6]='3';break;
case'F':word[6]='4';break;
case'G':word[6]='5';break;
case'A':word[6]='6';break;
case'B':word[6]='7';break;
default:break;
}
fseek(fp,i+32,SEEK_SET);
word[7]=fgetc(fp);
if(word[7]=='>')
{
fseek(fp,i+33,SEEK_SET);
word[7]=fgetc(fp);
for(j=0;;j++)
{
fseek(fp,i+81+j,SEEK_SET);
if(fgetc(fp)=='<')
break;
else
{
if(j==0)
{
fseek(fp,i+81+j,SEEK_SET);
jiepaishichang=zifuchange(fgetc(fp));
}
else
{
fseek(fp,i+81+j,SEEK_SET);
jiepaishichang=10*jiepaishichang+zifuchange(fgetc(fp));
}
}
}
}
else
{
fseek(fp,i+61,SEEK_SET);
word[7]=fgetc(fp);
if(word[7]=='<')
{
fseek(fp,i+60,SEEK_SET);
word[7]=fgetc(fp);
}
for(j=0;;j++)
{
fseek(fp,i+109+j,SEEK_SET);
if(fgetc(fp)=='<')
break;
else
{
if(j==0)
{
fseek(fp,i+109+j,SEEK_SET);