IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 单片机通过蜂鸣器播放任意音乐代码实现(2):音乐单片机代码自动生成 -> 正文阅读

[嵌入式]单片机通过蜂鸣器播放任意音乐代码实现(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;//错误计数
int flag=0;
int jiepai=0;
int print=0;
int divisions=1;
int jiepaishichang=1;
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]);
				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);
									jiepaishichang=zifuchange(fgetc(fp));
								}
								else
								{
									fseek(fp,i+109+j,SEEK_SET);
									jiepaishichang=10*jiepaishichang+zifuchange(fgetc(fp));
								}

							}

					}

					}
				/*	if(word[7]=='4')
						word[7]='2';
					else if(word[7]>'4')
						word[7]='3';
					else if(word[7]<'4') 
						word[7]='1';
						*/


					/*
					if(word[8]>'9'|word[8]<'0')
					{
						error++;
					}
					switch(word[8])
					{
						case '1':word[8]='1';break;
						case '2':word[8]='2';break;
						case '3':word[8]='3';break;
						case '4':word[8]='4';break;
						case '6':word[8]='6';break;
						case '8':word[8]='8';break;
						case '12':word[8]='12';break;
						default:break;
					}
					*/

					if(flag==0)
					{
						printf("%d,",jiepai);
						flag++;
						printf("\n");
					}
					if(word[6]<'0'||word[6]>'9')
						error++;
					if(word[7]<'0'||word[7]>'9')
						error++;



						printf("%c,%c,%d,",word[6],word[7],jiepaishichang);
					yinfushu++;//音符计数+1
					print++;
					if(print==3)
					{
						printf("\n");
						print=0;
					}

				}
				i++;
			}
			i=0;
	}
	fclose(fp);//关闭文件
	printf("0");
	printf("\n");
	printf("解析完成,共有%d个音符,共有%d个错误\n",yinfushu,error);
	system("pause");
	return 0;
}

int zifuchange(char x)
{
	switch(x)
	{
		case '0':return 0;break;
		case '1':return 1;break;
		case '2':return 2;break;
		case '3':return 3;break;
		case '4':return 4;break;
		case '5':return 5;break;
		case '6':return 6;break;
		case '7':return 7;break;
		case '8':return 8;break;
		case '9':return 9;break;
		default:return -1;break;
	}
}

???????使用该部分代码只需要更改以下文件路径为自己的文件路径即可,该部分代码如下:

fp = fopen("D:\\(34拍)丁香花(旋律).musicxml", "r"); 

???????将上面获取到的爱拼才会赢的musicxml格式文件路径添加进去后,手动输入每分钟节拍数(可自定义,也可填简谱节拍数)控制台会自动生成音乐代码,复制并粘贴进上一节所述的music[]数组即可。本次生成的代码如下:

4,89,
5,4,2,5,4,1,6,4,1,
1,5,6,6,4,2,5,4,2,
6,4,1,1,5,1,6,4,2,
5,4,2,3,4,12,3,4,2,
3,4,1,5,4,1,6,4,6,
1,5,2,5,4,2,3,5,2,
3,5,1,2,5,1,1,5,2,
2,5,12,1,5,2,2,5,2,
3,5,2,3,5,2,2,5,2,
3,5,1,2,5,1,1,5,6,
6,4,1,1,5,1,2,5,2,
2,5,2,2,5,1,1,5,1,
6,4,2,5,4,8,6,4,2,
5,4,1,6,4,1,5,4,2,
1,5,2,6,4,2,5,4,2,
3,4,2,1,4,4,2,4,12,
1,4,2,2,4,2,3,4,6,
5,4,2,5,4,2,3,4,2,
1,5,2,7,4,2,6,4,6,
6,4,2,6,4,4,1,5,2,
2,5,1,3,5,1,5,5,4,
3,5,4,2,5,2,1,5,2,
5,4,2,1,5,2,2,5,1,
3,5,1,2,5,12,3,5,1,
2,5,1,1,5,4,2,5,1,
1,5,1,6,4,4,1,5,2,
3,4,1,1,5,1,1,5,2,
6,4,2,1,5,4,6,4,1,
5,4,1,3,4,2,5,4,16,
5,4,2,6,4,2,1,5,2,
6,4,2,6,4,8,3,5,2,
3,5,2,3,5,2,3,5,1,
2,5,1,2,5,6,2,5,1,
1,5,1,6,4,6,3,5,2,
2,5,4,1,5,1,2,5,1,
1,5,1,6,4,1,1,5,16,
5,4,2,6,4,2,1,5,6,
6,4,2,5,4,2,6,4,1,
1,5,1,6,4,2,5,4,2,
3,4,12,3,4,2,3,4,1,
5,4,1,6,4,6,1,5,2,
5,4,2,3,5,2,3,5,1,
2,5,1,1,5,2,2,5,12,
1,5,2,2,5,2,3,5,2,
3,5,2,2,5,2,3,5,1,
2,5,1,1,5,6,6,4,1,
1,5,1,2,5,2,2,5,2,
2,5,1,1,5,1,6,4,2,
5,4,8,6,4,2,5,4,1,
6,4,1,5,4,2,1,5,2,
6,4,2,5,4,2,3,4,2,
1,4,4,2,4,12,1,4,2,
2,4,2,3,4,6,5,4,2,
5,4,2,3,4,2,1,5,2,
7,4,2,6,4,6,6,4,2,
6,4,4,1,5,2,2,5,1,
3,5,1,5,5,2,3,5,4,
2,5,2,1,5,2,5,4,2,
1,5,2,2,5,1,3,5,1,
2,5,12,3,5,1,2,5,1,
1,5,4,2,5,1,1,5,1,
6,4,4,1,5,2,3,4,1,
1,5,1,1,5,2,6,4,2,
1,5,4,6,4,1,5,4,1,
3,4,2,5,4,16,5,4,2,
6,4,2,1,5,2,6,4,2,
6,4,8,3,5,2,3,5,2,
3,5,2,3,5,1,2,5,1,
2,5,6,2,5,1,1,5,1,
6,4,6,3,5,2,2,5,4,
1,5,1,2,5,1,1,5,1,
6,4,1,1,5,16,1,4,2,
2,4,2,3,4,6,5,4,2,
5,4,2,3,4,2,1,5,2,
7,4,2,6,4,6,6,4,2,
6,4,4,1,5,2,2,5,1,
3,5,1,5,5,4,3,5,4,
2,5,2,1,5,2,5,4,2,
1,5,2,2,5,1,3,5,1,
2,5,12,3,5,1,2,5,1,
1,5,4,2,5,1,1,5,1,
6,4,4,1,5,2,3,4,1,
1,5,1,1,5,2,6,4,2,
1,5,4,6,4,1,5,4,1,
3,4,2,5,4,16,5,4,2,
6,4,2,1,5,2,6,4,2,
6,4,8,3,5,2,3,5,2,
3,5,2,3,5,1,2,5,1,
2,5,6,2,5,1,1,5,1,
6,4,6,3,5,2,2,5,4,
1,5,1,2,5,1,1,5,1,
6,4,1,1,5,16,0
解析完成,共有275个音符,共有0个错误

???????至此,你便可以利用借助单片机通过蜂鸣器播放任意音乐!试试吧!

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-12 16:46:05  更:2021-08-12 16:46:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 20:47:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码