[Back to SOUND SWAG index]  [Back to Main SWAG index]  [Original]

{$M 16384,0,655360}

uses Dos, CRT, objects;

const SBase = $220;               {Default port base for Sound Blaster.
                                   Change if necessary}
      SIrq  = 5;                  {Default Irq line for Sound Blaster.
                                   Change if necessary}
      SDMA  = 1;                  {Default DMA channel for Sound Blaster.}

type
 TWAVRec = record
             ID: LongInt;
            Len: LongInt;
           end;
 PWAVFmt = ^TWAVFmt;
 TWAVFmt = record
            case word of
             1:( FTag: word;
                 NChan: word;
                 SampR: word;
                 AvgSR: word;
                 BLKAl: word;
                 FMTLen: word;
                 FMTDat: array[0..256] of byte);
             2:( Chunk:Pointer);
           end;
var
 WAVFile: TDosStream;             {WAV file object}
 BlkID: TWAVRec;                  {ID for each block in WAV}
 BlkFmt: PWAVFmt;                 {Block format}
 TotalSz: LongInt;                {Total size of WAV data}
 DSPCmd: byte;
 NumBits: byte;
 SampByte: byte;
 BlockSize: word;
 EOB: boolean;
 DF: String;

procedure NewBlock; interrupt;    {Procedure to set up next block or}
var X:Byte;                       {end playback}
begin

 X := port[SBase+$e];
 port[$20] := $20;
 EOB := true;

end;

