返回
{***************************************************************}
{*** FVision Unit Version1.0 ***}
{*** 蓝蚂蚁工作室 ***}
{***************************************************************}
{*** 数字信号处理单元 ***}
{***************************************************************}
{*** DSP接口单元 ***}
{*** DSP端口: 2x6:初始化 ***}
{*** 2xa:读数据端口 ***}
{*** 2xc:写命令或数据/读状态 ***}
{*** 2xe:读数据有效状态 ***}
{***************************************************************}
{$F+,O+,X+,I-}
Unit FDsp;
Interface
Uses
Dos,Memory,FXmsDrv,FTool,FWrite,FGraph;
Const
DspFound:Boolean=False;
DspBasePort:Integer=$210;
DspVerHi:Byte=0;
DspVerLo:Byte=0;
SbIrq:Integer=5;
Const
MASTER_VOL = $22;
VOC_VOL = $04;
LINE_VOL = $2E;
FM_VOL = $26;
CD_VOL = $28;
RECORD_SRC = $0C;
MAX_LO_PLAY=22222;
MIDI_READ_POLL=$30;
MIDI_WRITE_POLL=$38;
DSP_READ_DATA=$0A;
DSP_WRITE_DATA=$0C;
DSP_WRITE_STATUS=$0C;
DSP_DATA_AVAIL=$0E;
Type
VolType=Record
Master:Byte;
Voice:Byte;
Music:Byte;
Line:Byte;
CD:Byte;
Mic:Byte;
end;
Type
WaveHead=Record
RiffSign:array [0..3] of Char;
FileSize:Longint;
WaveFmt:array [0..7] of Char;
Size1:Longint;
FmtTag:Integer;
Channel:Integer;
SamplesPerSec:Longint;
BytesPerSec:Longint;
BlockAlign:Integer;
BitsPerSample:Integer;
Flag:array [0..3] of Char;
end;
{ DmaBuffer=Record
Pb,Pe:Word;
Offb,Lenb,Offe,Lene:Word;
end;
}
Type
TWave=object
FileName:PathStr;
Header:WaveHead;
HighSpeed:Boolean;
DataSize:Longint;
Time_Constant:Word;
BlockSize:Word;
XmsHandle:Word;
Constructor Init(Name:PathStr);
Destructor Done;virtual;
Procedure LoadFromFile(Name:PathStr);
Procedure Halt;
Procedure Continue;
Procedure InitWave;
Procedure DoneWave;
Procedure DmacDac;
Procedure DmaWave;
Procedure Play;
Function Playing:Boolean;
end;
Procedure GetDspBasePort;
Procedure GetVol(Var Vol:VolType);
Procedure SetVol(Vol:VolType);
Procedure DspCommand(Com:Byte);
Procedure GetDspVer;
Procedure SpeakerOn;
Procedure SpeakerOff;
Function ReadMIDI:Byte;
Procedure WriteMidi(Data:Byte);
Procedure DmacDac;
Procedure DmaWave;
Procedure DmaPlay;
Procedure InitSbIrq;
Procedure DoneSbIrq;
Function InitWavFile(Name:PathStr):Boolean;
Procedure DoneWavFile;
Procedure HaltDmaWave;
Procedure ContinueDmaWave;
Implementation
Const
DMAComplete:Boolean=True;
Var
DMABuffer:Pointer;
BufSize:Word;
Var
OldSbHandle:Pointer;
WaveHeader:WaveHead;
HighSpeed:Boolean;
DataPoint:Pointer;
DataSize:Longint;
Time_Constant:Word;
WaveFile:File;
CurrentPage:Word;
DataOffset:Word;
DataLength:Word;
{获得DSP芯片的端口基地址}
Procedure GetDspBasePort;
var
Temp1,Temp2:Integer;
begin
DspBasePort:=$210;
DspFound:=False;
Temp1:=20;
while (DspBasePort<=$260) and (not DspFound) do
begin
Port[DspBasePort+6]:=1;
Port[DspBasePort+6]:=0;
Temp2:=100;
while (Temp2>0) and (Port[DspBasePort+$0E]<128) do
Dec(Temp2);
if (Temp2=0) or (Port[DspBasePort+$0A]<>$AA) then
begin
Dec(Temp1);
if Temp1=0 then
begin
Temp1:=20;
Inc(DspBasePort,$10);
end;
end
else
DspFound:=True;
end;
end;
Procedure GetVol(Var Vol:VolType);
begin
Port[DspBasePort+4]:=MASTER_VOL;
Vol.Master:=Port[DspBasePort+5];
Port[DspBasePort+4]:=VOC_VOL;
Vol.Voice:=Port[DspBasePort+5];
Port[DspBasePort+4]:=FM_VOL;
Vol.Music:=Port[DspBasePort+5];
Port[DspBasePort+4]:=LINE_VOL;
Vol.Line:=Port[DspBasePort+5];
Port[DspBasePort+4]:=CD_VOL;
Vol.CD:=Port[DspBasePort+5];
Port[DspBasePort+4]:=RECORD_SRC;
Vol.Mic:=Port[DspBasePort+5];
end;
Procedure SetVol(Vol:VolType);
begin
Port[DspBasePort+4]:=MASTER_VOL;
Port[DspBasePort+5]:=Vol.Master;
Port[DspBasePort+4]:=VOC_VOL;
Port[DspBasePort+5]:=Vol.Voice;
Port[DspBasePort+4]:=FM_VOL;
Port[DspBasePort+5]:=Vol.Music;
Port[DspBasePort+4]:=LINE_VOL;
Port[DspBasePort+5]:=Vol.Line;
Port[DspBasePort+4]:=CD_VOL;
Port[DspBasePort+5]:=Vol.CD;
Port[DspBasePort+4]:=RECORD_SRC;
Port[DspBasePort+5]:=Vol.Mic;
end;
{向DSP芯片发送命令}
Procedure DspCommand(Com:Byte);
begin
While (Port[DspBasePort+$0C] and $80)<>0 do
begin
end;
Port[DspBasePort+$0C]:=Com;
end;
{获得DSP芯片的版本号}
Procedure GetDspVer;
begin
if not DspFound then Exit;
DspCommand($E1);
while (Port[DspBasePort+$0E] and $80)=0 do
begin
end;
DspVerHi:=Port[DspBasePort+$0A];
while (Port[DspBasePort+$0E] and $80)=0 do
begin
end;
DspVerLo:=Port[DspBasePort+$0A];
end;
Procedure SpeakerOn;
begin
DspCommand($D1);
end;
Procedure SpeakerOff;
begin
DspCommand($D3);
end;
Function ReadMIDI:Byte;
begin
DspCommand(MIDI_READ_POLL);
while((Port[DSP_DATA_AVAIL] and $80)=0)do;
ReadMidi:=Port[DSP_READ_DATA];
end;
Procedure WriteMIDI(Data:Byte);
begin
DspCommand(MIDI_WRITE_POLL);
DspCommand(Data);
end;
Procedure SbHandle(Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP: Word);interrupt;
var
Temp:Byte;
begin
asm sti;
end;
DMAComplete:=True;
Temp:=Port[DspBasePort+$0E];
Port[$20]:=$20;
end;
{初始化DMA通道}
Procedure DmacDac;
begin
Port[$0A]:=5; {清除DMA通道1屏蔽}
Port[$0C]:=0; {清先后触发器}
Port[$0B]:=$49; {传输模式为DAC}
Port[$02]:=Lo(DataOffset); {发送基址LSB}
Port[$02]:=Hi(DataOffset); {发送基址MSB}
Port[$83]:=Lo(CurrentPage); {页面 号}
Port[$03]:=Lo(DataLength-1); {DMA数据个数LSB}
Port[$03]:=Hi(DataLength-1); {DMA数据个数MSB}
Port[$0A]:=1; {激活DMA通道1}
end;
{DMA方式播放一个片段}
Procedure DmaWave;
begin
DmacDac;
{ if HighSpeed then
begin
DspCommand($48);
DspCommand(Lo(DataLength-1));
DspCommand(Hi(DataLength-1));
DspCommand($91);
end else
}
begin
DspCommand($14);
DspCommand(Lo(DataLength-1));
DspCommand(Hi(DataLength-1));
end;
{ repeat Temp:=Port[$08];
until ((Temp and $20)=0) and ((Temp and $02)<>0);
}
repeat until DMAComplete;
end;
Procedure GetDmaPage(P:Pointer;Size:Word);
var
Segb,Offb,Abs:Longint;
begin
Segb:=Longint(Seg(P));
Offb:=Longint(Ofs(P));
Abs:=Segb shl 4 + Offb;
CurrentPage:=Word(Abs shr 16);
DataOffset:=Word(Abs){ and $FFFF)+11000};
DataLength:=Size;
writeshe(10,300,'Length: '+IntStr(DataLength),10,1);
writeshe(10,320,'Ofs: '+IntStr(DataOffset),10,1);
end;
Procedure DmaPlay;
var
Result:Word;
begin
DMAComplete:=False;
if DataSize>$FF00 then DataSize:=$FF00;
DataPoint:=MemAllocSeg(DataSize);
writeshe(10,260,'Seg: '+IntStr(Seg(DataPoint^)),10,1);
writeshe(10,280,'Ofs: '+IntStr(Ofs(DataPoint^)),10,1);
if DataPoint<>nil then
begin
BlockRead(WaveFile,DataPoint^,DataSize,Result);
GetDmaPage(DataPoint,DataSize);
DmaWave;
end;
end;
Procedure InitSbIrq;
var
Im:Byte;
begin
asm cli;
end;
GetIntVec($08+SBIrq,OldSbHandle);
SetIntVec($08+SbIrq,@SbHandle);
Im:=Port[$21];
Port[$21]:=Im and (not (1 Shl sbIRQ));
asm sti;
end;
end;
Procedure DoneSbIrq;
var
Im:Byte;
begin
asm cli;
end;
SetIntVec($08+SbIrq,OldSbHandle);
Im:=Port[$21];
Port[$21]:=Im or (1 Shl sbIRQ);
asm sti;
end;
end;
Procedure HaltDmaWave;
begin
DspCommand($D0);
end;
Procedure ContinueDmaWave;
begin
DspCommand($D4);
end;
Function InitWavFile(Name:PathStr):Boolean;
var
Result:Word;
Found:Boolean;
OffData,Off:LongInt;
Search:String;
begin
SpeakerOn;
InitWavFile:=False;
Assign(WaveFile,Name);
Reset(WaveFile,1);
if IOResult<>0 then Exit;
BlockRead(WaveFile,WaveHeader,Sizeof(WaveHead),Result);
if WaveHeader.WaveFmt<>'WAVEfmt ' then Exit;
if WaveHeader.Flag='data' then
BlockRead(WaveFile,DataSize,4,Result)
else
begin
Found:=False;
OffData:=39;
while (not Found) and (not eof(WaveFile)) do
begin
BlockRead(WaveFile,Search[1],128,Result);
Byte(Search[0]):=Result;
Off:=Pos('data',Search);
if Off>0 then
begin
Found:=True;
Inc(OffData,Off+4);
end else
Inc(OffData,Result);
end;
if Found then
begin
Seek(WaveFile,OffData);
BlockRead(WaveFile,DataSize,4,Result);
end else
Exit;
end;
HighSpeed:=WaveHeader.SamplesPerSec>MAX_LO_PLAY;
Time_Constant:=256-1000000 div (WaveHeader.SamplesPerSec*WaveHeader.Channel);
DspCommand($40); {Set Time Constant}
DspCommand(Time_Constant);
InitWavFile:=True;
Full(10,40,400,400,7);
writeshe(10,40,'RiffSign: '+WaveHeader.RiffSign,10,1);
writeshe(10,60,'FileSize: '+IntStr(WaveHeader.FileSize),10,1);
writeshe(10,80,'WaveFormat: '+WaveHeader.WaveFmt,10,1);
writeshe(10,100,'FmtTag: '+IntStr(WaveHeader.FmtTag),10,1);
writeshe(10,120,'Channel: '+IntStr(WaveHeader.Channel),10,1);
writeshe(10,140,'SamplesPerSec: '+IntStr(WaveHeader.SamplesPerSec),10,1);
writeshe(10,160,'BytesPerSec: '+IntStr(WaveHeader.BytesPerSec),10,1);
writeshe(10,180,'BlockAlign: '+IntStr(WaveHeader.BlockAlign),10,1);
writeshe(10,200,'BitsPerSample: '+IntStr(WaveHeader.BitsPerSample),10,1);
writeshe(10,220,'DataFlag: '+WaveHeader.Flag,10,1);
writeshe(10,240,'DataSize: '+IntStr(DataSize),10,1);
end;
Procedure DoneWavFile;
begin
Close(WaveFile);
if DataPoint<>nil then
FreeMem(DataPoint,DataSize);
SpeakerOff;
end;
Constructor TWave.Init;
begin
XmsHandle:=0;
LoadFromFile(Name);
end;
Destructor TWave.Done;
begin
if XmsHandle<>0 then FreeXms(XmsHandle);
end;
Procedure TWave.LoadFromFile;
var
Fp:File;
Result,Dlt:Word;
begin
Assign(Fp,Name);
Reset(Fp,1);
if IOResult<>0 then Exit;
BlockRead(Fp,Header,Sizeof(WaveHead),Result);
if Header.WaveFmt<>'WAVEfmt ' then Exit;
Dlt:=Sizeof(WaveHead)+4;
if WaveHeader.Flag='data' then
BlockRead(Fp,DataSize,4,Result)
else if WaveHeader.Flag='fact' then
begin
Inc(Dlt,12);
Seek(Fp,Sizeof(WaveHead)+12);
BlockRead(Fp,DataSize,4,Result);
end else Exit;
Close(Fp);
FileName:=Name;
if XmsHandle<>0 then FreeXms(XmsHandle);
XmsHandle:=MallocXms(DataSize div 1024 +1);
ReadFileToXms(Name,Dlt,DataSize,rxUser);
HighSpeed:=Header.SamplesPerSec>MAX_LO_PLAY;
Time_Constant:=256-1000000 div (WaveHeader.SamplesPerSec*WaveHeader.Channel);
end;
Procedure TWave.Halt;
begin
DspCommand($D0);
end;
Procedure TWave.Continue;
begin
DspCommand($D4);
end;
Procedure TWave.InitWave;
begin
BufSize:=$8000;
DMABuffer:=MemAllocSeg(BufSize);
InitSbIrq;
end;
Procedure TWave.DoneWave;
begin
DoneSbIrq;
if DMABuffer<>nil then
FreeMem(DMABuffer,BufSize);
end;
Procedure TWave.DmacDac;
var
AbsAdr:Longint;
DatOfs:Word;
CurPage:Byte;
begin
AbsAdr:=(Longint(Seg(DMABuffer^)) shl 4) +
Longint(Ofs(DMABuffer^));
DatOfs:=Word(AbsAdr);
CurPage:=AbsAdr shr 16;
Port[$0A]:=5; {清除DMA通道1屏蔽}
Port[$0C]:=0; {清先后触发器}
Port[$0B]:=$49; {传输模式为DAC}
Port[$02]:=Lo(DatOfs); {发送基址LSB}
Port[$02]:=Hi(DatOfs); {发送基址MSB}
Port[$83]:=CurPage; {页面 号}
Port[$03]:=Lo(BlockSize-1); {DMA数据个数LSB}
Port[$03]:=Hi(BlockSize-1); {DMA数据个数MSB}
Port[$0A]:=1; {激活DMA通道1}
end;
Procedure TWave.DmaWave;
begin
DmacDac;
if HighSpeed then
begin
DspCommand($48);
DspCommand(Lo(DataLength-1));
DspCommand(Hi(DataLength-1));
DspCommand($91);
end else
begin
DspCommand($14);
DspCommand(Lo(DataLength-1));
DspCommand(Hi(DataLength-1));
end;
end;
Procedure TWave.Play;
begin
if not DspFound then Exit;
SpeakerOn;
DspCommand($40); {Set Time Constant}
DspCommand(Time_Constant);
DMAComplete:=False;
InitWave;
BlockSize:=DataSize;
if BlockSize>$8000 then BlockSize:=$8000;
if DMABuffer<>nil then
DmaWave;
end;
Function TWave.Playing;
begin
Playing:=False;
if (DMABuffer=nil) or DMAComplete then Exit;
DoneWave;
SpeakerOff;
Playing:=True;
end;
end.