0xAA55 发表于 2015-9-14 00:52:18

【DOS】C语言写的声卡(Sound Blaster 16)驱动

转载请注明出处:http://www.0xaa55.com/thread-1514-1-1.html

经过实际的测试,可以使DOSBOX、Virtual PC虚拟的DOS、Virtual Box虚拟的DOS、VMware虚拟的DOS播放WAV音乐(支持44100 Hz 16-bit CD音质)
这就是编写能播放音乐的DOS游戏所需的玩意儿了。
目前的代码对Sound Blaster 16(版本4.xx)的支持比较好,但是对低版本的(2.00以下"SBLO")支持不太好。现在已经解决了播放声音时有杂音的问题。原先的代码没有搞对时序。
但是现在遇到的问题是播放MIDI的时候,貌似不能在VMware虚拟的DOS中播放MIDI音乐。但是可以在DOSBOX中正常播放。
这个驱动是纯C写的。所以可以拿去给你的DOS游戏作为声音模块使用。
编译器用的是WATCOM C。


BIN:
整个程序的所有源码,回帖后即可下载。**** Hidden Message *****

编译器:WATCOM C
http://www.0xaa55.com/thread-1329-1-1.html

参考资料:
【PDF】Sound Blaster 16 声霸卡驱动编写向导 英文
http://www.0xaa55.com/thread-1500-1-1.html
【DOS】混合语言实现WAV文件播放的程序,声霸卡驱动源码
http://www.0xaa55.com/thread-125-1-1.html
【C语言】Midi文件播放器(可跨平台)
http://www.0xaa55.com/forum.php?mod=viewthread&tid=1489&fromuid=1

下面放出最关键的代码。#include"sb.h"
#include<dos.h>
#include<time.h>
#include<ctype.h>
#include<conio.h>
#include<stdio.h>
#include<stdarg.h>
#include<stdlib.h>
#include<memory.h>

SBInfo_t g_SBInfo={0};

#define g_Port                                g_SBInfo.IOInfo.PortNum
#define g_Version                        g_SBInfo.HWInfo.Version
#define g_DMAL                                g_SBInfo.IOInfo.DMAL
#define g_DMALAddr                        g_SBInfo.DMAInfo.DMALAddr
#define g_DMALCount                        g_SBInfo.DMAInfo.DMALCount
#define g_DMALPage                        g_SBInfo.DMAInfo.DMALPage
#define g_DMAH                                g_SBInfo.IOInfo.DMAH
#define g_DMAHAddr                        g_SBInfo.DMAInfo.DMAHAddr
#define g_DMAHCount                        g_SBInfo.DMAInfo.DMAHCount
#define g_DMAHPage                        g_SBInfo.DMAInfo.DMAHPage
#define g_IRQ                                g_SBInfo.IOInfo.IRQ
#define g_BufAddr                        g_SBInfo.pBufAddr
#define g_BufSize                        g_SBInfo.BufSize
#define g_bPlaying8                        g_SBInfo.bPlaying8
#define g_bPlaying16                g_SBInfo.bPlaying16
#define g_bRecording8                g_SBInfo.bRecording8
#define g_bRecording16                g_SBInfo.bRecording16
#define g_bFirstBuf                        g_SBInfo.bFirstBuf
#define g_OnBuffer                        g_SBInfo.pfnOnBuffer
#define g_Print                                g_SBInfo.pfnOnDebugOutput

#define Far2Phys(f) (((uint32_t)f & 0xFFFF) + ((uint32_t)f >> 12))

#define SBPort_BaseBegin        0x210
#define SBPort_BaseStep                0x10
#define SBPort_BaseEnd                0x260

#define SBPort_MixerRegNum        0x04
#define SBPort_MixerRegVal        0x05
#define SBPort_Reset                0x06
#define SBPort_Read                        0x0A
#define SBPort_Write                0x0C
#define SBPort_WBufState        0x0C
#define SBPort_RBufState        0x0E
#define SBPort_Ack8                        0x0E
#define SBPort_Ack16                0x0F

#define SBMixer_VoiceVol        0x04
#define SBMixer_MicVol                0x0A
#define SBMixer_MasterVol        0x22

//Programmable Interrupt Controller
#define        PIC_MASK                        0x21
#define        PIC_MODE                        0x20
#define        PIC2_MASK                        0xA1
#define        PIC2_MODE                        0xA0

#define        PIC_EOI                                0x20
#define        PIC_SPECEOI                        0x60

#define        PIC_IRQ0_7BEGIN                0x08
#define        PIC_IRQ8_FBEGIN                0x70

#define        PIC_INTNUM(x)                ((x>7)?PIC_IRQ8_FBEGIN+(x):PIC_IRQ0_7BEGIN+(x))

//DMA
#define DMA8_FF_REG                        0x0C
#define DMA8_MASK_REG                0x0A
#define DMA8_MODE_REG                0x0B

#define DMA16_FF_REG                0xD8
#define DMA16_MASK_REG                0xD4
#define DMA16_MODE_REG                0xD6

#define DMA0_ADDR                        0x00
#define DMA0_COUNT                        0x01
#define DMA0_PAGE                        0x87

