单片机演奏简单的音乐

更新时间:2024-05-21 07:30:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

为了让单片机演奏音乐,看了不少的资料,现在整理了相关的资料,让你一次看懂音调、节拍的确定方法,看懂简谱,懂得编写用来演奏音乐的程序。 单片机不能像其他的专业的乐器一样能奏出多种音色的声音,即不包含相应幅度的谐波频率。单片机演奏的音乐基本都是点单音频率。因此单片机演奏音乐比较简单,只需弄清楚“音调”和“节拍”两个概念即可。

音调表示一个音符唱的多高的频率。 节拍表示一个音符唱多长的时间。 那么音调、节拍该怎样确定呢? 首先来看音调的确定:

不同音高的乐音是用C、D、E、F、G、A、B来表示,这7个字母就是音乐的音名,它们一般依次唱成DO、RE、MI、FA、SO、LA、SI,即唱成简谱的1、2、3、4、5、6、7,相当于汉字“多来米发梭拉西”的读音,这是唱曲时乐音的发音,所以叫“音调”,即Tone。 在音乐中常把五线谱中央C 上方的A 音定为标准音高,其频率f=440Hz。当两个声音信号的频率相差一倍时,也即f2=2*f1时,则称f2比f1 高一个倍频程, 例如高音DO的频率(1046Hz)刚好是中音DO的频率(523Hz)的一倍,中音DO的频率(523Hz)刚好是低音DO频率(266 Hz)的一倍;同样的,高音RE的频率(1175Hz)刚好是中音RE的频率(587Hz)的一倍,中音RE的频率(587Hz)刚好是低音RE频率(294 Hz)的一倍。在音乐学中称它相差一个八度音。在一个八度音内,有12个半音。以1—i 八音区为例, 12 个半音是:1—#1、#1—2、2—#2、#2—3、3—4、4—#4,#4—5、5 一#5、#5—6、6—#6、#6—7、7—i。那些在它们的左上角加上﹟号或者b号的叫变化音。﹟叫升记号,表示把音在原来的基础上升高半音,b叫降记音,表示在原来的基础上降低半音。这12 个音阶的分度基本上是以对数关系来划分的。如果我们只要知道了这十二个音符的音高,也就是其基本音调的频率,我们就可根据倍频程的关系得到其他音符基本音调的频率。基本频率看下表:

低音 Do Do﹟ Re Re﹟ Mi Fa Fa﹟ So So﹟ La La﹟ Si

频率 262 277 294 311 330 349 370 392 415 440 464 494

中音 Do Do﹟ Re Re﹟ Mi Fa Fa﹟ So So﹟ La La﹟ Si

频率 523 554 587 622 659 698 740 784 831 880 932 988

高音 Do Do﹟ Re Re﹟ Mi Fa Fa﹟ So So﹟ La La﹟ Si

频率 1046 1109 1175 1245 1318 1397 1480 1568 1661 1760 1865 1976

知道了一个音符的频率后,采用的方法就是通过一个延时程序,延时对应频率周期的二分之一周期(即t=1/2T)后,将单片机上连接蜂鸣器的I/O口来回取反,或者说来回清零,置位,从而让蜂鸣器发出声音,为了让单片机发出不同频率的声音,我们只需将不同的延时时间值t赋给延时程序即可实现。一般说来,常采用的方法就是通过单片机的定时器定时中断,将单片机上对应蜂鸣器的I/O口来回取反,或者说来回清零,置位,从而让蜂鸣器发出声音,为了让单片机发出不同频率的声音,我们只需将定时器予置不同的定时值就可实现。

那么怎样确定一个频率所对应的定时器的定时值呢?以标准音高A为例: A的频率f = 440 Hz,其对应的周期为: T = 1/ f = 1/440 =2272μs

由上图可知,单片机上对应蜂鸣器的I/O口来回取反的时间应为: t = T/2 = 2272/2 = 1136μs

这个时间t也就是单片机上定时器应有的中断触发时间。一般情况下,单片机奏乐时,其定时器为工作方式1,它以振荡器的十二分频信号为计数脉冲。设振荡器频率为f0,则定时器的予置初值由下式来确定: t = 12 *(TALL – THL)/ f0

