• 沒有找到結果。

void rexy() /*得到光标坐标*/

{ union REGS rg;

rg.h.ah=3;

rg.h.bh=0;

int86(0x10,&rg,&rg);

y0=rg.h.dl;

x0=rg.h.dh;

}

void prt(p) /*显示字符*/

{ union REGS rg;

rg.h.al=p;

rg.h.ah=14;

int86(0x10,&rg,&rg);

}

4.5 发声技术

播放歌曲就是意味着让计算机发声,由于声音是从 PC 内的扬声器发出的,因此要首先了 解一下计算机发声原理。在 PC 的系统板上装有定时与计数器 8253 芯片,还有 8255 可编程并 行接口芯片,由它们组成的硬件电路可用来产生 PC 内扬声器的声音,对于 286、386、486、

586 等 PC,由于采用了超大规模集成电路,因而看不到这些芯片,它们均集成在外围电路芯 片上了。当操作计算机时,常常听到的发声,就是由软件控制这些电路而产生的。声音的长短 和音调的高低,均可由程序进行控制。在扬声器电路中,定时器的频率决定了扬声器发音的频 率,所以可通过设定定时器电路的频率来使扬声器发出不同的声音。对定时器电路进行频率设 定时,首先对其命令寄存器(口地址为 0x43)写命令字,如写入 0xb6,这可用 outporb(0x43,0xb6);

来实现,则表示选择该定时器的第二个通道,计数频率先送低 8 位(二进制),后送高 8 位。

接着用口地址 0x42 送频率计数值,先送低 8 位,后送高 8 位,即用 outportb(0x42,低 8 位 频率计数值)和 outportb(0x42,高 8 位频率计数值)来实现。通过这两步使定时器电路产 生一系列方波信号,此信号是否能推动扬声器发声,还要看由 8255 产生的门控信号和送数信 号是否为 1,而它们也可编程,口地址为 0x61。为了不影响 8255 口地址 61H 中的其他高位,

应 先 输 入 口 地 址 61H 的 现 有 值 bits , 即 用 bits= inportb(0x61) 来 实 现 , 然 后 就 可 用 outportb(0x61,bits|3)来允许发声,而用 outportb(0x61,bits&0xfc)来禁止发声,且不改变 8255 其他位原来的值。

4.5.1 声音函数

编写音乐程序播放歌曲,最简单的方法是可以直接使用 Turbo C 2.0 在 dos.h 中提供的有 关发声的函数 sound()和 nosound()。

void sound(unsigned frequency);

该函数用于产生声音,其入口参数为扬声器要产生声音的频率。

void nosound(void);

该函数关闭扬声器,该函数没有入口和出口参数,它只是简单地把口地址 61H 中的低 2 位清 0。

在利用函数 sound 产生指定频率的声音后,一般要过一段时间后再调用函数 nosound 关 闭扬声器,这样才能清楚地听到一个声音。如果扬声器刚打开就关闭,是很难听到一个声音的。

某个频率的声音延续时间的长短是很重要的,它将直接影响音响效果。这需要使用 Turboc 提 供的专门的延时函数 delay,其原型说明如下:

void delay (unsigned milliseconds);

该函数中断程序的执行,中断的时间由 milliseconds 指定。

例 4-22 该程序每间隔 10000 milliseconds PC 扬声器发出不同频率的声音,直到频率为 于 5000Hz。

#include<dos.h>

main() { int freq;

for(freq=50;freq<5000;freq+=50) { sound (freq);

delay(10000);

}

nosound();

}

4.5.2 乐谱的计算机表示方法

音乐程序设计中有两个最重要的因素:音符和音长。进行音乐程序设计时必须要解决如 何用“曲调定义语言”来表示音符和如何控制音符的持续时间问题。

1.音符

音调由音符构成,音调的高低由音频决定,频率越高,音调也越高。音乐中使用的频率 一般为 131~1976Hz,它包括中央 C 调及前后的 4 个 8 度音程。这 4 个 8 度中各音符的频率 如表 4-15 所示。

表 4-15 频率与音阶的对照表

音调 低音 频率 中音 频率 高音 频率 最高音 频率

C 1 131 1 262 1 523 1 1047

D 2 147 2 296 2 587 2 1175

E 3 165 3 330 3 659 3 1319

F 4 176 4 349 4 699 4 1397

G 5 196 5 392 5 784 5 1568

A 6 220 6 440 6 880 6 1760

B 7 247 7 494 7 988 7 1976

为 了 在 程 序中 输 入 方便 , 常用 英 文 字母 表 示 音符的 频 率 。 用 asdfghj 表 示 中 音 段

CDEFGAB;zxcvbnm 代表低音段 CDEFGAB;qwertyu 代表高音段 CDEFGAB。对于很不常用 的最高音的 7 个音符,可以再用 7 个其他符号代替。

表示音符频率的另一个方案是用枚举类型常量来定义表 4-15 中各音符的频率。