#define DMA1_ADDR                        0x02
#define DMA1_COUNT                        0x03
#define DMA1_PAGE                        0x83

#define DMA2_ADDR                        0x04
#define DMA2_COUNT                        0x05
#define DMA2_PAGE                        0x81

#define DMA3_ADDR                        0x06
#define DMA3_COUNT                        0x07
#define DMA3_PAGE                        0x82

#define DMA4_ADDR                        0xC0
#define DMA4_COUNT                        0xC2
#define DMA4_PAGE                        0x8F

#define DMA5_ADDR                        0xC4
#define DMA5_COUNT                        0xC6
#define DMA5_PAGE                        0x8B

#define DMA6_ADDR                        0xC8
#define DMA6_COUNT                        0xCA
#define DMA6_PAGE                        0x89

#define DMA7_ADDR                        0xCC
#define DMA7_COUNT                        0xCE
#define DMA7_PAGE                        0x8A

//=============================================================================
//Func: OnDebugOutput
//Desc: Debug info output by Sound Blaster driver
//-----------------------------------------------------------------------------
void SBCallBack_t DefOnDebugOutput(char*format,...)
{
        va_list ap;
        va_start(ap,format);
        vfprintf(stderr,format,ap);
        va_end(ap);
}

//=============================================================================
//Func: SBIn
//Desc: Read a byte at the Read Data port
//-----------------------------------------------------------------------------
SB_i(uint8_t,SBIn)(uint16_t uPortNum)
{
        while(!(inp(uPortNum+SBPort_RBufState) & 0x80));
        return inp(uPortNum+SBPort_Read);
}

//=============================================================================
//Func: SBOut
//Desc: Write a byte to the Write Data port
//-----------------------------------------------------------------------------
SB_i(void,SBOut)(uint16_t uPortNum,uint8_t Data)
{
        while(inp(uPortNum+SBPort_WBufState) & 0x80);
        outp(uPortNum+SBPort_Write,Data);
}

//=============================================================================
//Func: SBGetMixerReg
//Desc: Read a mixer register value
//-----------------------------------------------------------------------------
SB_i(uint8_t,SBGetMixerReg)(uint16_t uPortNum,uint8_t uMixerReg)
{
        if(g_Version<SBVer_SBPro)
                return 0;
        outp(uPortNum+SBPort_MixerRegNum,uMixerReg);
        return inp(uPortNum+SBPort_MixerRegVal);
}

//=============================================================================
//Func: SBSetMixerReg
//Desc: Write a mixer register value
//-----------------------------------------------------------------------------
SB_i(void,SBSetMixerReg)(uint16_t uPortNum,uint8_t uMixerReg,uint8_t uVal)
{
        if(g_Version<SBVer_SBPro)
                return;
        outp(uPortNum+SBPort_MixerRegNum,uMixerReg);
        outp(uPortNum+SBPort_MixerRegVal,uVal);
}

//=============================================================================
//Func: SBReset
//Desc: Initialize or reset the sound blaster
//-----------------------------------------------------------------------------
SB_i(bool_t,SBReset)(uint16_t uPortNum)
{
        clock_t c;
       
        //Write a "1" to the Reset port (2x6h) and wait for atleast 3 ms
        outp(uPortNum+SBPort_Reset,1);
       
        //wait for 10 ms
        delay(10);
       
        //Write a "0" to the Reset port
        outp(uPortNum+SBPort_Reset,0);
       
        //Wait until it had finished initializing
        c=clock();
        while(!(inp(uPortNum+SBPort_RBufState) & 0x80))
        {
                //quit when out of time
                if(clock()-c>CLOCKS_PER_SEC*200/1000)
                        return false;
        }
       
        if(inp(uPortNum+SBPort_Read)==0xAA)
                return true;
        return false;
}

//=============================================================================
//Func: SBGetIOInfoByEnv
//Desc: Retrieve the informations from %BLASTER%
//-----------------------------------------------------------------------------
SB_i(bool_t,SBGetIOInfoByEnv)(SBIOInfo_p pIOInfo,char*szEnv)
{
        char Buf,SaveChar;
        bool_t DMAFound = false;
        bool_t IOPortFound = false;
        bool_t IRQFound = false;
        int i,mul;
       
        if(!szEnv)
                return false;
       
        do
        {
                switch(*szEnv)
                {
                case'A':// I/O base port address found
                case'a':
                        szEnv++;
                        for (i = 0; i < 3; i++)// Grab the digits
                        {
                                if(*szEnv==' ')
                                        break;
                                Buf = *szEnv;
                                szEnv++;
                        }
                       
                        // Convert to HEX
                        mul = 1;
                        pIOInfo->PortNum = 0;
                        while(i--)
                        {
                                int Digit;
                               
                                if (Buf >= '0' && Buf <= '9')
                                        Digit = Buf - '0';
                                else if (Buf >= 'A' && Buf <= 'F')
                                        Digit = Buf - 'A' + 10;
                                else if (Buf >= 'a' && Buf <= 'f')
                                        Digit = Buf - 'a' + 10;

                                pIOInfo->PortNum += Digit * mul;
                                mul <<= 4;
                        }
                        IOPortFound=true;
                        break;
                       
                case'D': // 8-bit DMA channel
                case'd':
                case'H': // 16-bit DMA channel
                case'h':
                case'I': // IRQ number
                case'i':
                        SaveChar=*szEnv++;
                        Buf=*szEnv++;
                        if(isdigit(*szEnv))
                        {
                                Buf=*szEnv++;
                                Buf='\0';
                        }
                        else
                                Buf='\0';
                        if(SaveChar=='D' || SaveChar=='d')
                        {
                                pIOInfo->DMAL = atoi(Buf);
                                DMAFound=true;
                        }
                        else if(SaveChar=='H' || SaveChar=='h')
                        {
                                pIOInfo->DMAH = atoi(Buf);
                                DMAFound=true;
                        }
                        else if(SaveChar=='I' || SaveChar=='i')
                        {
                                pIOInfo->IRQ = atoi(Buf);
                                IRQFound=true;
                        }
                        break;
                       
                default:
                        szEnv++;
                }
        }while(*szEnv);
       
        if(!IOPortFound || !DMAFound || !IRQFound)
        {
                g_Print("Incorrect %%BLASTER%% env.\n");
                return false;
        }
        return true;
}