式中TALL = 2^16 = 65536,THL为定时器待确定的计数初值。 因此定时器的高低计数器的初值为: TH = THL / 256 = ( TALL – t* f0/12) / 256 TL = THL % 256 = ( TALL – t* f0/12) %6

将t=1136μs代入上面两式(注意:计算时应将时间和频率的单位换算一致),即可求出标准音高A在单片机晶振频率f0=12Mhz,定时器在工作方式1下的定时器高低计数器的予置初值为 :

TH 440Hz = (65536 – 1136 * 12/12) /256 = FBH TL 440Hz = (65536 – 1136 * 12/12)%6 = 90H

根据上面的求解方法,我们就可求出其他音调相应的计数器的予置初值。如下表: (12MHz的单片机晶振)音调对应的计数器预置值:

低音 1 1﹟ 2 2﹟ 3 4 4﹟ 5 5﹟ 6 6﹟ 7

频率 262 277 294 311 330 349 370 392 415 440 464 494

TH TL F88B F8F2 F95B F9B7 FA14 FA66 FAB9 FB03 FB4A FB8F FBCF FC0B

中音 1 1﹟ 2 2﹟ 3 4 4﹟ 5 5﹟ 6 6﹟ 7

频率 523 554 587 622 659 698 740 784 831 880 932 988

TH TL FC43 FC78 FCAB FCDB FD08 FD33 FD5B FD81 FDA5 FDC7 FDE7 FE05

高音 1 1﹟ 2 2﹟ 3 4 4﹟ 5 5﹟ 6 6﹟ 7

频率 1046 1109 1175 1245 1318 1397 1480 1568 1661 1760 1865 1976

TH TL FB21 FE3C FE55 FE6D FE84 FE99 FEAD FEC0 FE02 FEE3 FEF3 FF02

知道了音调的确定方法,那就再来看看节拍的确定吧:

若要构成音乐,光有音调是不够的,还需要节拍,让音乐具有旋律(固定的律动),而且可以调节各个音的快满度。确定节拍的方法如下

1. 在一张乐谱中,我们经常会看到这样的表达式, 等

等,这里1=C,1=G表示乐谱的曲调,和我们前面所谈的音调有很大的关联(呵呵,其

关联就是升或降音调),就是用来表示节拍的。以乐谱中以四分音符为节拍,每一小结有三拍。比如:

为例加以说明,它表示

其中1 、2 为一拍,3、4、5 为一拍,6为一拍共三拍。1 、2的时长为四分音符的一半,即为八分音符长,3、4的时长为八分音符的一半,即为十六分音符长,5 的时长为四分音符的一半,即为八分音符长,6的时长为四分音符长。

那么一拍到底该唱多长呢?一般说来,如果乐曲没有特殊说明,一拍的时长大约为400—500ms 。如果我们以一拍的时长为400ms 为例,则当以四分音符为一节拍时,四分音符的时长就为400ms,八分音符(即1/2拍)的时长就为200ms,十六分音符(1/4拍)的时长就为100ms。只要设定延迟时间就可求得节拍的时间。假设1/4拍为1DELAY,则1拍应为4DELAY,以此类推。所以只要求得1/4拍的DELAY时间,其余的节拍就是它的倍数。

2. 若知道了一首歌曲的每分钟的节拍数,也可求出每一节拍的时间。以每分钟94拍为例:

一拍的时间=60/94=0.64s=640ms 节拍 1/4 3/4 1又1/4 1又3/4 2又1/4 2又3/4 3又1/4 3又3/4 时间S 0.16 0.48 0.80 1.12 1.44 1.76 2.08 2.40

关于音符长短的知识:

在简谱中,1、2、3、4、5、6、7这七个基本音符,不仅表示音的高低,而且还是表示时值长短的基本单位,称为四分音符,其他音符均是在四分音符的基础上,用加记短横线\和附点\

1.单纯音符

