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

{                                SBLASTER.PAS                            }
{ A small unit demonstrating DMA transfers to and from the Sound Blaster }
{ as well as simple double buffering techniques.                         }
{               Programmed by Ian Ash of Arcturus.                       }
Unit SBlaster;

interface
uses Crt, Dos;

  Procedure InitSB(rate : Word);
  Procedure SBPlay(segm, ofst, lgth : Word);
  Procedure SBRecord(segm, ofst, Lgth : Word);
  Procedure SetInterrupt(Intrrpt : Pointer);
  Procedure Record2Disk(var buf; name : String);

type
  InfoType = Array[1..1] Of Byte;
var
  samplerate : Byte;
  CurrentInterrupt : Pointer;
  FileIDByte : Array[1..2] Of Char;
  EndOfTransfer : Boolean;
const
  BasePort = $220;
  DSPReset = BasePort + $06;
  DSPRead = BasePort + $0A;
  DSPWrite = BasePort + $0C;
  DSPDataAvail = BasePort + $0E;
  IRQ2 = $0A;
  IRQ3 = $0B;
  IRQ5 = $0D;
  IRQ7 = $0F;

implementation
{ Simple interrupt handler for use. It acknowledges the end of transfer }
{ and then sets a flag EndOfTransfer to indicate the transfer end.      }
Procedure HandleInterrupt; interrupt;
var
  Info : Byte;
begin
  Info := Port[DSPDataAvail];
  Port[$21] := Port[$21] XOR 129;
  EndOfTransfer := True;
  Port[$20] := $20;
end;

Procedure InitSB(rate : Word);  { Initializes SoundBlaster And Sets The }
var                             { Sampling Rate To rate Which Is Given In }
  ActRate : LongInt;            { Hertz. }
  Count : Byte;
  InitComplete : Byte;
begin
  CurrentInterrupt := @HandleInterrupt;
  sampleRate := 256 - (1000000 div rate);
  Port[DSPReset] := 1;
  Delay(5);
  Port[DSPReset] := 0;
  Count := 0;
  If (Port[DSPDataAvail] And 128) = 128 Then
  repeat
    InitComplete := Port[DSPRead];
    Count := Count + 1;
  until (InitComplete = $AA) Or (Count > 150);
  FileIDByte[1] := 'I'; FileIDByte[2] := 'A';
end;

{ Plays a digitised sound clip. 'segm' and 'ofst' is the segment and }
{ offset where the data is stored and Lgth is the length in bytes of }
{ the sound clip. Note that this routine can only play a sound clip  }
{ of maximum size 64K.                                               }
Procedure SBPlay(segm, ofst, Lgth : Word);
var
  page : Byte;
  offset : Word;
  count : word;
  InitComplete : Byte;
  OldInt : Pointer;
begin
  SetIntVec(IRQ5, @HandleInterrupt);
  Port[$21] := Port[$21] XOR 129;
  page := Hi(segm) div $10;
  offset := (segm shl 4) + ofst;
  Port[$0A] := $05;
  Port[$0C] := $00;
  Port[$0B] := $49;
  Port[$02] := Lo(offset);
  Port[$02] := Hi(offset);
  Port[$83] := page;
  Port[$03] := lo(lgth);
  Port[$03] := hi(lgth);
  Port[$0A] := 1;
  Port[DSPWrite] := $D1;
  Port[DSPWrite] := $40;
  Port[DSPWrite] := samplerate;
  repeat until (Port[DSPWrite]  And 128) = 0;
  Port[DSPWrite] := $14;
  Delay(1);
  Port[DSPWrite] := lo(lgth);
  repeat until (Port[DSPWrite]  And 128) = 0;
  Port[DSPWrite] := hi(lgth);
end;

{ Records a sound clip to data location whose segment:offset is specified }
{ in 'segm' and 'ofst' respectively. 'lgth' is the number of bytes to be  }
{ recorded.                                                               }
Procedure SBRecord(segm, ofst, lgth : Word);
var
  page : Byte;
  offset : Word;
  OldInt : Pointer;
begin
  SetIntVec(IRQ5, @HandleInterrupt);
  Port[$21] := Port[$21] XOR 129;
  page := Hi(segm) div $10;
  offset := (segm shl 4) + ofst;
  Port[$0A] := $05;
  Port[$0C] := $00;
  Port[$0B] := $45;
  Port[$02] := Lo(offset);
  Port[$02] := Hi(offset);
  Port[$83] := page;
  Port[$03] := lo(lgth);
  Port[$03] := hi(lgth);
  Port[$0A] := 1;
  Delay(10);
  Port[DSPWrite] := $40;
  repeat until (Port[DSPWrite]  And 128) = 0;
  Port[DSPWrite] := samplerate;
  repeat until (Port[DSPWrite]  And 128) = 0;
  Port[DSPWrite] := $24;
  Delay(1);
  Port[DSPWrite] := lo(lgth);
  Delay(1);
  Port[DSPWrite] := hi(lgth);
end;

Procedure SetInterrupt(Intrrpt : Pointer);
begin
  CurrentInterrupt := Intrrpt;
end;

{ Records a digital sound to disk. buf is a data area to be used as a  }
{ buffer and must be 64K in size (a whole segment!!). name is the name }
{ of the DOS file to store the info in.                                }
Procedure Record2Disk(var buf; name : String);
type
  DataType = Array[1..1] Of Byte;
var
  f : File;
  Data : DataType absolute buf;
  WorkArea : Word;
  ch : Char;
begin
  Assign(f, name);
  Rewrite(f, 1);
  BlockWrite(f, FileIDByte, 2);
  FileIDByte[1] := Chr(samplerate);
  BlockWrite(f, FileIDByte, 1);
  FileIDByte[1] := 'I';
  ch := ReadKey;
  SetInterrupt(@handleInterrupt);
  EndOfTransfer := False;
  WorkArea := 1;
  SBRecord(Seg(Data[WorkArea]), Ofs(Data[WorkArea]), 30000);
  repeat
    repeat until EndOfTransfer;
    WorkArea := 30500;
    EndOfTransfer := False;
    SBRecord(Seg(Data[WorkArea]), Ofs(Data[WorkArea]), 30000);
    BlockWrite(f, Data[1], 30000);
    repeat until EndOfTransfer;
    WorkArea := 1;
    EndOfTransfer := False;
    SBRecord(Seg(Data[WorkArea]), Ofs(Data[WorkArea]), 30000);
    BlockWrite(f, Data[WorkArea + 30499], 30000);
  until KeyPressed;
  Close(f);
end;
end.

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