<dfn id="w48us"></dfn><ul id="w48us"></ul>
  • <ul id="w48us"></ul>
  • <del id="w48us"></del>
    <ul id="w48us"></ul>
  • DOS下DSP播音的編程

    時(shí)間:2024-06-06 09:10:14 計(jì)算機(jī)畢業(yè)論文 我要投稿
    • 相關(guān)推薦

    DOS下DSP播音的編程

    摘要 該文介紹了DSP編程的基本原則和方法,并給出程序?qū)嵗龓椭斫?讀者可以此為基礎(chǔ)來(lái)拓展、生成自己的實(shí)用程序。
    在DOS下編程,將聲音轉(zhuǎn)化為數(shù)據(jù)記錄下來(lái),或?qū)?shù)據(jù)轉(zhuǎn)化為聲音,通過(guò)聲卡上配置的喇叭回放出來(lái),是一項(xiàng)很有實(shí)用價(jià)值和開(kāi)發(fā)魅力的技術(shù)。時(shí)下流行的聲卡,如Sound Blaster Pro及其兼容卡,都配有數(shù)字聲音處理器DSP芯片(Digital Sound Processor),專(zhuān)門(mén)用于對(duì)聲音進(jìn)行數(shù)字記錄及回放,是聲音數(shù)字處理的基礎(chǔ)硬件。而WAV文件、VOC文件等,則都是這些數(shù)據(jù)記載的具體形式。Creative公司為了方便用戶(hù),提供了一組CT-Voice驅(qū)動(dòng)程序,專(zhuān)門(mén)針對(duì)VOC文件,作為開(kāi)發(fā)利用DSP功能的軟接口,使用比較方便。但是,也造成了某些限制。對(duì)于開(kāi)發(fā)者而言,直接對(duì)DSP硬件編程,實(shí)現(xiàn)其功能,也許是更有吸引力的。
    聲音,無(wú)論是從揚(yáng)聲器輸出的,還是從話(huà)筒輸入的,都是模擬量。
    而數(shù)據(jù),無(wú)論是內(nèi)存里操作的,還是磁盤(pán)上存儲(chǔ)的都是數(shù)字量。因此,微機(jī)處理聲音,大多離不開(kāi)ADC與DAC兩種轉(zhuǎn)換。由于聲音數(shù)據(jù)的數(shù)據(jù)量比較大,在聲音的數(shù)字處理中,除直接由CPU進(jìn)行傳輸外,批量數(shù)據(jù)常采用DMA方式傳輸,以節(jié)省較多的CPU時(shí)間。
    總括起來(lái),ADC與DAC兩種轉(zhuǎn)換方式,直接傳輸和DMA傳輸這兩種傳輸方式,再加上不同的壓縮方式,如喇叭控制、靜寂等等,所有這些的不同組合,就構(gòu)成了DSP的各種功能。根據(jù)DSP的硬件原理,其各種功能都規(guī)定了一定的操作步驟。

    一、DSP編程要點(diǎn)
    在DSP編程中,主要注意命令與端口兩個(gè)層次的操作。

    1.DSP命令。DSP的功能一般以一個(gè)操作碼(稱(chēng)作命令號(hào))的寫(xiě)操作為中心,按規(guī)定的步驟,配合若干必要的輔助操作,構(gòu)成一串操作的組合,稱(chēng)為DSP命令。如8位直接播放功能命令號(hào)為10h,8位直接錄音功能命令號(hào)為20h,喇叭的通斷功能命令號(hào)分別為d1h與d3h等等。

    2.端口操作。DSP命令主要靠端口操作來(lái)實(shí)現(xiàn)。端口操作包括DSP初始化、寫(xiě)DSP命令(即發(fā)DSP命令)、讀DSP狀態(tài)參數(shù)、DSP中斷等。所涉及的端口地址及相應(yīng)的用途如表1。
    表1 DSP端口及用途
    端口地址由基址2x0h加6、0ah、0ch、0eh等形成,其中,x可取值1、2、3、4、5、6等,具體情況隨硬件設(shè)置而定,多數(shù)卡在出廠(chǎng)被默認(rèn)設(shè)置為2,即基址為220h。通過(guò)跳線(xiàn),可改變此值,避免與其它設(shè)備口地址沖突。

    二、編程實(shí)例
    DSP的功能是比較豐富的,限于篇幅,本文只簡(jiǎn)要介紹其中的8位直接播放功能,由此舉一反三,其它功能的用法不難得知。各功能的規(guī)定操作可參考文獻(xiàn)1和2。

    1.命令操作步驟。8位直接播放功能的操作步驟如下:
    ·寫(xiě)命令號(hào)10h;
    ·寫(xiě)數(shù)據(jù)字節(jié)(即播放聲音的8位數(shù)據(jù));
    ·按采樣率所需時(shí)間周期延時(shí)。
    以此三步操作為循環(huán)體,進(jìn)行n次循環(huán),即完成播放。其中,n為聲音數(shù)據(jù)字節(jié)數(shù)。

    2.2xch端口寫(xiě)操作。在DSP編程中,無(wú)論是發(fā)送命令,還是發(fā)送數(shù)據(jù),都是通過(guò)寫(xiě)端口2xch來(lái)完成的。在寫(xiě)端口2xch之前,應(yīng)先讀此端口,直到所得值的bit7為0,這才表明此端口處于可寫(xiě)狀態(tài),才能進(jìn)行寫(xiě)操作。此過(guò)程的c語(yǔ)言形式如下:
    while (inportb(0x22c)&0x80);
    outportb(0x22c,byte);
    這里假定端口基址為220h。句中byte可以是命令號(hào),也可以是數(shù)據(jù)。

    3.定時(shí)器。為使播放按一定的采樣率進(jìn)行,需對(duì)數(shù)據(jù)發(fā)送進(jìn)行定時(shí)控制。這一般是借用主機(jī)定時(shí)中斷int8,將其調(diào)用頻率提高到與采樣率相當(dāng)?shù)某潭?利用其監(jiān)視、控制數(shù)據(jù)發(fā)送的時(shí)間,來(lái)滿(mǎn)足播音頻率的要求。關(guān)于定時(shí)中斷的編程技術(shù)已有過(guò)許多介紹,限于篇幅,不再贅述,讀讀文后的程序清單,即一目了然。應(yīng)該說(shuō)明的是,對(duì)于CPU較慢的機(jī)型如386,由于計(jì)時(shí)代碼本身的執(zhí)行時(shí)間可能已經(jīng)超過(guò)采樣率對(duì)應(yīng)的時(shí)間周期,定時(shí)控制就達(dá)不到預(yù)期的效果。這種情況下,用一個(gè)空循環(huán)來(lái)定時(shí),調(diào)整循環(huán)次數(shù),即可滿(mǎn)足頻率要求。此法的缺點(diǎn)是定時(shí)精度差,參數(shù)因CPU速度而異。所幸的是,目前多數(shù)配置多媒體的PC機(jī),其CPU都在486以上。

    4.內(nèi)存利用。人耳可辨聲音的最高頻率可達(dá)20kHz以上,因此DSP的采樣率至少也要達(dá)到與此相當(dāng)?shù)乃?而為了容納立體聲雙聲道信息,采樣率還要再翻一倍。常見(jiàn)的WAV聲音的采樣率有44100、22050、11025等。在這么高的采樣率下,聲音的數(shù)據(jù)量自然很大,如44k采樣率下,20秒的錄音數(shù)據(jù)長(zhǎng)達(dá)800多k。為在DOS常規(guī)內(nèi)存內(nèi)處理這種規(guī)模的數(shù)據(jù),實(shí)例程序采取了分塊處理的方式,將數(shù)據(jù)分成以當(dāng)前剩余自由內(nèi)存大小為單位的塊,將其逐次讀入,逐次處理。同時(shí),由于C語(yǔ)言的read()函數(shù)每次讀操作的字節(jié)數(shù)最多不過(guò)64k-1,因此,每一個(gè)分塊又需分
    若干次讀入。實(shí)例表明,經(jīng)此法處理的播放程序不受WAV文件長(zhǎng)度的限制,筆者在Windows下錄制的長(zhǎng)達(dá)5M多的WAV文件(11k采樣率,約8分鐘)也照播不誤。

    5.聲音文件。本文提供的程序?qū)嵗渎曇魯?shù)據(jù)取自WAV文件,其實(shí),對(duì)于VOC文件,本播放技術(shù)也一樣適用,只不過(guò)數(shù)據(jù)的讀取格式有所不同而已。關(guān)于WAV文件的格式,可參考文獻(xiàn)3,VOC文件的格式參考獻(xiàn)1和2。
    實(shí)例程序用Borland C++ 3.1編譯,在配置OPTI 386主板、海洋48
    6主板及多種與SoundBlaster Pro兼容聲卡的兼容機(jī)上運(yùn)行通過(guò)。

    三、源程序清單
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include"timer.h"
    #define n1 20
    #define n2 100
    struct WavHead
    {
    char riff[4];
    long size0;
    char wavefmt[8];
    long size1;
    int fmttag;
    int channel;
    long samplespersec;
    long bytespersec;
    int blockalign;
    int bitspersample;
    char flg[4];
    }whead;
    unsigned Port=0x210;
    char Found=0;
    unsigned cnt1,cnt2;
    void PortReset();
    void outwave(un

    signed char huge *,long);
    void WritePortC(unsigned char);
    void errexit(char *);
    void main()
    {

    int fp;


    unsigned n,r,nn,i,j;
    char name[32];
    long fermem,rr,datasize;
    unsigned char huge *data,huge *p;
    if(argc<2)errexit("miss file name\n");
    strcpy(name,argv[1]);strcat(name,".wav");
    fp=-open(name,0-RDONLY);if(fp=-1)errexit("Error open fil
    e\n");
    -read(fp,&whead,sizeof(WavHead));
    if(whead.blockalign=1 && strncmp(whead.flg,"data",4)==0)
    {
    -read(fp,&datasize,4);//單聲道WAV數(shù)據(jù)
    }
    else if(whead.blockalign=2 && strncmp(whead.flg,"fact"
    ,4)==0)
    {
    lseek(fp,12l,1);
    -read(fp,&datasize,4);//雙聲道WAV數(shù)據(jù)
    }
    else errexit("Error file struct\n");
    farmem=farcoreleft();
    PortReset();//初始化DSP端口
    Counter=0;//開(kāi)始計(jì)時(shí)
    SetTimer(NewTimer,44100);//調(diào)整時(shí)間中斷頻率
    WritePortC(0xd1);//接通喇叭
    if(farmem≥datasize)//數(shù)據(jù)量不超過(guò)內(nèi)存容量
    {
    p=data=(unsigned char huge *)farmalloc(datasize);
    n=datasize/32768;r=datasize%32768;
    for(i=0;i-read(fp,p,r);
    outwave(data,datasize);
    }
    else//數(shù)據(jù)量超過(guò)內(nèi)存容量
    {
    nn=datasize/farmem;//分塊操作的塊數(shù)
    rr=datasize%farmem;//最后一塊的大小
    n=farmem/32768;//每塊read次數(shù)
    r=farmem%32768;//read余零尾數(shù)
    data=(unsigned char huge *)farmalloc(farmem);
    for(i=0;i{
    p=data;
    for(j=0;j-read(fp,p,r);
    //讀入內(nèi)存
    outwave(data,farmem);//發(fā)送聲音數(shù)據(jù)
    }
    p=data;
    n=rr/32768;r=rr%32768;//最后塊的操作
    for(i=0;i-read(fp,p,r);
    //讀入
    outwave(data,rr);//發(fā)送
    }
    WritePortC(0xd3);//斷開(kāi)喇叭
    RestoreTimer();//恢復(fù)時(shí)間中斷
    farfree(data);
    -close(fp);
    }
    void PortReset()//初始化DSP端口
    {
    cnt1=n1;


    while(Port≤0x260)&&!Found)
    {//測(cè)端口基址
    outportb(Port+6,1);
    outportb(Port+6,0);
    cnt2=n2;
    while(cnt2>2 && 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 failed\n");//找不到基址
    }
    void outwave(unsigned char huge *p,long len)
    {//發(fā)送聲音數(shù)據(jù)
    long i;
    int smpl;
    smpl=44100/whead.samplespersec/whead.blockalign;
    //采樣周期系數(shù)
    for(i=0;i{
    WritePortC(0x10);//發(fā)送命令
    WritePortC(p[i]);//發(fā)送數(shù)據(jù)
    while(Counter}
    }
    void WritePortC(unsigned char v)
    {
    while(inportb(Port+0xc)&0x80);//等待寫(xiě)有效狀態(tài)
    outportb(Port+0xc,v);//寫(xiě)端口(發(fā)送)
    }
    void errexit(char *msg)
    {
    -AX=3;
    asm int 10h
    printf(msg);
    exit(0);
    }
    //Timer.h
    #includ
    #define OldTimerInt 0x60
    unsigned long Counter;
    unsigned CounterInt8,fpI8;
    void SetTimer(void interrupt(*Rout)(…),unsigned freq)
    {//設(shè)置新頻率的定時(shí)中斷
    int ICnt;


    fpI8=(freq+9)/18;//新舊頻率的倍數(shù)
    asm cli
    ICnt=1193180/freq;
    outportb(0x43,0x36);
    outportb(0x40,ICnt & 255);
    outportb(0x40,ICnt》8);
    setvect(OldTimerInt,getvect(

    8));//保存舊定時(shí)中斷
    setvect(8,rout);//置新的定時(shí)中斷
    sam sti;
    }
    void RestoreTimer()
    {
    asm cli
    outportb(0x43,0x36);
    outportb(0x40,0);
    outportb(0x40,0);
    setvect(8,getvect(OldTimerInt));//恢復(fù)原定時(shí)中斷
    asm sti
    }
    void interrupt NewTimer(…)
    {//新定時(shí)中斷
    REGPACK R;
    Counter++;//給應(yīng)用程序提供新頻率的計(jì)數(shù)
    if(--CounterInt8=0)
    {
    intr(OldTimerInt,&R);//按原頻率走動(dòng)時(shí)鐘
    CounterInt8=fpI8;//用新舊頻率的倍數(shù)分頻
    }
    else outportb(0x20,0x20);//退出中斷
    }

    參考文獻(xiàn)
    1 閻小兵等.多媒體開(kāi)發(fā)工具.北京:電子工業(yè)出版社,1994.
    2 Josha Munnik等著,敬萬(wàn)鈞等譯.聲霸--原理與應(yīng)用.北京:電子工業(yè)出版社,1995.
    3 石寧等.在DOS下使用Windows *.WAV文件.計(jì)算機(jī)世界月刊,1995(3)44-46.

    【DOS下DSP播音的編程】相關(guān)文章:

    在 DOS 下使用Windows *.WAV 文件03-03

    DOS界面下通用圖形編輯軟件的設(shè)計(jì)03-06

    TMS320C62X DSP的混合編程研究03-21

    通過(guò)JTAG口對(duì)DSP外部Flash存儲(chǔ)器的在線(xiàn)編程03-26

    TMS320C32 DSP的中斷編程方法及BOOT功能實(shí)現(xiàn)03-18

    Linux下的GTK圖形界面編程12-04

    基于DSP的數(shù)控二維橢圓及圓插補(bǔ)算法簡(jiǎn)化編程研究11-22

    DOS真彩色模式下真彩色圖像顯示技術(shù)03-03

    DOS用戶(hù)界面的設(shè)計(jì)03-20

    主站蜘蛛池模板: 国产偷国产偷高清精品| 人妻少妇精品中文字幕av蜜桃| 亚洲AV无码久久精品色欲| 麻豆精品| 午夜精品久久久久久影视777| 亚洲精品无码午夜福利中文字幕| 小辣椒福利视频精品导航| 亚洲精品高清久久| 久久精品国产亚洲Aⅴ蜜臀色欲| 亚洲福利精品电影在线观看| 亚洲精品天堂成人片?V在线播放| 久久91精品国产91久久小草| 伊人久久精品线影院| 精品国际久久久久999波多野| 四虎精品影院永久在线播放| 久久久久久国产精品美女| 久久伊人精品青青草原高清| 成人一区二区三区精品| 精品久久久久久国产三级| 亚洲国产成人精品女人久久久 | 久久精品国产亚洲沈樵| 人妻少妇精品无码专区二区| 四虎国产精品永久地址入口| 精品人妻少妇嫩草AV无码专区 | 国产精品素人搭讪在线播放| 国产系列高清精品第一页| 亚洲av无码国产精品色在线看不卡| 91精品国产91热久久久久福利| 精品水蜜桃久久久久久久| 91精品婷婷国产综合久久| 精品国产一区二区三区久久| 成人精品视频一区二区三区| 国产伦精品一区二区免费| 无码精品一区二区三区免费视频| 成人午夜精品视频在线观看| 精品成人一区二区三区四区| 四虎影视884a精品国产四虎| 久久国产精品一区二区| 久久福利青草精品资源站免费 | 精品人妻伦九区久久AAA片69| 精品人体无码一区二区三区|