//=============================================================================
//Func: SBGetHWInfo
//Desc: Retrieve the hardware informations.
//-----------------------------------------------------------------------------
SB_i(void,SBGetHWInfo)(uint16_t uPortNum,SBHWInfo_p pHWInfo)
{
        SBOut(uPortNum,0xE1);//get version
        pHWInfo->Version=SBIn(uPortNum);
        pHWInfo->Version<<=8;
        pHWInfo->Version|=SBIn(uPortNum);
}

//=============================================================================
//Func: SBGetInfo
//Desc: Retrieve the informations by IO.
//Note: If failed getting info from BLASTER, the function will try to get the
//      info by read it from the mixer register. But only SB16 supports it.
//-----------------------------------------------------------------------------
SB_i(bool_t,SBGetInfo)(SBIOInfo_p pIOInfo,SBHWInfo_p pHWInfo)
{
        if(SBGetIOInfoByEnv(pIOInfo,getenv("BLASTER")))
        {
                pIOInfo->InfoFromEnv=true;
                SBGetHWInfo(pIOInfo->PortNum,pHWInfo);
                return true;
        }
        else
        {
                uint16_t        Port;
                uint8_t                IRQBits;
                uint8_t                DMABits;
               
                pIOInfo->InfoFromEnv=false;
               
                //Scan the port to determine the port of the sound blaster
                for(Port=SBPort_BaseBegin;
                        Port<=SBPort_BaseEnd;
                        Port+=SBPort_BaseStep)
                {
                        if(!SBReset(Port))
                                continue;
                       
                        pIOInfo->PortNum=Port;
                       
                        SBGetHWInfo(Port,pHWInfo);
                       
                        if(pHWInfo->Version<SBVer_SB16)
                        {
                                pIOInfo->IRQ=5;
                                pIOInfo->DMAL=1;
                                pIOInfo->DMAH=5;
                                return true;
                        }
                       
                        //Read the mixer registers
                        IRQBits=SBGetMixerReg(Port,0x80);
                        DMABits=SBGetMixerReg(Port,0x81);
                       
                        //Get IRQ number
                        switch(IRQBits & 0xF)
                        {
                        case 1:
                                pIOInfo->IRQ=2;
                                break;
                        case 2:
                                pIOInfo->IRQ=5;
                                break;
                        case 4:
                                pIOInfo->IRQ=7;
                                break;
                        case 8:
                                pIOInfo->IRQ=10;
                                break;
                        default:
                                continue;
                        }
                       
                        //Get 8-bit DMA channel
                        switch(DMABits & 0xF)
                        {
                        case 1:
                                pIOInfo->DMAL=0;
                                break;
                        case 2:
                                pIOInfo->DMAL=1;
                                break;
                        case 8:
                                pIOInfo->DMAL=3;
                                break;
                        default:
                                continue;
                        }
                       
                        //Get 16-bit DMA channel
                        switch(DMABits & 0xF0)
                        {
                        case 0x20:
                                pIOInfo->DMAH=5;
                                break;
                        case 0x40:
                                pIOInfo->DMAH=6;
                                break;
                        case 0x80:
                                pIOInfo->DMAH=7;
                                break;
                        default:
                                continue;
                        }
                       
                        return true;
                }
               
                g_Print("Sound Blaster not found.\n");
                return false;
        }
}

//=============================================================================
//Func: _SendEOI
//Desc: Send end-of-interrupt to 8259
//-----------------------------------------------------------------------------
SB_i(void,_SendEOI)(uint8_t uIRQNum)
{
        if(uIRQNum<8)
                outp(PIC_MODE,PIC_SPECEOI|uIRQNum);
        else
                outp(PIC2_MODE,PIC_SPECEOI|(uIRQNum-8));
}

