BGM 取自【寒蝉鸣泣之时】插曲 You,简谱由网络提供,感谢作者……先把简谱附上:
还有一张自制的“钢琴按键频率分布”,没写那么全,只对应本程序,图如下:
附上源码:(初出茅庐,难免有写的不好的地方,仅作备份之用,欢迎指点,喷子退散……)
/*******************************************************************
喇叭播放音乐
BGM 取自【寒蝉鸣泣之时】的插曲 You
缺点:
1、没有添加调节音量的功能
2、音高、节拍 都要自己提前写好,工作量大
可以改进成:通过解析 midi 文件进行音乐播放
(midi 里边就是音高、节拍信息)
********************************************************************/
#include
sbit SPK = P1^0; // 喇叭输入
/************************************************************************************************************************************
PIANO_F[ ]:是钢琴按键的频率,数组下标对应钢琴按键,如上图所示
例:图上可以看出:8 号键对应钢琴的中央 C,正好是 PIANO_F[8],对应频率为 261.6 Hz
注:PIANO_F[0] 不代表任何按键,为了方便计数,数组下标从 1 开始有意义
*************************************************************************************************************************************/
float code PIANO_F[ ] = {7.63,
130.8, 146.8, 164.8, 174.5, 196.0, 220.0, 247.1,
261.6, 293.6, 329.6, 349.0, 392.0, 440.0, 494.0,
523.2, 587.2, 659.2, 698.3, 784.0, 880.0, 988.0,
1046.4, 1174.4, 1318.4, 1396.8, 1567.7, 1760.0, 1975.3};
/******************************************************************************************************
PITCH[ ]:指的是 音高,对应上图中的按键号
例:中音 do:如图对应 8 号键
高音 do:如图对应 15 号键
好处:假如歌曲播放到了第 i 个音,那么 PITCH[i] 就是对应钢琴上的按键
那么 PIANO_F[ PITCH[i] ] 就是对应的琴键的频率,方便编程使用
注:255 代表 音乐结束
0 代表 八分休止符
******************************************************************************************************/
unsigned char code PITCH[ ] = {16, 17, 16, 15, 16, 12, 17, 16, 15,
16, 17, 16, 15, 15, 19, 16,
16, 17, 16, 15, 16, 12, 17, 16, 15,
16, 17, 16, 15, 15, 12,
12, 10, 12, 15, 15,
16, 16, 16, 17, 17, 12,
12, 10, 12, 0, 15, 15, 16,
16, 16, 16, 19, 17, 12,
12, 10, 0, 12, 0, 15, 15, 15,
16, 16, 16, 17, 17, 12,
12, 10, 0, 12, 0, 15, 15, 16,
16, 16, 16, 19, 17, 26,
26, 24, 0, 26, 26, 22, 0, 23,
23, 22, 23, 0, 24, 23, 24, 26,
26, 24, 0, 26, 26, 22, 0, 23,
23, 22, 0, 23, 22,
255};
/***************************************************************************************************************
BEAT[ ]:指的是 节拍,数值代表基本节拍的倍数,这里基本节拍为 十六分音符 长
例:8,代表 8 倍 十六分音符 长,即:二分音符
4,代表 4 倍 十六分音符 长,即:四分音符
2,代表 2 倍 十六分音符 长,即:八分音符
1,代表 1 倍 十六分音符 长,即:十六分音符
6,代表 6 倍 十六分音符 长,即:符点四分音符
***************************************************************************************************************/
unsigned char code BEAT[ ] = {4, 1, 1, 2, 2, 2, 1, 1, 2, 4, 1, 1, 2, 2, 2, 4,
4, 1, 1, 2, 2, 2, 1, 1, 2, 4, 1, 1, 2, 6, 2,
2, 4, 2, 6, 2, 2, 2, 2, 2, 6, 2,
2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8,
255};
unsigned char time_h = 0; // 用于向定时器中断程序传递下一步定时信息
unsigned char time_l = 0;
unsigned int time = 0; // 根据按键频率,换算出应该定时多长时间
unsigned char i;
void Init_Timer0(void); // 初始化 定时器 0
void Delay(unsigned int t); // 普通延时
void Delay_ms(unsigned int t); // 相当近似的延迟 t ms
void main (void)
{
Init_Timer0();
for (i = 0; PITCH[i] != 255; i++) { // 顺序播放,255 为音乐结束
TR0 = 0; // 关 定时器0
Delay_ms(20); // 延时一段时间,保证按键与按键之间能分得清楚
/*********************************************************************************************************************
定时时间 time 的计算:
以 中央 C 为例:频率为 261.6 Hz,就是每秒 261.6 下,倒数就是 每下 1/261.6 s,
换成 us 就是 1,000,000/261.6,每响一次是一个上升沿加一个下降沿,
因此,变换的次数应该是 2 倍,每次变换所花时间是一半,
即:1,000,000/(2*261.6)
**********************************************************************************************************************/
time = (unsigned int)(1000000 / (2 * PIANO_F[PITCH[i]]));
time_h = (65536 - time) / 256;
time_l = (65536 - time) % 256;
TH0 = time_h; // 定时器赋值
TL0 = time_l;
TR0 = 1; // 开 定时器0
/****************************************************************************************************************
如果音高是 0,说明是 八分休止符,关定时器,定时器不驱动喇叭,也就没有声音了
****************************************************************************************************************/
if (PITCH[i] == 0) TR0 = 0;
/********************************************************************************************************
相当重要的延时!节拍全部由它控制,不是很精确,其实用 定时器 1 可能更好
********************************************************************************************************/
Delay_ms(BEAT[i] * 30);
}
TR0 = 0; // 音乐播放完后,关定时器,收尾
/*****************************************************************************************************************
相当重要!没有此语句将导致循环执行 main 函数!
详细讨论参见:http://gaebolg.blog.163.com/blog/static/19826906820122254823928/
*****************************************************************************************************************/
while (1);
}
void Init_Timer0(void)
{
TMOD = 0x01; // 定时器 0,模式1
EA = 1; // 开 总中断
ET0 = 1; // 开 定时器 0 中断
}
void Timer0_ISR(void) interrupt 1
{
TH0 = time_h; // 重装初值
TL0 = time_l;
SPK = !SPK; // 产生方波,驱动喇叭发声
}
void Delay(unsigned int t)
{
while (t--);
}
void Delay_ms(unsigned int t) // 根据测试,可以相当近似的表示 t ms
{
while (t--) {
Delay(245);
Delay(245);
}
}
上一篇:【C51】源码 5 -- LCD 1602 显示字符
下一篇:【C51】源码 3 -- 电子时钟(独立按键、8 位数码管、定时器)
推荐阅读最新更新时间:2024-03-16 15:12
- 热门资源推荐
- 热门放大器推荐