在简谱中,如果音符时值的长短用短横线\-\表示,就称为单纯音符。单纯音符除四分音符外,有以下两种形式:

⑴ 在基本音符右侧加记一条短横线,表示增长一个四分音符的时值。这类加记在音符右侧、使音符时值增长的短横线,称为增时线。增时线越多,音符的时值越长。

⑵ 在基本音符下方加记一条短横线,表示缩短原音符时值的一半。这类加记在音符下方、使音符时值缩短的短横线,称为减时线。减时线越多,音符的时值越短。

单纯音符的名称以全音符为标准而定。如:全音符的二分之一称为二分音符,全音符的四分之一称为四分音符,其余类推。

常见的单纯音符的名称、写法及时值长短列表如下:

2.附点音符

在简谱中,加记在单纯音符的右侧的.使音符时值增长的小圆点·,称为附点.加记附点的音符称为附点音符。

附点本身并无一定的长短,其长短由前面的单纯音符来决定。附点的意义在于增长原音符时值的一半,常用于四分音符和小于四分音符的各种音符之后。例如:

附点四分音符:

附点八分音符: 附点十六分音符:

在简谱中,大于四分音符的单纯音符通常不加记附点,而用增时线来表示。例如; 附点二分音符:

不用

表示。

带有两个附点的单纯音符称为复附点音符,第二个附点表示增长第一个附点时值的一

半,即音符时值的四分之-。例如:

复附点常用于器乐曲中,在歌曲中很少使用。

通过上面关于一个音符音调和节拍的确定方法,我们就可以在单片机上实现演奏音乐了。具体的实现方法为:将乐谱中的每个音符的音调及节拍变换成相应的音调参数和节拍参数,将他们做成数据表格,存放在存储器中,通过程序取出一个音符的相关参数,播放该音符,该音符唱完后,接着取出下一个音符的相关参数??,如此直到播放完毕最后一个音符,根据需要也可循环不停地播放整个乐曲。另外,对于乐曲中的休止符,一般用00H表示,乐曲结束音符用FFH来表示。

说到这里看看你能不能够看懂简谱了呢,来对照着简谱看看

为了方便编程下面再来看看编码吧

do re mi fa so la si分别编码为1~7,重音do编为8,重音re编为9,停顿编为0。播放长度以十六分音符为单位(在本程序中为165ms),一拍即四分音符等于4个十六分音符,编为4,其它的播放时间以此类推。音调作为编码的高4位,而播放时间作为低4位,如此音调和节拍就构成了一个编码。以0xff作为曲谱的结束标志。

举例1:音调do,发音长度为两拍,即二分音符,将其编码为0x18。 举例2:音调re,发音长度为半拍,即八分音符,将其编码为0x22

歌曲播放的设计。先将歌曲的简谱进行编码,储存在一个数据类型为unsigned char 的数组中。程序从数组中取出一个数,然后分离出高4位得到音调,接着找出相应的值赋给定时器0,使之定时操作蜂鸣器,得出相应的音调;接着分离出该数的低4位,得到延时时间,接着调用软件延时。

简谱对应的音调简码、节拍简码

发音 停顿 中音DO 中音RE 中音MI 中音FA 中音SO 中音LA 中音SI 高音DO 高音RE 高音MI 高音FA 高音SO 高音LA 高音SI 结束

音调 1 2 3 4 5 6 7 1 2 3 4 5 6 7

音调简码

0 1 2 3 4 5 6 7 8 9 A B C D E F

5/4拍 6/4拍 8/4拍 10/4拍 12/4拍 15/4拍

节拍数

1/4拍 1/2拍 3/4拍 1拍 1又1/4拍 1又1/2拍 2拍 2又1/2拍 3拍 3又3/4拍 4拍

音符长度

十六分音符 八分音符

四分音符 全分音符

二分音符

全分音符

节拍简码

0 1 2 3 4 5 6 8 A C E F

例子:

#include

#define uchar unsigned char #define uint unsigned int

sbit buzzer=P3^7;//蜂鸣器连续的IO口

uchar timeH,timeL,i;

//---------------------------简谱--------------------------------------- //编程规则:

