1、DOS 下 DSP 播音的编程 摘要 该文介绍了 DSP 编程的基本原则和方法,并给出程序实例帮助理解,读者可以此为基础来拓展、生成自己的实用程序。在 DOS 下编程,将声音转化为数据记录下来,或将数据转化为声音,通过声卡上配置的喇叭回放出来,是一项很有实用价值和开发魅力的技术。时下流行的声卡,如 Sound Blaster Pro 及其兼容卡,都配有数字声音处理器DSP 芯片(Digital Sound Processor),专门用于对声音进行数字记录及回放,是声音数字处理的基础硬件。而 WAV 文件、VOC 文件等,则都是这些数据记载的具体形式。Creative 公司为了方便用户,提供了一
2、组 CT-Voice驱动程序,专门针对 VOC 文件,作为开发利用 DSP 功能的软接口,使用比较方便。但是,也造成了某些限制。对于开发者而言,直接对 DSP 硬件编程,实现其功能,也许是更有吸引力的。声音,无论是从扬声器输出的,还是从话筒输入的,都是模拟量。而数据,无论是内存里操作的,还是磁盘上存储的都是数字量。因此,微机处理声音,大多离不开 ADC 与 DAC 两种转换。由于声音数据的数据量比较大,在声音的数字处理中,除直接由 CPU 进行传输外,批量数据常采用 DMA方式传输,以节省较多的 CPU 时间。总括起来,ADC 与 DAC 两种转换方式,直接传输和 DMA 传输这两种传输方式,
3、再加上不同的压缩方式,如喇叭控制、静寂等等,所有这些的不同组合,就构成了 DSP 的各种功能。根据 DSP 的硬件原理,其各种功能都规定了一定的操作步骤。一、DSP 编程要点在 DSP 编程中,主要注意命令与端口两个层次的操作。1.DSP 命令。DSP 的功能一般以一个操作码(称作命令号)的写操作为中心,按规定的步骤,配合若干必要的辅助操作,构成一串操作的组合,称为DSP 命令。如 8 位直接播放功能命令号为 10h,8 位直接录音功能命令号为20h,喇叭的通断功能命令号分别为 d1h 与 d3h 等等。2.端口操作。DSP 命令主要靠端口操作来实现。端口操作包括 DSP 初始化、写 DSP
4、命令(即发 DSP 命令)、读 DSP 状态参数、DSP 中断等。所涉及的端口地址及相应的用途如表 1。表 1 DSP 端口及用途端口地址由基址 2x0h 加 6、0ah、0ch、0eh 等形成,其中,x 可取值1、2、3、4、5、6 等,具体情况随硬件设置而定,多数卡在出厂被默认设置为 2,即基址为 220h。通过跳线,可改变此值,避免与其它设备口地址冲突。二、编程实例DSP 的功能是比较丰富的,限于篇幅,本文只简要介绍其中的 8 位直接播放功能,由此举一反三,其它功能的用法不难得知。各功能的规定操作可参考文献 1 和 2。1.命令操作步骤。8 位直接播放功能的操作步骤如下:写命令号 10h
5、;写数据字节(即播放声音的 8 位数据);按采样率所需时间周期延时。以此三步操作为循环体,进行 n 次循环,即完成播放。其中,n 为声音数据字节数。2.2xch 端口写操作。在 DSP 编程中,无论是发送命令,还是发送数据,都是通过写端口 2xch 来完成的。在写端口 2xch 之前,应先读此端口,直到所得值的 bit7 为 0,这才表明此端口处于可写状态,才能进行写操作。此过程的 c 语言形式如下:while (inportb(0x22c)0x80);outportb(0x22c,byte);这里假定端口基址为 220h。句中 byte 可以是命令号,也可以是数据。3.定时器。为使播放按一定
6、的采样率进行,需对数据发送进行定时控制。这一般是借用主机定时中断 int8,将其调用频率提高到与采样率相当的程度,利用其监视、控制数据发送的时间,来满足播音频率的要求。关于定时中断的编程技术已有过许多介绍,限于篇幅,不再赘述,读读文后的程序清单,即一目了然。应该说明的是,对于 CPU 较慢的机型如 386,由于计时代码本身的执行时间可能已经超过采样率对应的时间周期,定时控制就达不到预期的效果。这种情况下,用一个空循环来定时,调整循环次数,即可满足频率要求。此法的缺点是定时精度差,参数因 CPU 速度而异。所幸的是,目前多数配置多媒体的 PC 机,其 CPU 都在 486 以上。4.内存利用。人
7、耳可辨声音的最高频率可达 20kHz 以上,因此 DSP 的采样率至少也要达到与此相当的水平,而为了容纳立体声双声道信息,采样率还要再翻一倍。常见的 WAV 声音的采样率有 44100、22050、11025 等。在这么高的采样率下,声音的数据量自然很大,如 44k 采样率下,20 秒的录音数据长达 800 多 k。为在 DOS 常规内存内处理这种规模的数据,实例程序采取了分块处理的方式,将数据分成以当前剩余自由内存大小为单位的块,将其逐次读入,逐次处理。同时,由于 C 语言的 read()函数每次读操作的字节数最多不过 64k-1,因此,每一个分块又需分若干次读入。实例表明,经此法处理的播放
8、程序不受 WAV 文件长度的限制,笔者在 Windows 下录制的长达 5M 多的 WAV 文件(11k 采样率,约 8 分钟)也照播不误。5.声音文件。本文提供的程序实例其声音数据取自 WAV 文件,其实,对于 VOC 文件,本播放技术也一样适用,只不过数据的读取格式有所不同而已。关于 WAV 文件的格式,可参考文献 3,VOC 文件的格式参考献 1 和 2。实例程序用 Borland C+ 3.1 编译,在配置 OPTI 386 主板、海洋 486 主板及多种与 SoundBlaster Pro 兼容声卡的兼容机上运行通过。三、源程序清单#includeio.h#includedos.h#
9、includeconio.h#includestdio.h#includefcntl.h#includestdlib.h#includestring.h#includealloc.h#include“timer.h“#define n1 20#define n2 100struct WavHeadchar riff4;long size0;char wavefmt8;long size1;int fmttag;int channel;long samplespersec;long bytespersec;int blockalign;int bitspersample;char flg4;wh
10、ead;unsigned Port=0x210;char Found=0;unsigned cnt1,cnt2;void PortReset();void outwave(unsigned char huge *,long);void WritePortC(unsigned char);void errexit(char *);void main()int fp;unsigned n,r,nn,i,j;char name32;long fermem,rr,datasize;unsigned char huge *data,huge *p;if(argc2)errexit(“miss file
11、namen“);strcpy(name,argv1);strcat(name,“.wav“);fp=-open(name,0-RDONLY);if(fp=-1)errexit(“Error open filen“);-read(fp,whead,sizeof(WavHead);if(whead.blockalign=1 strncmp(whead.flg,“data“,4)=0)-read(fp,datasize,4);/单声道 WAV 数据else if(whead.blockalign=2 strncmp(whead.flg,“fact“,4)=0)lseek(fp,12l,1);-rea
12、d(fp,datasize,4);/双声道 WAV 数据else errexit(“Error file structn“);farmem=farcoreleft();PortReset();/初始化 DSP 端口Counter=0;/开始计时SetTimer(NewTimer,44100);/调整时间中断频率WritePortC(0xd1);/接通喇叭if(farmemdatasize)/数据量不超过内存容量p=data=(unsigned char huge *)farmalloc(datasize);n=datasize/32768;r=datasize%32768;for(i=0;in
13、;i+,p+=32768) -read(fp,p,32768);-read(fp,p,r);outwave(data,datasize);else/数据量超过内存容量nn=datasize/farmem;/分块操作的块数rr=datasize%farmem;/最后一块的大小n=farmem/32768;/每块 read 次数r=farmem%32768;/read 余零尾数data=(unsigned char huge *)farmalloc(farmem);for(i=0;inn;i+)/逐块处理p=data;for(j=0;jn;j+,p+=32768)-read(fp,p,32768)
14、;-read(fp,p,r);/读入内存outwave(data,farmem);/发送声音数据p=data;n=rr/32768;r=rr%32768;/最后块的操作for(i=0;in;i+,p+=32768)-read(fp,p,32768);-read(fp,p,r);/读入outwave(data,rr);/发送WritePortC(0xd3);/断开喇叭RestoreTimer();/恢复时间中断farfree(data);-close(fp);void PortReset()/初始化 DSP 端口cnt1=n1;while(Port0x260)!Found)/测端口基址outpo
15、rtb(Port+6,1);outportb(Port+6,0);cnt2=n2;while(cnt22 inportb(Port+0xe)128)-cnt2;if(cnt2=0|inportb(Port+0xa)!=oxaa)-cnt1;if(cnt1=0)cnt1=n1;Port=Port+0x10;else Found=1;/找到基址if(!Found)errexit(“Reset failedn“);/找不到基址void outwave(unsigned char huge *p,long len)/发送声音数据long i;int smpl;smpl=44100/whead.samplespersec/whead.blockalign;/采样周期系数for(i=0;ilen;i+)WritePortC(0x10);/发送命令WritePortC(pi);/发送数据while(Countersmpl);Counter=0;/定时void WritePortC(unsigned char v)while(inportb(Port+0xc)0x80);/等待写有效状态outportb(Port+0xc,v);/写端口(发送)void errexit(char *msg)-AX=3;asm int 10hprintf(msg);
Copyright © 2018-2021 Wenke99.com All rights reserved
工信部备案号:浙ICP备20026746号-2
公安局备案号:浙公网安备33038302330469号
本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。