{

 SoundS.INC  5-27-93  by Steven Tallent

This is a Unit to play 8-bit raw Sound Files on any PC, up to 64k
large.  It supports the PC speaker or a DAC (LPT1 or LPT2), although
I do plan to upgrade it to support the SoundBlaster and Adlib Sound
cards.  It is Object-oriented in nature, With one instance of a
speaker defined automatically.  This Unit is public domain, With
code and ideas captured from this echo and Dr. Dobbs Journal.

Using the code is simple.  Just setup the the Speaker.Kind,
Speaker.Silent, and Speaker.DisINT to the appropriate values, then
just use the methods included.  The SoundBoard Object is very
flexible For your own code.

SoundBoard.Play  - Plays 8-bit music in What^ For Size length, With
                   Speed milliseconds between each Byte, and SampleRate
                   as the sample rate (in Hz).  Speed will need to be
                   changed on different computers (of course).

SoundBoard.Sound - Plays a Sound at HZ Hertz, Duration in ms, on
                   VOICE voice.  The code included is useable on
                   the PC speaker (1 voice) or the Tandy speaker
                   (3 voices!).

SoundBoard.Reset - Resets the Sound board.

SoundBoard.Silent- Convenient Variable that disables all PLAY and Sound
                   if set to True.

SoundBoard.DisINT- Disables all interrupts (except during Delays)
                   While using PLAY.

This code may be freely distributed, changed, or included in your
own commercial or shareware code, as long as this isn't all your code
does.  This code may be included in commercial or shareware code
libraries only With my permission (I'd like to see someone get some
use out of it).
}

Unit Sounds;

Interface

Type
  BigArray    = Array[0..0] of Byte;
  PBigArray   = ^BigArray;
  KSoundBoard = (PCspeaker, Tandy, DAC1, DAC2, AdLib, SB, SBpro, SB16);

  SoundBoard  = Object
    Kind   : KSoundBoard;
    Silent : Boolean;
    DisINT : Boolean;
    Procedure Play(What : PBigArray; Size : Word; Speed : Byte;
                    SampleRate : Word);
    Procedure Sound(Hz, Duration : Word; Voice, Volume : Byte);
    Procedure Reset;
  end;

Var
  Speaker : SoundBoard;

Procedure Delay(ms : Word);

Implementation

Procedure SoundBoard.Reset;
begin
  Case Kind of
    PCspeaker, Tandy : Port[97] := Port[97] and $FC;
  end;
  end;

Procedure SoundBoard.Sound(Hz, Duration : Word; Voice, Volume : Byte);
Var
  Count   : Word;
  SendByte,
  VoiceID : Byte;
begin
  Case Kind of
    PCspeaker :
      begin
        Count := 1193180 div Hz;
        Port[97] := Port[97] or 3;
        Port[67] := 182;
        Port[66] := Lo(Count);
        Port[66] := Hi(Count);
        Delay(Duration);
        Port[97] := Port[97] and $FC;
      end;
    Tandy :
      begin
        if Voice = 1 then
          VoiceId := 0
        else
        if Voice = 2 then
          VoiceId := 32
        else
          VoiceId := 64;
        Count := 111861 div Hz;
        SendByte := 128 + VoiceId + (Count mod 16);
        Port [$61] := $68;
        Port [$C0] := SendByte;
        Port [$C0] := Count div 16;
        if Voice = 1 then
          VoiceId := 16
        else
        if Voice = 2 then
          VoiceId := 48
        else
          VoiceId := 96;
        SendByte := 128 + VoiceId + (15 - Volume);
        Port [$61] := $68;
        Port [$C0] := SendByte;
        Delay(Duration);
        SendByte := 128 + VoiceId + 15;
        Port [$61] := $68;
        Port [$C0] := SendByte;
    DAC1:;
    DAC2:;
    AdLib:;
    SB:;
    SBPro:;
    SB16:;
  end;

Procedure SoundBoard.Play(What : PBigArray; Size : Word;
                          Speed : Byte; SampleRate : Word);
Var
  Loop,
  Count,
  Data  : Word;
begin
  if not Silent then
  begin
    Case Kind of
      PCspeaker, Tandy :
        begin
          Port[97] := Port[97] or 3;
          Count := 1193180 div (SampleRate div 256);
          For Loop := 1 to Size do
          begin
            Data := Count div (What^[Loop] + 1);
            Port[67] := 182;
            Port[66] := Lo(Data);
            Port[66] := Hi(Data);
            Delay(Speed);
            if DisINT then
            Asm
              CLI
            end;
          end;
          Port[97] := Port[97] and $FC;
        end;

        DAC1:
          For Loop := 1 to Size do
          begin
            Port [$0378] := What^[Loop];
            Delay (Speed);
            if DisINT then
            Asm
              CLI
            end;
          end;

        DAC2:
          For Loop := 1 to Size do
          begin
            Port [$0278] := What^[Loop];
            Delay (Speed);
            if DisINT then
            Asm
              CLI
            end;
          end;

        AdLib:;
        SB:;
        SBPro:;
        SB16:;
      end;
      Asm
        STI
      end;
  end;
end;

Procedure Delay(ms : Word); Assembler;
Asm
  STI
  MOV AH, $86
  MOV CX, 0
  MOV DX, [ms]
  INT $15
end;

end.

{-----------------------------------------------------------------
Here's a Program that will accept three values from the command
line, the File, its speed, and the sample rate, and plays it
through the PC speaker.  I've tried in on WAV, VOC, SAM, and even
Amiga sampled Files, With no problems (limited to 64k). I've even
played MOD Files to hear all the sampled instruments!  This Program
does not strip header information, but plays it too, but I can't
hear the difference on WAV and VOC Files.
}
Program TestSnd;
Uses
  Sounds;
Var
  I2   : PBigArray;
  spd  : Integer;
  samp : Word;
  res  : Word;
  siz  : Word;
  s    : String;
  f1   : File of Byte;
  F    : File;
begin
  Speaker.Kind   := PCspeaker;
  Speaker.DisINT := True;
  Speaker.Silent := False;
  s := ParamStr(1);
  Assign(f1,s);  {Get size of File}
  Reset(f1);
  Val (ParamStr(2), Spd, Res);
  Val (ParamStr(3), samp, Res);
  siz := FileSize(f1);
  close(f1);
  Assign(f,s);
  Reset(f);
  getmem (I2,siz);  {Allocate Memory For Sound File}
  BlockRead(f,I2^,siz,res);  {Load Sound into Memory}
  Speaker.Play (i2, siz, spd, samp);
  FreeMem (I2, siz);
end.
