![]() ![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
C语言程序设计(第9章实用编程技巧)3 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
作者:佚名 文章来源:不详 点击数 更新时间:2008/4/18 13:59:59 文章录入:杜斌 责任编辑:杜斌 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
9.3.1 音乐程序设计 我们知道,音乐是音高和音长的有序组合,设计微机音乐最重要的就是如何定义音高和音长,以及如何让扬声器发出指定的音符。下面给出音符与频率的关系表。C语言提供的三个函数sound( )、nosound( )和clock( )可以很方便地解决上述的问题。sound( )函数可以用指定频率打开PC机扬声器直到用nosound( )函数来关闭它; clock( )函数正好用来控制发声时间,而且它不受PC机主频高低的影响。下面这段程序可使微机发出c调1的声音。 表9-2 音符与频率关系表
[例9-12] 音乐程序music1.c #include<stdio.h> #include<dos.h> void pause(int); void sound1(int,int); void main(void) { int i,freq,speed=5; int time=4*speed; char *qm="iddgwwwqqgfff dddfghhhggg ddgwwwqqgfff\ ddffhjqqqqq wpggjhgddgqq hhqwwqjjjggg\ ddgwwwqqqgfff ddffhjqqqqqq";/*定义歌曲*/ while (*qm++ !='\0'){ i=1; switch(*qm){ case 'k': time=1*speed; i=0; break; case 'i': time=6*speed; i=0; break; case 'o': time=10*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) /*freq为频率,time为持续时间*/ { union{ long divisor; unsigned char c[2]; }count; unsigned char ch; count.divisor=1193280/freq; /* 1193280 是系统时钟速率*/ outp(67,182); outp(66,count.c[0]); outp(66,count.c[1]); ch=inp(97); outp(97,ch|3); pause(time); outp(97,ch); } void pause(int time) { int t1,t2; union REGS in,out; in.h.ah=0X2c; int86(0X21,&in,&out); /* 取当前时间*/ t1=t2=100*out.h.dh+out.h.dl; /*out.h.dh 为秒值,out.h.dl 为1/100 秒值*/ while(t2-t1<time) { int86(0X21,&in,&out); t2=100*out.h.dh+out.h.dl; if (t2<t1)t2+=6000; /* 增加一分钟*/ } } 9.3.2 自动识谱音乐程序 音乐的简谱是由各种音符构成的,将这些音符按不同的频率、持续时间连续发出声音,就形成了旋律。因此音乐演奏的关键是曲调的定义与识别及发音时间的控制。 为了实现计算机自动识谱,可定义一套曲调的编码,其中“ 1 2 3 4 5 6 7”表示中音的1、2、3、5、6、7;高音可在中音之后加“ *”;低音在中音之后加“;”号;减号“ -”表示两拍;“.”表示一拍半;“=”表示四分之一拍;下画线“ _”表示1 / 2拍。 我们可用文本编辑软件(如E D I T)按上述编码将一首曲子的乐谱输到计算机中,得到乐谱文件供程序调用。其中乐谱文件的第一节的数字分别为:节拍基数和速度,输入时用空格分开。乐谱文件的第二行到最后一行为歌曲的内容,每小节之间用“ /”分开。 下面根据“世上只有妈妈好”编制曲谱文件如下Ma.txt:
程序中设置两个整型数组S a和S b用于存放音符的频率及节拍,其内容是一一对应的,若Sa[i]存放音符,则Sb[i]是该音符的演奏节拍。 发声的原理是利用C的标准库函数Sound( )发声,若要发音中音“ 1”音符,其音频为“262”,则函数调用Sound(262)则可通过PC机扬声器发音控制音符发声的时间由标准函数delay( )决定,nosound( )函数为关闭扬声器发音。 源程序如下: #include <stdio.h> #include<dos.h> #include<math.h> #include<stdlib. h> #include<string.h> #include<bios.h> int sa[1000],sb[1000]; int j,step,rate,len,lenl,half; chat, strl27[127]; int getmusic(); void chang(void); void music(void); /************************/ int main() { len=0; len1=0; if(getmusic()!=0) exit (1); delay(500); music( ); return 0; } /******************/ int getmusic() { FILE * fp; if((fp=fopen("ma.txt","r"))==NULL) /* 打开曲谱文件*/ { printf("file not open\n") ; return 1; } fscanf(fp, "%d %d\n", &step,&rate); while(! fgets(str127, 127, fp)==NULL) { chang ( ); } fclose (fp); return 0; } /******************************/ void chang(void) { int k; for(k=0;k<strlen(strl27);k++) /*组合音符频率*/ if ((str127[k]>=‘0’)& & ( s t r 1 2 7 [ k ] < ’ 7 ’ ) ) { sa[len]=str127[k]-48; Switch(sa[len]) { case 1:sa[len]=262;break; case 2:sa[len]=294;break; case 3:sa[len]=330;break; case 4:sa[len]=349;break; case 5:sa[len]=392;break; case 6:sa[len]=440;break; case 7:sa[len]=494;break; case 0:sa[len]=0; } len++; if(len>999) exit(0); half=0; } for(k=0;k<strlen(str127);k++)sb[k]=step; for(k=0;k<strlen(str127);k++) { switch(str127[k]) /* 组合音符节拍*/ { case '_':len1++; break; case'.': sb[len1]=sb[len]*3/4; lenl++; break; case '=': sb[len1]=ceil(sb[len1]/4); lenl++; break; case'': sb[len1]=sb[len1]/2; lenl++; break; case'_': sb[len1]=ceil(sb[len1]/2); lenl++; break; case ';': if(sa[len1]>0) sa[len1]=sa[len1]/2; break; case '*': if(sa[len1]>0) sa[len1]=sa[len1]*2; break; } } } /***********************************/ void music(void) { j=0; while ((j<=len)&&(bioskey(1)==0)) { sound(2*sa[j]); /*发声*/ delay(2*rate*sb[j]); /* 延迟*/ nosound( ); /*关闭发声*/ j++; } } 9.3.3 实现后台演奏音乐的技巧 BASIC语言有一个前后台演奏音乐的语句play,该语句有很强的音乐功能。而C语言虽有sound( )函数,但不能进行后台演奏,并且必须指明音乐频率,才能使它发声。为此可编制一个与play语句相同的后台演奏音乐函数。 若要奏乐,每一个音符必须有一个频率用sound 去发声,且必须有适当的时间延时,形成拍子,这样才能演奏音乐。我们可用指定1拍的时间来推出其它节拍。例如: #define L1 1000 #define L2 L1/2 #define l4 L1/4 即L1为1拍,L2为1/2拍,L4为1/4拍。 后台演奏,可通过修改1 C向量来实现。计算机每秒发出1 8 . 2次中断调用1 C,因此,就可以通过它来计算,实现后台演奏。 程序PLAY.C只是一个简单的后台演奏音乐的例子,将其编译后,就可在DOS提示符后直接执行,演奏过程中,按任一键都将停止演奏。 [例9-13] 后台演奏程序PLAY.C #include<stdio.h> #include<dos.h> #include<bios.h> #include<conio.h> #define L1 1000 #define L2 L2/2 #define L4 L1/4 void play(int *); void interrupt new_int9(void); void interrupt (*old_int9)(void); int HZ[4][7]={ {131,147,165,175,196,220,247}, {262,294,330,349,392,440,494}, {523,587,659,698,784,880,980} }; int *s; int buf[100]={11,12,12,12,13,12,14,12,16,12,17,12,21,11,22,12,23,12,24,12,25,12,26,12,27,12,31,12,0,0,0}; void main(void) { play(buf); while (*s && !bioskey(0)); /* 判断结束条件*/ nosound(); setvect(0X1c,old_int9); } void play(int *ms) { s=ms; old_int9=getvect(0X1c); setvect(0X1c,new_int9); } void interrupt new_int9(void) { static int count=0,tt=0; count++; if (*s!=0) { if (count>=tt) { sound(HZ[*s/10][*s%10]);s++; tt=*s*18.2/1000; s++; count=0; } else nosound(); old_int9( ); } } |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() ![]() |