返回
{***************************************************************}
{***               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.