//字节高四位是音调(1-7代表中央C调,8-E代表高八度,0代表停顿), //低四位是节拍

//最后的FF是结束标志

uchar code xiaoPingGuo[] = { //小苹果简谱

0x62, 0x62, 0x62, 0x31, 0x81, 0x82, 0x72, 0x62, 0x32, 0x01, 0xa2, 0x92, 0x82, 0x62, 0x52, 0x64, 0x01,

0xc2, 0xa2, 0x92, 0x81, 0x91, 0x92, 0xa2, 0x92, 0x52, 0x62, 0xa2, 0x92, 0x82, 0x82, 0x52, 0x62,

0x62, 0x61, 0x71, 0x82, 0x92, 0xc2, 0xb2, 0xa2, 0x82, 0x82, 0x32, 0x52, 0x62, 0x02, 0x52, 0x64,

0xa2, 0x92, 0x82, 0x61, 0x91, 0x92, 0x82, 0x62, 0x32, 0x52, 0xa2, 0x92, 0x82, 0x82, 0xc2, 0x64, 0x08,//前奏

0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x64, //我种下一颗种子

0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x54, //终于长出了果实

0x52, 0x52, 0x52, 0x52, 0x52, 0x62, 0x52, 0x62, //今天是个伟大日子

0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x64, //摘下星星送给你

0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x54, //拽下月亮送给你

0x52, 0x52, 0x52, 0x52, 0x52, 0x62, 0x52, 0x62, //让太阳每天为你升起

0x62, 0x62, 0x62, 0x72, 0x82, 0xa2, 0x92, 0x82, //变成蜡烛燃烧自己

0x72, 0x62, 0x72, 0x62, 0x78, //只为照亮你

0x52, 0x52, 0x52, 0x62, 0x72, 0x92, 0x82, 0x72, //把我一切都献给你

0x62, 0x52, 0x62, 0x52, 0x68, //只要你欢喜

0x62, 0x62, 0x62, 0x72, 0x82, 0xa2, 0x92, 0x82, //你让我每个明天都

0x72, 0x62, 0x72, 0x62, 0x78, //变得有意义

0x52, 0x52, 0x52, 0x62, 0x72, 0x92, 0x82, 0x72, //生命虽短爱你永远 0x14, 0x14, 0x14, 0x14, //不离不弃

0xa4, 0x84, 0x94, 0x64, //你是我的

0xa2, 0x92, 0x82, 0x92, 0x68, //小呀小苹果儿

0xa4, 0x84, 0x94, 0x94, //怎么爱你

0xc2, 0xa2, 0x74, 0x84, 0x82, 0x72,

0x6f, 0x6f, //都不嫌多,红红

0x64, 0x72, 0x82, 0x94, 0x54, //的小脸儿温暖

0xd2, 0xc2, 0xa4, 0xa4, 0xa2, 0x92, //我的心窝

0x84, 0x92, 0xa2, 0x92, 0xa2, 0x92, 0xc2, 0xcf, //点亮我生命的火 火火火火 0xa4, 0x84, 0x94, 0x64, //你是我的

0xa2, 0x92, 0x82, 0x92, 0x68, //小呀小苹果儿

0xa4, 0x84, 0x94, 0x92, 0x92, //就像天边

0xc2, 0xa2, 0x74, 0x84, 0x82, 0x72, //最美的云朵

0x64, 0x72, 0x82, 0x94, 0x54, //春天又来到了

0xd2, 0xc2, 0xa4, 0xa4, 0xa2, 0x92, //花开满山坡

0x84, 0x92, 0xa2, 0x94, 0x52, //种下希望

0x64, 0x62, 0x82, 0x68, //就会收获

0xa2, 0xd1, 0xd1, 0xc2, 0xd2, 0xc2, 0xa2, 0xd1, 0xd1, 0xc2, 0xd2, 0x82, 0xa2, 0xd1, 0xd1, 0xc2, 0xd2, 0xc2, 0xa2, 0xd1, 0xd1, 0xc2, 0xd2, 0x82, 0xc2, 0xa2, 0x74, 0x84, 0x82, 0x72,

0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x64, //从不觉得你讨厌

0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x54, //你的一切都喜欢

0x52, 0x52, 0x52, 0x52, 0x52, 0x62, 0x52, 0x62, //有你的每天都新鲜

0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x64, //有你阳光更灿烂

0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x54, //有你黑夜不黑暗

0x52, 0x52, 0x52, 0x52, 0x52, 0x62, 0x52, 0x62, //你是白云我是蓝天

0x62, 0x62, 0x62, 0x72, 0x82, 0xa2, 0x92, 0x82, //春天和你漫步在

0x72, 0x62, 0x72, 0x62, 0x78, //盛开的 花丛间

0xd2, 0x82, 0xe2, 0x82, 0xd2, 0xc2, 0xe2, 0x82, 0x6f, 0x6f, 0xd2, 0xd2, 0xd2, 0xd2,

0x52, 0x52, 0x52, 0x62, 0x72, 0x92, 0x82, 0x72, //夏天夜晚陪你一起

0x62, 0x52, 0x62, 0x52, 0x68, //看 星星眨眼

0x62, 0x62, 0x62, 0x72, 0x82, 0xa2, 0x92, 0x82, //秋天黄昏与你徜徉

0x72, 0x62, 0x72, 0x62, 0x78, //在 金色麦田

0x52, 0x52, 0x52, 0x62, 0x72, 0x92, 0x82, 0x72, //冬天雪花飞舞有你 0x14, 0x14, 0x14, 0x14, //更加温暖

0xa2, 0xd1, 0xd1, 0xc2, 0xd2, 0xc2, 0xd2, 0x82, 0xa2, 0xd1, 0xd1, 0xc2, 0xd2, 0x82, 0xe2, 0x82, 0xa2, 0xd1, 0xd1, 0xc2, 0xd2, 0xc2, 0xd2, 0xc2, 0xa2, 0xd1, 0xd1, 0xc2, 0xd2, 0x82, 0xe2, 0x82, 0xc2, 0xa2, 0x74, 0x84, 0x82, 0x72,

0xa4, 0x84, 0x94, 0x64, //你是我的

0xa2, 0x92, 0x82, 0x92, 0x68, //小呀小苹果儿

0xa4, 0x84, 0x94, 0x94, //怎么爱你

0xc2, 0xa2, 0x74, 0x84, 0x82, 0x72, //都不嫌多,红红

0x64, 0x72, 0x82, 0x94, 0x54, //的小脸儿温暖

0xd2, 0xc2, 0xa4, 0xa4, 0xa2, 0x92, //我的心窝

0x84, 0x92, 0xa2, 0x92, 0xa2, 0x92, 0xc2, 0xcf, //点亮我生命的火 火火火火 0xa4, 0x84, 0x94, 0x64, //你是我的

0xa2, 0x92, 0x82, 0x92, 0x68, //小呀小苹果儿

0xa4, 0x84, 0x94, 0x92, 0x92, //就像天边

0xc2, 0xa2, 0x74, 0x84, 0x82, 0x72, //最美的云朵

0x64, 0x72, 0x82, 0x94, 0x54, //春天又来到了

0xd2, 0xc2, 0xa4, 0xa4, 0xa2, 0x92, //花开满山坡

0x84, 0x92, 0xa2, 0x94, 0x52,

0xd2, 0xd2, 0xd2, 0xd2,

//种下希望

0x64, 0x62, 0x82, 0x68, //就会收获

0x64, 0x72, 0x82, 0x94, 0x54, 0xd2, 0xc2, 0xa4, 0xa4, 0xa2, 0x92, 0x84, 0x92, 0xa2, 0x94, 0x52, 0x64, 0x62, 0x82, 0x68, 0xff };

