第三讲让你的程序声图并茂.docx
《第三讲让你的程序声图并茂.docx》由会员分享,可在线阅读,更多相关《第三讲让你的程序声图并茂.docx(24页珍藏版)》请在冰豆网上搜索。
第三讲让你的程序声图并茂
C语言高级程序设计讲座
第三讲让你的程序声图并茂
鹏程C语言教学网站庆祝建站一周年特别企划
技术支持:
http:
//202.112.86.129
经过上面两节课的学习,我们应该对C语言的图形设计有一定的了解,这节课我们主要讲一下C语言的发音,让你的程序声图并茂。
学习目标
·学习用C语言发声
·了解声音功能函数
·了解乐谱文件的制作
·能够结合图像与声音,让你的程序声图并茂
打破沉寂
首先我们来看一个源代码:
#defineFALSE0
#defineTRUE1
#include
main()
{
intsnd;/*whichsoundtoproduce*/
intcnt;/*numberoftimestorepeatsound*/
intnote;/*Currentnote,whensweepingfrequencies*/
while(TRUE){
/*MaleSureanyprevioussoundsaveturnedoff.*/
nosound();
/*Asktheuserforwhichtypeofsounf*/
printf("1-siren;2-overload;3-whoop;4-phaser;0-exit");
/*readtheanswer*/
scanf("%d",&snd);
/*iftheanswerittoexit,doso.*/
if(snd==0)
break;
/*Askhowmanytimestorepastthesound.*/
printf("Nungeroftimes:
");
/*gettheanswer*/
scanf("%d",&cnt);
/*repeatthesoundthenumberoftimespecifed*/
while(cnt--){
/*swichontypeofsoundtoproduce*/
switch(snd)
{case1:
/*doasiren:
sweepup*/
for(note=1000;note>150;note-=10)
{
sound(note);
delay(20);
}
/*Sweepdown*/
for(;note<1000;note+=10)
{
sound(note);
delay(20);
}
break;
case2:
/*doanoverload.sweepup*/
for(note=4000;note>10;note-=10)
{
sound(note);
delay(70);
}
break;
case3:
/*doawhoop:
Sweepup*/
for(note=1000;note>10;note-=10)
{
sound(note);
delay(200);
}
break;
case4:
/*doaphaser:
sweepdown*/
for(note=60;note<2000;note+=10)
{
sound(note);
delay(100);
}
break;
default:
/*unknown,askagain*/
printf("Invalidentry;tryagain\n");
break;
}
}
}
}
请大家打开TC运行该代码,程序重复的询问用户要产生哪一种声音。
C语言调用两个库函数使PC发出声音:
sound()打开声音;nosound()关闭声音。
他们包含在dos.h中,格式如下:
voidsound(unsignedintfrequency);
voidnosound(void);
其中frequency为频率,单位Hz。
为了使声音持续一段时间,我们还常用delay()函数,格式如下:
voiddelay(unsignedintmilliseconde);
其中milliseconde表示所需延时的时间。
下面这个例子将不断发出各种频率的声音:
#include
#include
#include
#include
main()
{
inti,j;
randomize();
while(!
bioskey
(1))
{
i=rand()*5000;
sound(i);
delay(10);
}
nosound();
}
前台音乐设计
首先我们来看下面这个例子,演奏的是《好人一生平安》
#include
#include
#defineN164
#defineN232
#defineN416
#defineN88
#defineN164
#defineEND0
enumNOTES
{
C10=131,D10=147,E10=165,F10=175,G10=196,A10=220,B10=247,
C0=262,D0=296,E0=330,F0=349,G0=392,A0=440,B0=494,
C1=525,D1=587,E1=659,F1=698,G1=784,A1=880,B1=988,
C2=1047,D2=1175,E2=1319,F2=1397,G2=1568,A2=1760,B2=1796
}song[]={
D0,N4,E0,N8,D0,N8,C0,N4,A10,N4,G10,N8,E10,N8,G10,N8,A10,
N8,C0,N2,A10,N4,A10,N8,C0,N8,G10,N8,A0,N8,E0,N8,G0,N8,
D0,N2,E0,N4,D0,N8,E0,N8,G0,N4,E0,N4,G10,N8,E10,N8,G10,
N8,A10,N8,C0,N2,A10,N4,A10,N8,C0,N8,A10,N8,A10,N8,D10,
N8,E10,N8,G10,N2,D0,N4,D0,N4,G0,N4,A0,N8,G0,N8,F0,N2,G0,
N2,A0,N4,G0,N8,E0,N8,D0,N8,E0,N8,C0,N8,A10,N8,D0,N2,E0,
N4,G0,N8,E0,N8,G0,N4,E0,N4,G10,N8,E10,N8,G10,N8,A10,N8,
C0,N4,A10,N4,A10,N8,C0,N8,D0,N8,A10,N8,C0,N8,E0,N8,D0,
N1,END,END};
main()
{
intnote=0,fre,dur,control;
clock_tgoal;
while(song[note]!
=0)
{
fre=song[note];
dur=song[note+1];
if(kbhit())break;
if(fre)
{
outportb(0x43,0xb6);
fre=(unsigned)(1193180L/fre);
outportb(0x42,(char)fre);
outportb(0x42,(char)(fre>>8));
control=inportb(0x61);
outportb(0x61,(control)|0x3);
}
goal=(clock_t)dur+clock();
while(goal>clock());
if(fre)
outportb(0x61,control);
goal=(clock_t)0;note=note+2;
}
}
音乐是时间的艺术,即把各种音符按不同的时值演奏出来,就可以构成曲调。
因此,音乐程序设计中的两个重要因素是:
如何用“曲调定义语言”来表示音符(即音高);如何控制音符的持续时间(即音长)。
解决了这两个问题之后,剩下的就是如何用c语言控制计算机的扬声器发声。
下面我们详细的讲解一下。
(1)音符及音长的定义
音调由音符构成,音调的高低由音符频率决定,频率越高,音调也越高。
音乐中使用的频率一般为131~1976HZ,它包括了中央C调及其前后的4个8度的音程。
用c语言中的枚举类型常量可定义上述表中的各音符的频率。
如果音乐中有比表中的音符更高的音调,则可根据表上的有关值推出,如高8度的C,D和E的频率分别为2091,2350和2638,还需作适当的调整。
EnumNOTES
{
C10=131,DlO=147,ElO=165,FlO=175,GlO=196,A10=220,B10=247.
CO=262,DO=294,EO=330,FO=349,GO=392,AO=440,BO=494,
C1=523,Dl=587,EI=659,Fl=698,G1=784,A1=880,B1=988,
C2=1047,D2=1175,E2=1319,F2=1397,G2=1568,A2=1760,B2=1976
音长即一个音符的持续题意。
在乐曲中,音长用全音符、半音符、4分音符……来表示,通常以4分音符一拍,等等。
音长可用下面定义:
#defineN132
#defineN216
#defineN48
#defineN84
#defineN162
#defineEND0
如果感觉计算机所演奏的乐曲速度过快,可以适当调整上面的值。
(2)用C语言定义音乐
NOTES类型中各音调对应的简谱如下:
上面的乐谱共有10个音符,其音高和音长如下:
(3)如何控制扬声器
如何控制扬声器发生,则可用TurboC中的库函数中outportb(intport,charbyte),函数的原型在”dos.h”文件中,port为端口地址,byte为传送给端口的字节。
扬声器的端也址为0x42,下面的程序中主要是发声的频率。
另外,还需要使发声延迟,就要用到”clock_t”,类型变量goal和clock()库函数,变量和程序的原型在”time.h”文件中,使扬声器发声的步骤为:
①初始化端口0x42;
②向端口0x42传送声频率fre;
延迟:
当goal>clock()时,做循环。
后台音乐设计
后台音乐设计主要是程序的背景声音,看下面的例子:
#include
#include
#include
#include
#include
#defineN164
#defineN232
#defineN416
#defineN88
#defineN164
#defineEND0
voidinterrupt(*handler)();
voidinterruptmusic();
inthandle,control;
enumNOTES
{
C10=131,D10=147,E10=165,F10=175,G10=196,A10=220,B10=247,
C0=262,D0=296,E0=330,F0=349,G0=392,A0=440,B0=494,
C1=523,D1=587,E1=659,F1=698,G1=784,A1=880,B1=988,
C2=1047,D2=1175,E2=1319,F2=1397,G2=1568,A2=1760,B2=1796
}song[]={
E1,N4,E1,N8,E1,N8,F1,N4,G1,N4,F1,N4,F1,N4,E1,N4,D1,
N4,C1,N4,C1,N4,D1,N4,E1,N4,E1,N4,D1,N4,D1,N4,E1,N4,
E1,N8,E1,N8,F1,N4,G1,N4,G1,N4,F1,N4,E1,N4,D1,N4,C1,
N4,C1,N4,D1,N4,E1,N4,D1,N4,D1,N4,C1,N4,D1,N4,D1,N8,
D1,N8,E1,N4,C1,N4,D1,N4,E1,N8,F1,N8,E1,N4,C1,N4,D1,
N4,E1,N8,F1,N8,E1,N4,C1,N4,C1,N4,D1,N4,G0,N4,E1,N4,
E1,N4,E1,N8,F1,N4,G1,N4,G1,N4,F1,N4,E1,N4,D1,N4,C1,
N4,C1,N4,D1,N4,E1,N4,E1,N4,D1,N4,C1,N4,D1,N4,
END,END
};
main()
{
intgdriver=VGA,gmode=1,i;
initgraph(&gdriver,&gmode,"");
handler=getvect(0x1c);
setvect(0x1c,music);
cleardevice();
setbkcolor(BLUE);
setcolor(YELLOW);
setfillstyle(SOLID_FILL,RED);
sector(200,150,50,120,70,50);
bar3d(400,150,500,200,10,5);
setactivepage
(1);
sector(200,150,50,170,70,50);
bar3d(400,200,500,250,10,5);
for(i=0;i<100;i++)
{
while(kbhit())gotoend;
setvisualpage
(1);
delay((100-i)*10);
setvisualpage(0);
delay((100-i)*10);
}
end:
;
outportb(0x61,control&0xfe);
setvect(0x1c,handler);
cleardevice();
closegraph();
}
voidinterruptmusic()
{
staticintflag=0,note=0,fre,dur=8;
flag++;
fre=song[note];
dur=song[note+1];
if(/*flag>(int)dur*2/5*/fre)
{
flag=0;
outportb(0x43,0xb6);
fre=(unsigned)(1193180L/fre);
outportb(0x42,(char)fre);
outportb(0x42,(char)(fre>>8));
control=inportb(0x61);
outportb(0x61,(control)|0x3);
note=note+2;
if(note>=134)note=0;
}
handler();
}
乐谱文件的制作
我们在这里使用的乐谱文件格式如下:
最高音的前面加”*”;高音,在每个音的前面加”h”;中音,在每个音的前面加”m”;低音,在每个音的前面加”L”。
“*”、“h”、“m”、“L”与其控制的音符构成音高,决定发声频率。
音高的后面是音长,可用整数或小数输入,以控制延时,但中间必须用空格分开。
乐谱文件的最前端是一个整数,表示音长基数,一般为300、600、900、1200。
乐谱文件的最末端是乐谱文件结束符”##”,以表示乐谱文件结束。
下面是电影《<城南旧事》插曲《送别》的一段。
乐谱文件为:
600m51m30.5m50.5hl2m61hl0.5m60.5m52m5Iml0.5m20.5m31m20.5ml0.5m23##
每个音的音长=音长基数*节拍数,其中,音长基数是乐谱文件的第一个字符,如上面乐谱文件为600,每个音的音频可用一摸拟频率值输入。
下面是程序源代码、编译、连接成可执行文件music.exe,用法为:
music乐谱文件。
#include
#include
#include
#include
voidmain(intargc,char*argv[])
{
FILE*fp;
intrate;
charsound_high[3];
floatsound_long;
registerinti=0,j;
intsign=0;
floatstr[100][2];
if(argc!
=2)/*命令行参数个数不正确*/{
printf("PararnetersErrors!
\n");
exit
(1);
}
if((fp=fopen(argv[1],"r"))==NULL)/* 文件打开失败*/
{
printf("openfilemusic,docErrors!
\n");
exit
(1);
}
fscanf(fp,"%d",&rate);/*读取音长基数的值 */
while(!
feof(fp)&&!
sign)/*文件没有结束并且数据还是乐谱*/
{
fscanf(fp,"%s%f",sound_high,&sound_long);/*得到音频、音长的数值*/
str[i][1]=rate*sound_long;/* 音乐=音长基数*节拍数*/
switch(sound_high[0]){
case'*':
/*最高音*/
switch(sound_high[1]){/*确定发音的频率*/
case'1':
str[i++][0]=1046.5;
break;
case'2':
str[i++][0]=1174.7;
break;
case'3':
str[i++][0]=1318.5;
break;
case'4':
str[i++][0]=1396.9;
brcak:
case'5':
str[i++][0]=1568;
break;
case'6':
str[i++][0]=1760;
break;
case'7':
str[i++][0]=1975.5;
break;
default:
printf("\nErrorsinmusic.doc\n");
exit
(1);
break;
}
break;
case'h':
/*高音*/
switch(sound_high[1]){
case'1':
str[i++][0]=523.3;
break;
case'2':
str[i++][0]=587.3;
break;
case'3':
str[i++][0]=659.3;
break;
case'4':
str[i++][0]=698.5;
break;
case'5':
str[i++][0]=784.0;
break;
case'6':
str[i++][0]=880;
break;
case'7':
str[i++][0]=987.8;
break;
default:
printf("\nErrorsinmusic.doc\n");
exit
(1);
break;
}
break;
case'm':
/*中音*/
switch(sound_high[1]){
case'1':
str[i++][0]=262;
break;
case'2':
str[i++][0]=296;
break;
case'3':
str[i++][0]=329.6;
break;
case'4':
str[i++][0]=349.2;
break;
case'5':
str[i++][0]=392;
break;
case'6':
str[i++][0]=440;
break;
case'7':
str[i++][0]=493.9;
break;
default:
printf("\nErrorsinmusic.doc\n");
exit
(1);
break;
}
break;
case'l':
/*低音*/
switch(sound_high[1]){
case'1':
str[i++][0]=131;
break;
case'2':
str[i++][0]=147;
break;
case'3':
str[i++][0]=165;
break;
case'4':
str[i++][0]=176;
break;
case'5':
str[i++][0]=196;
break;
case'6':
str[i++][0]=220;
break;
case'7':
str[i++][0]=247;
break;
default:
printf("\nErrorsinmusic.doc\n");
exit
(1);
break;
}
break;
case'#':
if(sound_high[1]=='#')sign=1;
break;
default:
printf("\nErrorsinmusic.doc\n");
exit
(1);
}
}
for(j=0;j
{
sound(str[j][0]);/*按频率值发出歌曲的音调*/
delay(str[j][1]);
}
nosound();/*关闭扬声器*/
}
声图并茂
我们来看下面这个程序的源代码,实现的是一个表盘的走动,并且发出响声,整点还有报时功能。
#include
#include
#include
#defineCENTERX320/*表盘中心位置*/
#defineCENTERY175
#defineCLICK100/*喀嗒声频率*/
#defineCLICKDELAY30/*喀嗒声延时*/
#defineHEBEEP10000/*高声频率*/
#defineLOWBEEP500/*低声频率*/
#defineBEEPDELAY200/*报时声延时*/
/*表盘刻度形状*/
intMrk_1[8]={-5,-160,5,-160,5,-130,-5,-130,};
intMrk_2[8]={-5,-160,5,-160,2,-130,-2-130,};
/*时针形状*/
intHourHand[