//=============================================================================
//Func: _SetPICMask
//Desc: Set PIC mask bit for uIRQNum
//-----------------------------------------------------------------------------
SB_i(void,_SetPICMask)(uint8_t uIRQNum)
{
        if(uIRQNum<8)
                outp(PIC_MASK,inp(PIC_MASK)|(1<<uIRQNum));
        else
                outp(PIC2_MASK,inp(PIC_MASK)|(1<<(uIRQNum-8)));
}

//=============================================================================
//Func: _ClearPICMask
//Desc: Clear PIC mask bit for uIRQNum
//-----------------------------------------------------------------------------
SB_i(void,_ClearPICMask)(uint8_t uIRQNum)
{
        if(uIRQNum<8)
                outp(PIC_MASK,inp(PIC_MASK)&~(1<<uIRQNum));
        else
                outp(PIC2_MASK,inp(PIC_MASK)&~(1<<(uIRQNum-8)));
}

//=============================================================================
//Func: _CallBufCB
//Desc: Call the user specified callback function
//-----------------------------------------------------------------------------
SB_i(void,_CallBufCB)()
{
        uint8_t far*pPtr=g_BufAddr;
        if(!g_bFirstBuf)
        {
                g_bFirstBuf=true;
                g_OnBuffer(pPtr,g_BufSize/2);
        }
        else
        {
                pPtr+=g_BufSize/2;
                g_bFirstBuf=false;
                g_OnBuffer(pPtr,g_BufSize/2);
        }
}

//=============================================================================
//Func: _SBISR
//Desc: Sound blaster interrupt handler.
//-----------------------------------------------------------------------------
SB_ISR(_SBISR)(void)
{
        uint8_t uReason;
       
        if(g_Version>=SBVer_SB16)
        {
                uReason=SBGetMixerReg(g_Port,0x82);
               
                if(uReason & 0x01)
                {
                        inp(g_Port+SBPort_Ack8);
                        outp(DMA8_FF_REG,0);
                        _CallBufCB();
                }
                if(uReason & 0x02)
                {
                        inp(g_Port+SBPort_Ack16);
                        outp(DMA16_FF_REG,0);
                        _CallBufCB();
                }
        }
        else
        {
                inp(g_Port+SBPort_Ack8);
                outp(DMA8_FF_REG,0);
        }
       
        _SendEOI(g_IRQ);
        g_SBInfo.pfnISRSave();
}

//=============================================================================
//Func: _InstallISR
//Desc: Install the interrupt service routine
//-----------------------------------------------------------------------------
SB_i(void,_InstallISR)()
{
        ISR_f OrgISR;
       
        OrgISR=_dos_getvect(PIC_INTNUM(g_IRQ));
        if(OrgISR!=_SBISR)
        {
                g_SBInfo.pfnISRSave=OrgISR;
                _dos_setvect(PIC_INTNUM(g_IRQ),_SBISR);
               
                _ClearPICMask(g_IRQ);
        }
}

//=============================================================================
//Func: _RestoreISR
//Desc: Restore the interrupt service routine to the old one.
//-----------------------------------------------------------------------------
SB_i(void,_RestoreISR)()
{
        _dos_setvect(PIC_INTNUM(g_IRQ),g_SBInfo.pfnISRSave);
}

//=============================================================================
//Func: SBInit
//Desc: Initialize sound blaster.
//-----------------------------------------------------------------------------
SB_f(bool_t,SBInit)(DbgOut_f pfnOnDebugOutput)
{
        if(pfnOnDebugOutput)
                g_Print=pfnOnDebugOutput;
        else
                g_Print=DefOnDebugOutput;
       
        if(!SBGetInfo(&(g_SBInfo.IOInfo),&(g_SBInfo.HWInfo)))
                return false;
       
        switch(g_DMAL)
        {
        case 0:
                g_DMALAddr= DMA0_ADDR;
                g_DMALCount=DMA0_COUNT;
                g_DMALPage= DMA0_PAGE;
                break;
        case 1:
                g_DMALAddr= DMA1_ADDR;
                g_DMALCount=DMA1_COUNT;
                g_DMALPage= DMA1_PAGE;
                break;
        case 3:
                g_DMALAddr= DMA3_ADDR;
                g_DMALCount=DMA3_COUNT;
                g_DMALPage= DMA3_PAGE;
                break;
        }
        switch(g_DMAH)
        {
        case 5:
                g_DMAHAddr= DMA5_ADDR;
                g_DMAHCount=DMA5_COUNT;
                g_DMAHPage= DMA5_PAGE;
                break;
        case 6:
                g_DMAHAddr= DMA6_ADDR;
                g_DMAHCount=DMA6_COUNT;
                g_DMAHPage= DMA6_PAGE;
                break;
        case 7:
                g_DMAHAddr= DMA7_ADDR;
                g_DMAHCount=DMA7_COUNT;
                g_DMAHPage= DMA7_PAGE;
                break;
        }
       
        _InstallISR();

        return true;
}

//=============================================================================
//Func: _CheckDMAAddr
//Desc: If the buffer crosses a page boundary, the func. returns false.
//-----------------------------------------------------------------------------
SB_i(bool_t,_CheckDMAAddr)(void far*Base,uint16_t Count)
{
        if((((uint32_t)Base & 0xFFFF)+Count) >> 16)
                return false;
       
        return true;
}