例如,定义 enum music {

C0=131, D0=147, E0=165, F0=175, G0=196, A0=220, B0=247, C1=262, D1=294, E1=330, F1=349, G1=392, A1=440, B1=494, C2=523, D2=587, E2=659, F2=698, G2=784, A2=880, B2=988, C3=1047,D3=1175,E3=1319,F3=1397,G3=1568,A3=1760,B3=1976, }

2.音长

音长表示音符的持续时间。演奏时,每一个音符必须有一个频率用 sound 去发声,且必须 有适当的时间延时,形成拍子。在乐曲中,音长分全音符、半音符、4 分音符、8 分音符等。

通常以 4 分音符为一拍,这样可以用 1 拍的时间来推出其他节拍。

为了在程序中表示音长,一般可以有 2 个方案来解决这个问题。一是在曲谱中找出持续 最短的音符,把它作为一个字母写出,然后时长是其一倍的就用两个同样的字母表示,以此类 推,两倍的用 3 个同样的字母表示,4 倍的就用 4 个同样的字母表示。

例如,对于 2 . 3 1 1 6.|56.. 5.两小节乐谱,可以表示成:

s s s d a a n bb n n bbbb。

另一个方案是采用宏定义来解决:

# define m1 32

# define m2 m1/2

# define m4 m1/4

# define m8 m1/8

# define m16 m1/16

上述两小节乐谱,可以表示成如表 4-16 所示。

表 4-16 乐谱示例

音符 音高 音长

2 D1 m8+m16

3 E1 m16

1 C1 m8

1 C1 m16

6 A0 m16

5 G0 m8

6 A0 m8

5 G0 m4

知道了这些知识,就容易编制一个乐谱程序。

4.5.3 应用

例 4-23 《雪绒花》歌曲程序。

源代码如下:

# include<conio.h>

# include<dos.h>

# define speed 2

void sound1(int freq,int time);

void pause(int time);

main() { int i,freq;

int time=4*speed;

char *qm="iddgwwwqqgfffddddfghhhggg ddgwwwqqgfff\

ddgghjqqqqqwpggjhgddgqqq hhqwwqjjjggg\

ddgwwwqqgfffddgghjqqqqqq";

gotoxy(40,20);

cprintf("Snow Flower");

while(*qm++!='\0') { i=1;

switch(*qm)

{ case'k': time=1*speed; i=0; break;

case 'l': time=2*speed; i=0; break;

case 'i': time=4*speed; i=0; break;

case 'o': time=6*speed; i=0; break;

case 'p': pause(time); i=0; break;

case 'a': freq=523; break;

case 's': freq=587; break;

case 'd': freq=659; break;

case 'f': freq=698; break;

case 'g': freq=784; break;

case 'h': freq=880; break;

case 'j': freq=988; break;

case 'z': freq=262;break;

case 'x': freq=294;break;

case 'c': freq=330;break;

case 'v': freq=349;break;

case 'b': freq=392;break;

case 'n': freq=440;break;

case 'm': freq=494;break;

case 'q': freq=1047;break;

case 'w': freq=1175;break;

case 'e': freq=1319; break;

case 'r': freq=1397; break;

case 't': freq=2568; break;

case 'y': freq=1760; break;

case 'u': freq=1976; break;

default: i=0; break;

} if(i)

sound1(freq,time);

} }

void sound1(int freq,int time) { int n;

sound(freq);

n=time+clock();

while(n>clock());

nosound();

}

void pause(int time) { int n;

n=time+clock();

while(n>clock()) nosound();

}

注释:clock()测得从程序开始到调用处处理机所用的时间,其头文件为 time.h。

函数 sound()可以用指定的频率控制机内扬声器发出指定的声响,直到用函数 nosound()去 关闭它。另外,分别用 k、l、i 表示 1/2、1/4、3/2 节拍。P 表示休止符。演奏速度由 SPEED 控制。具体识音过程采用 switch case 语句。本例中 i=0 表示不发音;i=1 表示发音。

例 4-24 2 . 3 1 1 6.|56.. 5.两小节计算机乐谱。

# include <stdio.h>

#include<dos.h>

# define m1 4000000000

# define m2 4000000000/2

# define m4 4000000000/4

# define m8 4000000000/8

# define m16 4000000000/16 enum music

{ C0=131, D0=147, E0=165, F0=175, G0=196, A0=220, B0=247, C1=262, D1=294, E1=330, F1=349, G1=392, A1=440, B1=494, C2=523, D2=587, E2=659, F2=698, G2=784, A2=880, B2=988, C3=1047,D3=1175,E3=1319,F3=1397,G3=1568,A3=1760,B3=1976, }s1[ ]={D1,E1,C1,C1,A0,G0,A0,G0};

main() { int i ;

int s2[ ]={m8+m16,m16,m8,m16,m16,m8,m8,m4};

for(i=0;i<8;i++) { sound(s1[i]);

delay(s2[i]);

}

nosound();

}

相關文件