• <sub id="h4knl"><ol id="h4knl"></ol></sub>
    <sup id="h4knl"></sup>
      <sub id="h4knl"></sub>

      <sub id="h4knl"><ol id="h4knl"><em id="h4knl"></em></ol></sub><s id="h4knl"></s>
      1. <strong id="h4knl"></strong>

      2. DOS下DSP播音的編程

        時間:2024-06-06 09:10:14 計算機畢業論文 我要投稿
        • 相關推薦

        DOS下DSP播音的編程

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

        一、DSP編程要點
        在DSP編程中,主要注意命令與端口兩個層次的操作。

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

        2.端口操作。DSP命令主要靠端口操作來實現。端口操作包括DSP初始化、寫DSP命令(即發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;
        ·寫數據字節(即播放聲音的8位數據);
        ·按采樣率所需時間周期延時。
        以此三步操作為循環體,進行n次循環,即完成播放。其中,n為聲音數據字節數。

        2.2xch端口寫操作。在DSP編程中,無論是發送命令,還是發送數據,都是通過寫端口2xch來完成的。在寫端口2xch之前,應先讀此端口,直到所得值的bit7為0,這才表明此端口處于可寫狀態,才能進行寫操作。此過程的c語言形式如下:
        while (inportb(0x22c)&0x80);
        outportb(0x22c,byte);
        這里假定端口基址為220h。句中byte可以是命令號,也可以是數據。

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

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

        5.聲音文件。本文提供的程序實例其聲音數據取自WAV文件,其實,對于VOC文件,本播放技術也一樣適用,只不過數據的讀取格式有所不同而已。關于WAV文件的格式,可參考文獻3,VOC文件的格式參考獻1和2。
        實例程序用Borland C++ 3.1編譯,在配置OPTI 386主板、海洋48
        6主板及多種與SoundBlaster Pro兼容聲卡的兼容機上運行通過。

        三、源程序清單
        #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數據
        }
        else if(whead.blockalign=2 && strncmp(whead.flg,"fact"
        ,4)==0)
        {
        lseek(fp,12l,1);
        -read(fp,&datasize,4);//雙聲道WAV數據
        }
        else errexit("Error file struct\n");
        farmem=farcoreleft();
        PortReset();//初始化DSP端口
        Counter=0;//開始計時
        SetTimer(NewTimer,44100);//調整時間中斷頻率
        WritePortC(0xd1);//接通喇叭
        if(farmem≥datasize)//數據量不超過內存容量
        {
        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//數據量超過內存容量
        {
        nn=datasize/farmem;//分塊操作的塊數
        rr=datasize%farmem;//最后一塊的大小
        n=farmem/32768;//每塊read次數
        r=farmem%32768;//read余零尾數
        data=(unsigned char huge *)farmalloc(farmem);
        for(i=0;i{
        p=data;
        for(j=0;j-read(fp,p,r);
        //讀入內存
        outwave(data,farmem);//發送聲音數據
        }
        p=data;
        n=rr/32768;r=rr%32768;//最后塊的操作
        for(i=0;i-read(fp,p,r);
        //讀入
        outwave(data,rr);//發送
        }
        WritePortC(0xd3);//斷開喇叭
        RestoreTimer();//恢復時間中斷
        farfree(data);
        -close(fp);
        }
        void PortReset()//初始化DSP端口
        {
        cnt1=n1;


        while(Port≤0x260)&&!Found)
        {//測端口基址
        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)
        {//發送聲音數據
        long i;
        int smpl;
        smpl=44100/whead.samplespersec/whead.blockalign;
        //采樣周期系數
        for(i=0;i{
        WritePortC(0x10);//發送命令
        WritePortC(p[i]);//發送數據
        while(Counter}
        }
        void WritePortC(unsigned char v)
        {
        while(inportb(Port+0xc)&0x80);//等待寫有效狀態
        outportb(Port+0xc,v);//寫端口(發送)
        }
        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)
        {//設置新頻率的定時中斷
        int ICnt;


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

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

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

        《&.doc》
        将本文的Word文档下载到电脑,方便收藏和打印
        推荐度:
        点击下载文档

        【DOS下DSP播音的編程】相關文章:

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

        DOS界面下通用圖形編輯軟件的設計03-06

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

        通過JTAG口對DSP外部Flash存儲器的在線編程03-26

        TMS320C32 DSP的中斷編程方法及BOOT功能實現03-18

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

        基于DSP的數控二維橢圓及圓插補算法簡化編程研究11-22

        DOS真彩色模式下真彩色圖像顯示技術03-03

        DOS用戶界面的設計03-20

        在线咨询
        国产高潮无套免费视频_久久九九兔免费精品6_99精品热6080YY久久_国产91久久久久久无码
      3. <sub id="h4knl"><ol id="h4knl"></ol></sub>
        <sup id="h4knl"></sup>
          <sub id="h4knl"></sub>

          <sub id="h4knl"><ol id="h4knl"><em id="h4knl"></em></ol></sub><s id="h4knl"></s>
          1. <strong id="h4knl"></strong>

          2. 久久午夜国产精品 | 亚洲中文字幕免费 | 精品国产亚洲一区二区三区 | 亚洲一区二区三区深田咏美 | 亚洲国产日韩综合久久精品 | 亚洲怡红院在线视频 |

            DOS下DSP播音的編程

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

            一、DSP編程要點
            在DSP編程中,主要注意命令與端口兩個層次的操作。

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

            2.端口操作。DSP命令主要靠端口操作來實現。端口操作包括DSP初始化、寫DSP命令(即發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;
            ·寫數據字節(即播放聲音的8位數據);
            ·按采樣率所需時間周期延時。
            以此三步操作為循環體,進行n次循環,即完成播放。其中,n為聲音數據字節數。

            2.2xch端口寫操作。在DSP編程中,無論是發送命令,還是發送數據,都是通過寫端口2xch來完成的。在寫端口2xch之前,應先讀此端口,直到所得值的bit7為0,這才表明此端口處于可寫狀態,才能進行寫操作。此過程的c語言形式如下:
            while (inportb(0x22c)&0x80);
            outportb(0x22c,byte);
            這里假定端口基址為220h。句中byte可以是命令號,也可以是數據。

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

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

            5.聲音文件。本文提供的程序實例其聲音數據取自WAV文件,其實,對于VOC文件,本播放技術也一樣適用,只不過數據的讀取格式有所不同而已。關于WAV文件的格式,可參考文獻3,VOC文件的格式參考獻1和2。
            實例程序用Borland C++ 3.1編譯,在配置OPTI 386主板、海洋48
            6主板及多種與SoundBlaster Pro兼容聲卡的兼容機上運行通過。

            三、源程序清單
            #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數據
            }
            else if(whead.blockalign=2 && strncmp(whead.flg,"fact"
            ,4)==0)
            {
            lseek(fp,12l,1);
            -read(fp,&datasize,4);//雙聲道WAV數據
            }
            else errexit("Error file struct\n");
            farmem=farcoreleft();
            PortReset();//初始化DSP端口
            Counter=0;//開始計時
            SetTimer(NewTimer,44100);//調整時間中斷頻率
            WritePortC(0xd1);//接通喇叭
            if(farmem≥datasize)//數據量不超過內存容量
            {
            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//數據量超過內存容量
            {
            nn=datasize/farmem;//分塊操作的塊數
            rr=datasize%farmem;//最后一塊的大小
            n=farmem/32768;//每塊read次數
            r=farmem%32768;//read余零尾數
            data=(unsigned char huge *)farmalloc(farmem);
            for(i=0;i{
            p=data;
            for(j=0;j-read(fp,p,r);
            //讀入內存
            outwave(data,farmem);//發送聲音數據
            }
            p=data;
            n=rr/32768;r=rr%32768;//最后塊的操作
            for(i=0;i-read(fp,p,r);
            //讀入
            outwave(data,rr);//發送
            }
            WritePortC(0xd3);//斷開喇叭
            RestoreTimer();//恢復時間中斷
            farfree(data);
            -close(fp);
            }
            void PortReset()//初始化DSP端口
            {
            cnt1=n1;


            while(Port≤0x260)&&!Found)
            {//測端口基址
            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)
            {//發送聲音數據
            long i;
            int smpl;
            smpl=44100/whead.samplespersec/whead.blockalign;
            //采樣周期系數
            for(i=0;i{
            WritePortC(0x10);//發送命令
            WritePortC(p[i]);//發送數據
            while(Counter}
            }
            void WritePortC(unsigned char v)
            {
            while(inportb(Port+0xc)&0x80);//等待寫有效狀態
            outportb(Port+0xc,v);//寫端口(發送)
            }
            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)
            {//設置新頻率的定時中斷
            int ICnt;


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

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

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