//=============================================================================
//Func: _StopDMAL
//Desc: Stop DMA
//-----------------------------------------------------------------------------
SB_i(void,_StopDMAL)()
{
        outp(DMA8_MASK_REG,0x04 | g_DMAL);
}

//=============================================================================
//Func: _StopDMAH
//Desc: Stop DMA
//-----------------------------------------------------------------------------
SB_i(void,_StopDMAH)()
{
        outp(DMA16_MASK_REG,0x04 | (g_DMAH-4));
}

//=============================================================================
//Func: _StopDMAL
//Desc: Stop DMA
//-----------------------------------------------------------------------------
SB_i(void,_StartDMAL)()
{
        outp(DMA8_MASK_REG, g_DMAL);
}

//=============================================================================
//Func: _StopDMAH
//Desc: Stop DMA
//-----------------------------------------------------------------------------
SB_i(void,_StartDMAH)()
{
        outp(DMA16_MASK_REG, (g_DMAH-4));
}

//=============================================================================
//Func: _SetupDMAL
//Desc: Setup DMA for playing
//-----------------------------------------------------------------------------
SB_i(void,_SetupDMAL)(void far*Base,uint16_t Count,bool_t Input)
{
        uint32_t uPhysAddr=Far2Phys(Base);
        uint16_t uOffs=uPhysAddr & 0xFFFF;
        uint8_t uPage=uPhysAddr>>16;
       
        //Disable DMA
        _StopDMAL();
       
        //Clear FF
        outp(DMA8_FF_REG,0);
       
        //Set mode
        if(Input)
                outp(DMA8_MODE_REG,0x54 | g_DMAL);
        else
                outp(DMA8_MODE_REG,0x58 | g_DMAL);
       
        Count--;
        outp(g_DMALCount,(Count & 0xFF));                // LO byte of count
        outp(g_DMALCount,(Count >> 8));                        // HI byte of count
       
        outp(g_DMALAddr, uOffs & 0xFF);                // LO byte address of buffer
        outp(g_DMALAddr, uOffs >> 8);                // HI byte address of buffer
        outp(g_DMALPage, uPage);                        // Physical page number
       
        //Enable DMA
        _StartDMAL();
}

//=============================================================================
//Func: _SetupDMAH
//Desc: Setup DMA for playing
//-----------------------------------------------------------------------------
SB_i(void,_SetupDMAH)(void far*Base,uint16_t Count,bool_t Input)
{
        uint32_t uPhysAddr=Far2Phys(Base);
        uint16_t uOffs=(uPhysAddr>>1) & 0xFFFF;
        uint8_t uPage=uPhysAddr>>16;
       
        //Disable DMA
        _StopDMAH();
       
        //Clear FF
        outp(DMA16_FF_REG,0);
       
        //Set mode
        if(Input)
                outp(DMA16_MODE_REG,0x54 | (g_DMAH-4));
        else
                outp(DMA16_MODE_REG,0x58 | (g_DMAH-4));

        Count=Count/2-1;//Two bytes per transfer
        outp(g_DMAHCount,(Count & 0xFF));        // LO byte of count
        outp(g_DMAHCount,(Count >> 8));                // HI byte of count
       
        outp(g_DMAHAddr, uOffs & 0xFF);                // LO byte address of buffer
        outp(g_DMAHAddr, uOffs >> 8);                // HI byte address of buffer
        outp(g_DMAHPage, uPage);                        // Physical page number
       
        //Enable DMA
        _StartDMAH();
}
//=============================================================================
//Func: SBShutDown
//Desc: Shutdown the sound blaster, stop playing, restore IRQ
//-----------------------------------------------------------------------------
SB_f(void,SBShutDown)()
{
        if(!g_Version)
                return;
        SBStop();
        SBReset(g_Port);
        _SetPICMask(g_IRQ);
        _RestoreISR();
        memset(&g_SBInfo,0,sizeof(g_SBInfo));
}

//=============================================================================
//Func: SBDirectOut8
//Desc: 8-bit direct mode single byte digitized sound output
//-----------------------------------------------------------------------------
SB_f(bool_t,SBDirectOut8)(uint8_t uByte)
{
        SBOut(g_Port,0x10);
        SBOut(g_Port,uByte);
        return true;
}

//=============================================================================
//Func: SBInByte8
//Desc: 8-bit direct mode single byte digitized sound input
//-----------------------------------------------------------------------------
SB_f(bool_t,SBDirectIn8)(uint8_t*pByte)
{
        SBOut(g_Port,0x20);
        *pByte=SBIn(g_Port);
        return true;
}