procedure PrepareSB;
begin

 SetIntVec(SIrq + 8, @NewBlock);           {Set up service routine}

 asm
  in al,$61                                 {Enable timer 2, but}
  and al,$fc                                {do not turn on sound.}
  or al,1
  out $61,al

  sti

  mov dx,SBase+6                            {DSP (Digital Sound Processor)
                                             reset port}
  mov al,1                                  {Reset command}
  out dx,al

  mov bx,4
  call @9                                   {Wait 4 clocks}

  mov al,0                                  {Normal mode}
  out dx,al

 @3: mov dx,SBase+$e                        {DSP status port}
 @2: in al,dx                               {Read status}
  test al,$80                               {If high bit not set, no data}
  jz @2                                     {ready}

  mov dx,Sbase+$a                           {DSP read port}
  in al,dx                                  {Read status}
  cmp al,$aa                                {AA indicates ready}
  jnz @3
  jmp @4
 @5:
  in al,dx                                  {Wait for response to last byte}
  test al,$80                               {sent}
  jnz @5
  mov al,ah
  out dx,al                                 {Send next byte}
  ret

 @9:
  push bx
  mov al,$b6                                {Write count to timer #3}
  out $43,al

  mov al,0                                  {Low byte of count}
  out $42,al

  mov al,$10                                {High byte count}
  out $42,al

  sub bx,$1000
  neg bx                                    {1000h-clocks=desired count}
 @10:
  mov al,$80                                {Read count from timer}
  out $43,al

  in  al,$42                                {Low byte}
  mov ah,al
  in  al,$42                                {High byte}
  xchg ah,al

  cmp bx,ax                                 {Pause until count reached}
  jl  @10
  pop bx
  ret
 @4:
  mov dx,SBase+$c

  mov ah,$40                                {Set time constant}
  call @5

  mov ah,SampByte                           {Time divisor}
  call @5

 end;

 port[$21] := port[$21] and not (1 shl SIRQ);   {Enable SB interrupt}

end;

procedure ErrorEnd;
begin
 WAVFile.Done;
 Writeln('Error in .WAV');
 Halt(1);
end;

procedure PlaySound(SndLen: longint);

var AbsAddr: LongInt;
    FirstBlk, SecBlk, CurBlk: Pointer;

begin

 EOB := False;

 GetMem(BlkFmt, BlockSize*2);
 FirstBlk := BlkFmt;
 SecBlk := pointer(longint(FirstBlk) + BlockSize);
 CurBlk := FirstBlk;


 WAVFile.Read(BlkFmt^, BlockSize);
 SndLen := SndLen - BlockSize;

 repeat
  AbsAddr := Seg(CurBlk^);
  AbsAddr := AbsAddr * 16 +Ofs(CurBlk^);
  SndLen := SndLen - BlockSize;
  asm
   jmp @4

  @5:
   in al,dx                                 {Wait for response to last byte}
   test al,$80                              {sent}
   jnz @5
   mov al,ah
   out dx,al                                {Send next byte}
   ret

  @4:

   mov bx,1
   mov cx,integer(AbsAddr)
   mov dx,SBase+$c

   mov al,0                                 {Clear byte high/low flip-flop}
   out $c,al

   mov al,$49                               {Set memory read, single transfer,}
   out $b,al                                {channel 1}

   mov al,cl                                {Enter base address}
   out SDMA*2,al
   mov al,ch
   out SDMA*2,al

   mov ax,integer(AbsAddr+2)                {High 4 bits goes to DMA page reg}
   mov dx,$83
   mov cl,SDMA
   sub cl,2
   mov ch,2                                 {Calculate DMA page address}
   shr ch,cl
   xor dl,ch
   out dx,al                                {Send page byte}

   mov ax,BlockSize                         {Set byte count}
{   dec ax   }
   out SDMA*2+1,al
   xchg al,ah
   out SDMA*2+1,al
   push ax

   mov al,SDMA                              {Re-enable DMA channel 1}
   out $a,al

   mov dx,SBase+$c                          {DSP port}

   mov ah,DSPCmd                            {DMA 8-bit transfer}
   call @5

   pop ax                                   {Get transfer again}
   mov bl,al
   call @5
   mov ah,bl
   call @5

  end;

  DSPCmd := DSPCmd and $fe;

  if (CurBlk = FirstBlk) then CurBlk := SecBlk else CurBlk := FirstBlk;
  if SndLen > 0 then WAVFile.Read(CurBlk^, BlockSize);

  while not EOB do
   if Keypressed then ErrorEnd;
  EOB := False;

 until (SndLen<=0);
end;


begin

 DF := ParamStr(1);

 WAVFile.Init(DF, stOpenRead);              {Open WAV file}
 WAVFile.Read(BlkID, SizeOf(TWAVRec));      {Read in first block}

 if BlkID.ID = $46464952 then               {ID of WAV file}
 begin
  TotalSz := BlkID.Len;                     {Get total size}
  repeat
   WAVFile.Read(BlkID, 4);                  {Read in type chunk}
   TotalSz := TotalSz - 4;                  {and update TS}

   if BlkID.ID <> $45564157 then ErrorEnd;  {Must be "WAVE"}
   repeat
    WAVFile.Read(BlkID, SizeOf(TWAVRec));    {Read in format chunk}
    TotalSz := TotalSz - SizeOf(TWavRec);

    if BlkID.ID = $20746d66  then            {"fmt ", set WAV format}
    begin
     getmem(BlkFmt, BlkID.Len);
     WAVFile.Read(BlkFmt^, BlkID.Len);
     TotalSz := TotalSz - BlkID.Len;
     with BlkFmt^ do
     begin
      if FTag = $200 then DSPCmd := $75 else {ADPCM 4-bit compression}
       if FTag = 1 then DSPCmd := $14 else   {Normal}
        ErrorEnd;
      if DSPCmd = $75 then NumBits := 4 else NumBits := 8;
      if NChan = 2 then DSPCmd := DSPCmd + 8; {Stereo}
      SampByte := 256-(1000000 div SampR);   {Sampling rate}
      BlockSize := BlkAl;                    {Size of buffer}
     end;
     freemem(BlkFmt, BlkID.Len);
    end else

    if BlkID.ID = $61746164 then
    begin
     PrepareSB;                              {Perform init stuff}
     TotalSz := TotalSz - BlkID.Len;
     PlaySound(BlkID.Len);
    end else

     ErrorEnd;
   until TotalSz <= 0;
  until TotalSz <= 0;
 end else
  ErrorEnd;
 WAVFile.Done;
 port[$21] := port[$21] or (1 shl SIrq);
end.

[Back to SOUND SWAG index]  [Back to Main SWAG index]  [Original]