您现在的位置: 中国男护士网 >> 考试频道 >> 计算机等级 >> 二级辅导 >> C语言 >> 辅导 >> 正文    
  C语言程序设计(第9章实用编程技巧)3 【注册男护士专用博客】          

C语言程序设计(第9章实用编程技巧)3

www.nanhushi.com     佚名   不详 

9.3 音响技巧
9.3.1 音乐程序设计
    我们知道,音乐是音高和音长的有序组合,设计微机音乐最重要的就是如何定义音高和音长,以及如何让扬声器发出指定的音符。下面给出音符与频率的关系表。C语言提供的三个函数sound( )、nosound( )和clock( )可以很方便地解决上述的问题。sound( )函数可以用指定频率打开PC机扬声器直到用nosound( )函数来关闭它; clock( )函数正好用来控制发声时间,而且它不受PC机主频高低的影响。下面这段程序可使微机发出c调1的声音。

表9-2 音符与频率关系表
音符 c d e f g a b
1 2 3 4 5 6 7
频率 262 294 330 349 392 440 494

音符 c d e f g a b
1 2 3 4 5 6 7
频率 523 587 659 698 784 880 988

音符 c d e f g a b
1 2 3 4 5 6 7
频率 1047 1175 1319 1397 2568 1760 1976

[例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:

    曲谱文件首行是8节拍基数, 5 0是演奏速度,从第二行开始至文件尾,均为曲谱正文。需要说明的是在曲谱文件中,每个音符应跟上其音符的节拍,文中空格‘ ’符表示其音符是全音符。
    程序中设置两个整型数组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( );
    }
}

 

文章录入:杜斌    责任编辑:杜斌 
  • 上一篇文章:

  • 下一篇文章:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
     

    联 系 信 息
    QQ:88236621
    电话:15853773350
    E-Mail:malenurse@163.com
    免费发布招聘信息
    做中国最专业男护士门户网站
    最 新 热 门
    最 新 推 荐
    相 关 文 章
    没有相关文章
    专 题 栏 目