//=============================================================================
//Func: _SBSetTimeConst
//Desc: set digitized sound output sampling rate
//-----------------------------------------------------------------------------
SB_i(bool_t,_SBSetTimeConst)(uint32_t SplRate,uint8_t uChannels)
{
        if(SplRate<4000)
        {
                g_Print("The sampling rate is too low.\n");
                return false;
        }
       
        if(g_Version<SBVer_SBLO)
        {
                if(SplRate>13000)
                {
                        g_Print("The sampling rate is too high for the current Sound Blast"
                        "er.\n");
                        return false;
                }
        }
       
        if(g_Version<SBVer_SBPro)
        {
                if(uChannels!=1)
                {
                        g_Print("The current Sound Blaster doesn't support stereo digitize"
                        "d sound data.\n");
                        return false;
                }
        }
       
        if(SplRate>44100)
        {
                g_Print("The sampling rate is too high for the current Sound Blaster."
                "\n");
                return false;
        }
       
        if(uChannels!=1 && uChannels!=2)
        {
                g_Print("Invalid channel number.\n");
                return false;
        }
       
        SBOut(g_Port,0x40);
        SBOut(g_Port,256 - 1000000/(uChannels*SplRate));
        return true;
}

//=============================================================================
//Func: SBSetOutSplRate
//Desc: set digitized sound output sampling rate
//-----------------------------------------------------------------------------
SB_f(bool_t,SBSetOutSplRate)(uint32_t SplRate,uint8_t uChannels)
{
        if(g_Version<SBVer_SB16)
                return _SBSetTimeConst(SplRate,uChannels);
        else
        {
                if(SplRate<5000)
                        return _SBSetTimeConst(SplRate,uChannels);
                else if(SplRate>45000)
                {
                        g_Print("The sampling rate is too high.\n");
                        return false;
                }
               
                SBOut(g_Port,0x41);
                SBOut(g_Port,SplRate >> 8);
                SBOut(g_Port,SplRate & 0xFF);
                return true;
        }
}

//=============================================================================
//Func: SBSetInSplRate
//Desc: set digitized sound input sampling rate
//-----------------------------------------------------------------------------
SB_f(bool_t,SBSetInSplRate)(uint32_t SplRate,uint8_t uChannels)
{
        if(g_Version<SBVer_SB16)
                return _SBSetTimeConst(SplRate,uChannels);
        else
        {
                if(SplRate<5000)
                        return _SBSetTimeConst(SplRate,uChannels);
                else if(SplRate>45000)
                {
                        g_Print("The sampling rate is too high.\n");
                        return false;
                }
               
                SBOut(g_Port,0x42);
                SBOut(g_Port,SplRate >> 8);
                SBOut(g_Port,SplRate & 0xFF);
                return true;
        }
}

//=============================================================================
//Func: _SBSetBlockSize
//Desc: Set DSP block transfer size
//-----------------------------------------------------------------------------
SB_i(bool_t,_SBSetBlockSize)(uint16_t BlockSize)
{
        if(g_Version<SBVer_SBLO)
        {
                g_Print("The current sound blaster doesn't support auto-init mode.\n");
                return false;
        }
               
        SBOut(g_Port,0x48);
        SBOut(g_Port,BlockSize & 0xFF);
        SBOut(g_Port,BlockSize >> 8);
        return true;
}

//=============================================================================
//Func: _Stop8
//Desc: Pause 8-bit digitized sound ip
//-----------------------------------------------------------------------------
SB_i(bool_t,_Stop8)()
{
        SBOut(g_Port,0xD0);
        _StopDMAL();
        return true;
}

//=============================================================================
//Func: _Start8
//Desc: Continue 8-bit digitized sound ip
//-----------------------------------------------------------------------------
SB_i(bool_t,_Start8)()
{
        SBOut(g_Port,0xD4);
        _StartDMAL();
        return true;
}

//=============================================================================
//Func: _Stop16
//Desc: Pause 16-bit digitized sound ip
//-----------------------------------------------------------------------------
SB_i(bool_t,_Stop16)()
{
        if(g_Version<SBVer_SB16)
        {
                g_Print("The current sound blaster doesn't support 16-bit digitized so"
                "und data.\n");
                return false;
        }
        SBOut(g_Port,0xD5);
        _StopDMAH();
        return true;
}

//=============================================================================
//Func: _Start16
//Desc: Continue 16-bit digitized sound ip
//-----------------------------------------------------------------------------
SB_i(bool_t,_Start16)()
{
        if(g_Version<SBVer_SB16)
        {
                g_Print("The current sound blaster doesn't support 16-bit digitized so"
                "und data.\n");
                return false;
        }
        SBOut(g_Port,0xD6);
        _StartDMAH();
        return true;
}

//=============================================================================
//Func: SBStop
//Desc: Stop(Pause) digitized sound ip
//-----------------------------------------------------------------------------
SB_f(void,SBStop)()
{
        if(g_bPlaying8 || g_bRecording8)
                _Stop8();
        if(g_bPlaying16 || g_bRecording16)
                _Stop16();
}

//=============================================================================
//Func: SBStart
//Desc: Start(Continue) digitized sound ip
//-----------------------------------------------------------------------------
SB_f(void,SBStart)()
{
        if(g_bPlaying8 || g_bRecording8)
                _Start8();
        if(g_bPlaying16 || g_bRecording16)
                _Start16();
}

