2023年11月26日发(作者:)

DOS 下使用Windows *.WAV 文件

摘要该文介绍了inds声波文件(*.av)的格式,然后分析了在ds下不使用声音适

配卡播放声波文件的关键问题,并给出了程序清单。

关键词ds应用软件开发多媒体声波文件在ds应用软件开发过程中,我们非常希

望能在不附加任何硬件设备的条件下实现一些简单的多媒体功能。

过去许多文章中都讨论过inds图像文件(*.bp,*.px)的格式及其用于美化ds程序

界面的方法。在sids3.1以后,inds又提供了标准的声波文件(*.av),因此我们可以

利用已有的声波文件镶嵌在自己的软件中,在ds下实现语音或其它音响的播放,提高

我们的软件质量。

一、声波文件格式分析

*.av文件作为多媒体中使用的声波文件格式之一,它是以riff格式为标准的。

riff是英文resureinterhangefilefrat的缩写,每个av文件的头四个字节便是

“riff”。

常见的声波文件主要有两种,分别对应于单声道(11.025khz采样率、8bit的采样

)和双声道(44.1khz采样率、16bit的采样值)。这里,采样率是指:声波信号[模→

]转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声波模拟信号的

积分值,在编程播放过程中我们认为它是扬声器在此周期单位时间段的音量。

*.av文件由文件头和数据体两大部分组成。其中文件头又分为riff/av文件标识

段和声波数据格式说明段两部分。

av文件各部分内容及格式见附表。

对于单声道声波文件,采样数据为八位的短整数(shrtint00h-ffh);而对于双声

道立体声声波文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代

表左右两个声道。

@@;*.av文件格式说明表@@

二、av文件编程

在没有声音适配卡的条件下,利用p机内部扬声器发声需解决几个关键问题。

首先是如何产生按指定采样率要求的标准时间间隔段,以此为基础控制扬声器发

声。

由于此时间段要求精确且非常短暂,因此实现起来有一定的难度。解决该问题的

思路是修改8253定时器芯片的计数器0(地址:040h)的初始值,改变系统时钟中断频

率使其和采样率相一致,建立用户的时钟中断例程,最终产生标准的时间间隔段。但

是在我们修改原有系统时钟中断(int08h)以后,最终必须恢复原有18.2hz的系统时钟

中断。

其次是如何快速地打开和关闭扬声器。解决这个问题的方法是直接向8255芯片端

(地址:061h)写操作。由于p机机内扬声器发声只有开/闭两种状态,并不能控制音

量大校

因此还须考虑如何通过开闭扬声器来摸拟实现音量大小的控制。实现方法是:在

每个时间单位内通过改变扬声器打开延时的长短代表音量的大校例如:对于8bit单声

道声波文件,采样数据的最大值是0ffh,那么在每个标准时间单位内扬声器打开时间

应为delay=(采样值/256)*标准时间段长度。在此思想下可以将该方法简化,设扬声

器延时只有01(时间单位)两种情况,即在每个时间单位内,如果采样值大于128

发声,如果采样值小于128就不发声。显然这样做是以抛弃大量声波信息为代价的,

采用的信息量只占原有用信息的1/12

8,所以这种方法产生的音质较差。

三、程序实例

下面是一个能播放11.025khz/8bit/单声道声波文件的演示程序。关于使用*.av

文件的其它细节,可通过阅读本程序得到。它采用了第二种延时方式,如果读者有兴

趣提高音质可将其改成使用第一种方法,只需将neint08h中的声音开/关判断(128

比较)部分改成循环等待即可。

循环次数通过i=int(vl[unter]/256)*axties得到。

式中axties为延长一个标准时间单位的循环次数。

程度运行环境:486兼容机,sds6.0t2.0编译系统。

/*/*/*

*.av文件播放程序de.,石宁1994.12

*/*/*/

#inlude"ds.h"

#inlude"stdi.h"

#inlude"string.h"

#defineaxsize50000

strutave-file_head/*声波*/

{/*文件头*/

harriff_id[4];/*结构体*/

lngintsize0;

harave-ft[8];

lntintsizel;

intfttag;

inthannel;

lngintsaplesperse;

lngintbyteperse;

intblkalign;

intbitpersaples;

}filehead;

lngintdatasize,unter=0;

unsignedharvl[axsize];

unsignedlkdiv;

intldlk=0,running=1;

vidsundn();

vidsundff();

vidinterrupt(*ldint8h)();

vidinterruptneint8h()

else

{

i=inprtb(0x61);/*关扬*/

i=i0x00f;

utprtb(0x61,i);/*声器*/

}

unter+=1;

enable();/*打开中断*/

if(unter=datasize)unter=0;

utprtb(0x20,0x20);

running=1;

}

}

vidsundn()

{

lkdiv=1193180/perse;

/*计算8253计数器0初始值*/

ldint8h=getvet(0x08);/*保存旧的08h中断向量*/

setvet(0x08,neint8h);/*置新的08h中断例程*/

utprtb(0x43,0xb6);/*初始化*/

utprtb(0x42,1);/*8253计数器3/

utprtb(0x42,0);/*初始值*/

utprtb(0x43,0x36);/*修改8253*/

utprt(0x40,lkdiv0x00ff);/*计数器0*/

utprt(0x40,(lkdiv8)0x00ff);/*初始值*/

}

vidsundff()

{

inti;

setvet(0x08,ldint8h);/*恢复旧的08h中断向量*/

utprtb(0x43,0x36);/*恢复正常*/

utprt(0x40,0)/*的时钟中*/

utprt(0x40,0);/*断频率*/

i=inprtb(0x61);/*关扬*/

i=i0x00f;

utprtb(0x61,i);/*声器*/

}

vidain(intarg,har*argv[])

{

file*fp;/

if(arg==1)

{

fr(j=0;jdatasize;j++)vl[j]=get(fp);

lse(fp);

printf("%sn","nsundn,presseststp!");