//----------------------------简谱音调对应的定时器初值--------------------------- //适合11.0592M的晶振 uchar code pinLvChuZi[]={ 0xff,0xff,//占位 0xFC,0x8E,//中央C调1-7 0xFC,0xED, 0xFD,0x43, 0xFD,0x6A, 0xFD,0xB3, 0xFD,0xF3, 0xFE,0x2D, 0xFE,0x47, //高八度1-7 0xFE,0x76, 0xFE,0xA1, 0xFE,0xC7, 0xFE,0xD9, 0xFE,0xF9, 0xFF,0x16 };

void delayms(uint z); //延时1MS void delay(uint z); //延时,十六分音符 void song(void);

void timer0Init(void);

void main(void) { timer0Init(); while(1) { song();

delayms(1000); } }

void timer0Init(void) { TMOD=0x01;//定时器0工作在方式1 TH0=0; TL0=0; ET0=1; EA=1;//开总中断 }

void song(void) { uint temp; uchar yinDiao;//yinDiao是简谱 i=0; while(1) { temp=xiaoPingGuo[i]; if(temp==0xff) { break;} yinDiao=temp/16; //取数的高4位 if(yinDiao!=0) { timeH=pinLvChuZi[yinDiao*2]; timeL=pinLvChuZi[yinDiao*2+1]; } else { TR0=0; buzzer=1;//关蜂鸣器 } delay(temp); //取数的低4位 TR0=0; //唱完一个音停10MS buzzer=1; delayms(10); TR0=1; i++; } TR0=0; buzzer=1;

//取频率初值

}

void delay(uint z) //延时,十六分音符 { uint x,y; for(x=z;x>0;x--) for(y=11000;y>0;y--); }

void delayms(uint z) //延时1MS { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); }

void timer0() interrupt 1 //产生各种音调 { TH0=timeH; TL0=timeL; buzzer=~buzzer; }

再附上一个可以变音调的演奏音乐的程序 实验程序

/* =========================================================== */

/* ----------------------------------------------------------- */

/* 曲谱存贮格式 uchar code 数组名{音高,音长,音高,音长....} */

/* 音高由三位数字组成: */

/* 个位是表示 1~7 这七个音符. */

/* 十位是表示音符所在的音区,1-低音,2-中音,3-高音; */

/* 百位表示这个音符是否要升半音 0(不写)-不升,1-升半音。 */

/* 音长最多由三位数字组成: */

/* 个位表示音符的时值,其对应关系是: */

/* 数值(n) 0 1 2 3 4 5 6 */

/* --------------------------------------------- */

/* 几分音符 1 2 4 8 16 32 64 */

/* 即:音符=2^n ,这样做的目的是为了节省曲谱的存贮空间。 */

/* 十位表示音符的演奏效果(0-2),0-普通,1-连音,2-顿音。 */

/* 百位是符点位,0(不写)-无符点,1-有符点。 */

/* ----------------------------------------------------------- */

/* 调用演奏子程序的方法为: */

/* play(乐曲数组名,调号,升降八度,演奏速度,开始指针,结束指针) */

/* 调号(0-11)是指乐曲升多少个半音演奏;升降八度(1-3)是指在演奏 */

/* 在哪个八度演奏: 1-降八度,2-不升不降,3-升八度.开始指针(0- ) */

/* 是从哪个音符开始演奏,结束指针是演奏到哪个音符为止. */

/* ----------------------------------------------------------- */

//本程序用T0 来产生音调,用T1 产生音长

#include

#define uchar unsigned char

#define yx 4/5 /* 定义普通音符演奏的长度分率 */

#define plen 2 /* 定义晶振的时钟周期(us) */

#define uchar unsigned char

#define uint unsigned int

sbit speaker=P3^5;

/* ------------------下面是曲谱 ------------------------------ */

uchar code sound[100]=

{25,2,23,3,25,3,31,1,26,2,31,3,26,3,25,1,25,2,21,3,22,3,23,2,22,3,21,3,22,0,

25,2,23,3,25,3,31,102,27,3,26,2,31,2,25,1,25,2,22,3,23,3,24,102,17,3, 21,0};

uchar tc0,tc1,sc0,sc1; /* 音长和音符两个计数器初值暂存 */

void play(sound,dh,sj,speed,point1,point2)

uchar code sound[]; /* 接受乐曲数组的地址 */

uchar speed,sj,dh; /* 速度、八度、调号 */

uint point1,point2; /* 乐曲开始、结束指针 */ {

uint code fftab[12]={262,277,294,311,330,349,369,392,415,440,466,494}; /* 频率表*/

uchar code stab[7]={0,2,4,5,7,9,11}; /* 1~7 在频率表中的位置 */

uchar code ltab[7]={1,2,4,8,16,32,64};

uchar tl,ts,sl,sm,sh,slen,xg,ii,fd;

uint point,hz,tc,sc,len,len0,len1,len2,len4,i,ftab[12];

speaker=1;

for(i=0;i<12;i++) /* 根据调号及升降八度来计算音符频率 */ {

ii=i+dh;

if(ii>11) {

ii=ii-12;

ftab[i]=fftab[ii]*2; } else

ftab[i]=fftab[ii];

if(sj==1) ftab[i]>>=2;

if(sj==3) ftab[i]<<=2; }

point=point1;

ts=sound[point];

tl=sound[point+1]; /* 读出第一个音符和它时时值 */

tc=65535-10000/plen; /* 算出10ms 的初装值 */

tc0=tc%6; /* 计算TL1 应装入的初值 */

tc1=tc/256; /* 计算TH1 应装入的初值 */

len0=12000/speed; /* 算出1 分音符的长度(几个10ms) */

len4=len0/4; /* 算出4 分音符的长度 */

len4=len4-len4*yx; /* 普通音最长间隔标准 */

TMOD=0x11;

TH1=tc1; TL1=tc0;

ET0=1; EA=1;

TR0=0; TR1=1;

while(point<=point2) {

sl=ts; /* 计算出音符 */

sh=ts/100; /* 计算出是否升半 */

sm=ts/10; /* 计算出高低音 */

hz=ftab[stab[sl-1]+sh]; /* 查出对应音符的频率 */

if(sl!=0) {

if (sm==1) hz>>=2; /* 若是低音 */

if (sm==3) hz<<=2; /* 若是高音 */

sc=(50000/hz)*10/plen; /* 计算脉冲个数 */

sc=65536-sc; /* 计算计数器初值 */

sc0=sc%6; /* 算出TL0 应装初值 */

sc1=sc/256; /* 算出TH0 应装初值 */

TH0=sc1; /* 装入初值 */

TL0=sc0+12; /* 加12 是对中断延时的补偿 */ }

slen=ltab[tl]; /* 算出是几分音符 */

xg=tl/10; /* 算出音符类型(0 普通1 连音2 顿音) */

fd=tl/100;

len=len0/slen; /* 算出连音音符演奏的长度(多少个10ms)*/

if (fd==1) len=len+len/2;

if(xg!=1)

if(xg==0) /* 算出普通音符的演奏长度 */

if (slen<=4)

len1=len-len4; else

len1=len*yx; else

len1=len/2; /* 算出顿音的演奏长度 */ else

len1=len;

if(sl==0) len1=0;

len2=len-len1; /* 算出不发音的长度 */

if (sl!=0) {

TR0=1;

for(i=len1;i>0;i--) /* 发规定长度的音 */ {

while(TF1==0);

TH1=tc1; TL1=tc0;

TF1=0; } }

if(len2!=0) {

TR0=0; speaker=1;

for(i=len2;i>0;i--) /* 音符间的间隔 */ {

while(TF1==0);

TH1=tc1; TL1=tc0;

TF1=0; } }

point+=2; /* 音符指针下移 */

ts=sound[point]; tl=sound[point+1]; /* 读出下一个音符和它时时值 */ } }

void yin() interrupt 1 /* 音符发生程序(中断服务程序)*/ {

speaker=~speaker;

TH0=sc1; TL0=sc0; }

//==============================================

main() {

while(1) {

play(sound,0,2,60,0,57);

play(sound,0,1,60,0,57);

play(sound,0,3,60,0,57);

play(sound,0,2,40,0,57);

play(sound,5,2,60,0,57);

play(sound,0,2,80,0,57); } }

本文来源:https://www.bwwdw.com/article/c3d7.html

Top