//=============================================================================
//Func: SBOut8
//Desc: 8-bit auto-init mode digitized sound output
//-----------------------------------------------------------------------------
SB_f(bool_t,SBOut8)
(
        void far*BufAddr,
        uint16_t BufSize,
        uint8_t uChannels,
        uint32_t SplRate,
        Buffer_f pfnOnBuffer
)
{
        //Double buffering
        uint16_t HalfSize;
       
        if(g_Version<SBVer_SBLO)//no auto-init
        {
                g_Print("The current Sound Blaster doesn't support auto-init mode.\n");
                return false;
        }
       
        if(g_Version<SBVer_SB16 & uChannels!=1)//no stereo
        {
                g_Print("The current Sound Blaster doesn't support stereo sound data."
                "\n");
                return false;
        }
               
        if(!_CheckDMAAddr(BufAddr,BufSize))
        {
                g_Print("DMA segbound wrapping.\n");
                return false;
        }
       
        if(!SBSetOutSplRate(SplRate,uChannels))
                return false;
       
        SBStop();
       
        g_SBInfo.pfnOnBuffer=pfnOnBuffer;
        g_BufAddr=BufAddr;
        g_BufSize=BufSize;
       
        g_bFirstBuf=false;
        _CallBufCB();
        _CallBufCB();
       
        _SetupDMAL(BufAddr,BufSize-uChannels,0);
       
        //Double buffering
        HalfSize=BufSize/2-uChannels;
       
        if(uChannels==1)
        {
                if(g_Version<SBVer_SB16)
                {
                        _SBSetBlockSize(HalfSize);
                        if(SplRate<=23000)//low speed
                                SBOut(g_Port,0x1C);
                        else
                        {
                                if(g_Version<SBVer_SBHI)
                                        return false;
                               
                                SBOut(g_Port,0x90);
                        }
                }
                else
                {
                        SBOut(g_Port,0xC4);//Output, auto-init, no fifo
                        SBOut(g_Port,0x00);//mono, unsigned
                        SBOut(g_Port,HalfSize & 0xFF);
                        SBOut(g_Port,HalfSize >> 8);
                }
        }
        else if(uChannels==2)
        {
                SBOut(g_Port,0xC4);//Output, auto-init, no fifo
                SBOut(g_Port,0x20);//stereo, unsigned
                SBOut(g_Port,HalfSize & 0xFF);
                SBOut(g_Port,HalfSize >> 8);
        }
        else
                return false;
       
        g_bPlaying8=true;
        g_bPlaying16=false;
        g_bRecording8=false;
        g_bRecording16=false;
        return true;
}

//=============================================================================
//Func: SBIn8
//Desc: 8-bit auto-init mode digitized sound input
//-----------------------------------------------------------------------------
SB_f(bool_t,SBIn8)
(
        void far*BufAddr,
        uint16_t BufSize,
        uint8_t uChannels,
        uint32_t SplRate,
        Buffer_f pfnOnBuffer
)
{
        if(g_Version<SBVer_SBLO)//no auto-init
        {
                g_Print("The current Sound Blaster doesn't support auto-init mode.\n");
                return false;
        }
       
        if(g_Version<SBVer_SB16 & uChannels!=1)//no stereo
        {
                g_Print("The current Sound Blaster doesn't support stereo sound data."
                "\n");
                return false;
        }
               
        if(!_CheckDMAAddr(BufAddr,BufSize))
        {
                g_Print("DMA segbound wrapping.\n");
                return false;
        }
       
        if(!SBSetInSplRate(SplRate,uChannels))
                return false;
       
        SBStop();
       
        g_SBInfo.pfnOnBuffer=pfnOnBuffer;
        g_BufAddr=BufAddr;
        g_BufSize=BufSize;
       
        g_bFirstBuf=false;
       
        //Double buffering
        _SetupDMAL(BufAddr,BufSize-uChannels,1);
       
        //Double buffering
        BufSize=BufSize/2-uChannels;
       
        if(uChannels==1)
        {
                if(g_Version<SBVer_SB16)
                {
                        _SBSetBlockSize(BufSize);//Double buffer
                        if(SplRate<=23000)//low speed
                                SBOut(g_Port,0x2C);
                        else
                        {
                                if(g_Version<SBVer_SBHI)
                                        return false;
                               
                                SBOut(g_Port,0x98);
                        }
                }
                else
                {
                        SBOut(g_Port,0xCC);//Input, auto-init, no fifo
                        SBOut(g_Port,0x00);//mono, unsigned
                        SBOut(g_Port,BufSize & 0xFF);
                        SBOut(g_Port,BufSize >> 8);
                }
        }
        else if(uChannels==2)
        {
                SBOut(g_Port,0xCC);//Input, auto-init, no fifo
                SBOut(g_Port,0x20);//stereo, unsigned
                SBOut(g_Port,BufSize & 0xFF);
                SBOut(g_Port,BufSize >> 8);
        }
        else
                return false;
       
        g_bPlaying8=false;
        g_bPlaying16=false;
        g_bRecording8=true;
        g_bRecording16=false;
        return true;
}

//=============================================================================
//Func: SBOut16
//Desc: 16-bit auto-init mode digitized sound output
//-----------------------------------------------------------------------------
SB_f(bool_t,SBOut16)
(
        void far*BufAddr,
        uint16_t BufSize,
        uint8_t uChannels,
        uint32_t SplRate,
        Buffer_f pfnOnBuffer
)
{       
        //Double buffering
        uint16_t HalfSize;
       
        if(g_Version<SBVer_SB16)
        {
                g_Print("The current sound blaster doesn't support 16-bit digitized so"
                "und data.\n");
                return false;
        }
       
        if(uChannels!=1 && uChannels!=2)
        {
                g_Print("Invalid channel number.\n");
                return false;
        }
       
        if(!_CheckDMAAddr(BufAddr,BufSize))
        {
                g_Print("DMA segbound wrapping.\n");
                return false;
        }
       
        if(!SBSetOutSplRate(SplRate,uChannels))
                return false;
       
        SBStop();
       
        g_SBInfo.pfnOnBuffer=pfnOnBuffer;
        g_BufAddr=BufAddr;
        g_BufSize=BufSize;
       
        g_bFirstBuf=false;
        _CallBufCB();
        _CallBufCB();
       
        _SetupDMAH(BufAddr,BufSize,0);
       
        //Two bytes per sample, double buffering
        HalfSize=BufSize/4-1;
       
        SBOut(g_Port,0xB4);//Output, auto-init, no fifo
        SBOut(g_Port,uChannels==1?0x10:0x30);//stereo, unsigned
        SBOut(g_Port,HalfSize & 0xFF);
        SBOut(g_Port,HalfSize >> 8);
       
        g_bPlaying8=false;
        g_bPlaying16=true;
        g_bRecording8=false;
        g_bRecording16=false;
        return true;
}

//=============================================================================
//Func: SBIn16
//Desc: 16-bit auto-init mode digitized sound input
//-----------------------------------------------------------------------------
SB_f(bool_t,SBIn16)
(
        void far*BufAddr,
        uint16_t BufSize,
        uint8_t uChannels,
        uint32_t SplRate,
        Buffer_f pfnOnBuffer
)
{
        if(g_Version<SBVer_SB16)
        {
                g_Print("The current sound blaster doesn't support 16-bit digitized so"
                "und data.\n");
                return false;
        }
       
        if(uChannels!=1 && uChannels!=2)
        {
                g_Print("Invalid channel number.\n");
                return false;
        }
       
        if(!_CheckDMAAddr(BufAddr,BufSize))
        {
                g_Print("DMA segbound wrapping.\n");
                return false;
        }
       
        if(!SBSetInSplRate(SplRate,uChannels))
                return false;
       
        SBStop();
       
        g_SBInfo.pfnOnBuffer=pfnOnBuffer;
        g_BufAddr=BufAddr;
        g_BufSize=BufSize;
       
        g_bFirstBuf=false;
        _SetupDMAH(BufAddr,BufSize,1);
       
        //Two bytes per sample, double buffering
        BufSize=BufSize/4-1;
       
        SBOut(g_Port,0xBC);//Input, auto-init, no fifo
        SBOut(g_Port,uChannels==1?0x10:0x30);//stereo, unsigned
        SBOut(g_Port,BufSize & 0xFF);
        SBOut(g_Port,BufSize >> 8);
       
        g_bPlaying8=false;
        g_bPlaying16=false;
        g_bRecording8=false;
        g_bRecording16=true;
        return true;
}

//=============================================================================
//Func: SBSetVolume
//Desc: Set speaker volume
//-----------------------------------------------------------------------------
SB_f(bool_t,SBSetVolume)(uint8_t Master,uint8_t Voice,uint8_t Mic)
{
        if(g_Version<SBVer_SBPro)
        {
                g_Print("The current sound blaster doesn't have a mixer.\n");
                return false;
        }
        SBSetMixerReg(g_Port,SBMixer_MasterVol,Master);
        SBSetMixerReg(g_Port,SBMixer_VoiceVol,Voice);
        SBSetMixerReg(g_Port,SBMixer_MicVol,Mic);
        return true;
}

//=============================================================================
//Func: SBSendMIDIByte
//Desc: Output MIDI data to the MIDI port in non-UART mode
//-----------------------------------------------------------------------------
SB_f(bool_t,SBSendMIDIByte)(uint8_t MIDI_Byte)
{
        SBOut(g_Port,0x38);
        SBOut(g_Port,MIDI_Byte);
        return true;
}

cyycoish 发表于 2015-9-16 12:25:57

1134Lines 不容易!

0xAA55 发表于 2015-9-19 18:15:14

cyycoish 发表于 2015-9-16 12:25
1134Lines 不容易!

已更新

langzhe 发表于 2017-11-12 20:46:42

666666666666666666666666

watermelon 发表于 2019-1-31 22:18:22

屌!一个C语言文件写的程序感觉好爽啊哈哈

snn_snn 发表于 2020-1-27 16:09:30

谢谢分享

矿洞菠菜 发表于 2020-2-2 08:24:25

感谢分享
页: [1]
查看完整版本: 【DOS】C语言写的声卡(Sound Blaster 16)驱动