unit noiz;   { NOIZ.PAS  Copyright (c) 1990 DSoft Specialties }
interface    { Sound routines for the Tandy 1000 and/or PCJr. See NOIZ.SIM }

{ All I ask is if you use any of these routines in your program
  please mention DSoft in the docs or in a copyright message }

const
  inturbo: boolean = true;

type
  voices = 0..3;
  attenuations = 0..15;
  styles = 1..4;

const
  A1 = 27;  A2 = 55;  A3 = 110;  A4 = 220;  A5 = 440;  A6 = 880;  A7 = 1760;
  B1 = 31;  B2 = 62;  B3 = 123;  B4 = 247;  B5 = 494;  B6 = 988;  B7 = 1976;
  C1 = 33;  C2 = 65;  C3 = 131;  C4 = 262;  C5 = 523;  C6 = 1047; C7 = 2093;
  D1 = 37;  D2 = 74;  D3 = 147;  D4 = 294;  D5 = 588;  D6 = 1175; D7 = 2349;
  E1 = 41;  E2 = 83;  E3 = 165;  E4 = 330;  E5 = 660;  E6 = 1320; E7 = 2640;
  F1 = 44;  F2 = 88;  F3 = 175;  F4 = 350;  F5 = 700;  F6 = 1400; F7 = 2800;
  G1 = 49;  G2 = 98;  G3 = 196;  G4 = 392;  G5 = 784;  G6 = 1568; G7 = 3136;

  A8 = 3520;  A9 = 7040;  A10 = 14080;
  B8 = 3952;  B9 = 7904;  B10 = 15808;
  C8 = 4160;  C9 = 8320;  C10 = 16640;
  D8 = 4704;  D9 = 9408;  D10 = 18816;
  E8 = 5280;  E9 = 10560;
  F8 = 5600;  F9 = 11200;
  G8 = 6272;  G9 = 12544;

  AS1 = 29; AS2 = 58; AS3 = 116; AS4 = 231; AS5 = 466; AS6 = 928; AS7 = 1856;
  CS1 = 34; CS2 = 69; CS3 = 139; CS4 = 277; CS5 = 554; CS6 = 1108;CS7 = 2240;
  DS1 = 39; DS2 = 78; DS3 = 156; DS4 = 311; DS5 = 622; DS6 = 1244;DS7 = 2496;
  FS1 = 46; FS2 = 93; FS3 = 185; FS4 = 370; FS5 = 740; FS6 = 1480;FS7 = 2960;
  GS1 = 26; GS2 = 52; GS3 = 208; GS4 = 415; GS5 = 830; GS6 = 1660;GS7 = 3320;

  AS8 = 3712;  AS9 = 7424;  AS10 = 14848;
  CS8 = 4480;  CS9 = 8960;  CS10 = 17920;
  DS8 = 4992;  DS9 = 9984;  DS10 = 19968;
  FS8 = 5920;  FS9 = 11840;
  GS8 = 6640;  GS9 = 13280;

const
  stacatto: boolean = false;   legato: boolean = false;
  zetto:    boolean = false;   xetto:  boolean = false;
  dtime:    integer = 80;

procedure wait(dt: longint);
procedure delay(dt: longint);
procedure sound(freq: word);
procedure nosound;
procedure sound_level(voice: voices;atten: attenuations);
procedure sound_period(voice: voices;period: integer);
procedure sound_pitch(voice: voices;freq: real);
procedure sound_off;
procedure extsound(freq,dur: integer;level: attenuations;voice: voices);
procedure plays(freq,dur: word;attack,decay: integer;voice: voices);
procedure chord(freq1,freq2,freq3,dur,level: integer);
procedure play(freq,dur: integer;v: voices;style: styles);
procedure noise(ch: char;sr,atten,dur: word);
procedure note1(freq,dura: word);
procedure note4(note,dura: integer);
procedure dubend(freq1,freq2,dt: integer);
procedure bend(tone,tone1,tonedur,dur,reps: integer);
procedure scale(freq1,freq2,freq3: integer;a,b,c,d,e,f,g,aa: integer);
procedure scale2(a,b,c,d,e,f,g,z: integer;dtime,attack,decay: integer;
                 v: voices);

procedure snd(freq: integer);
procedure snd2(freq: integer);
procedure nosnd;
procedure nosnd2;
procedure quiet;
function fkey: char;
function keyhit: boolean;

implementation

uses dos;

procedure wait(dt: longint);
const
  inturb = 32;
  indos = 60;
var tt,ir,tr: longint;
begin
  if inturbo then
    tt:=inturb
  else
    tt:=indos;
  for ir:=1 to dt do
    for tr:=1 to tt do
end;

procedure delay(dt: longint);
begin
  wait(dt);
end;

{$F+}
procedure sound(freq: word);
begin
  inline(
  $8B/$5E/$06/$B8/$DD/$34/
  $BA/$12/$00/$39/$DA/
  $73/$1A/$F7/$F3/$89/$C3/
  $E4/$61/$A8/$03/$75/$08/
  $0C/$03/$E6/$61/$B0/$B6/
  $E6/$43/$88/$D8/$E6/$42/
  $88/$F8/$E6/$42);
end;
{$F-}

procedure nosound;
begin
  inline($E4/$61/$24/$FC/$E6/$61);
end;

procedure sound_level(voice: voices;atten: attenuations);
{ change the level (atten) of a voice }
begin
  if (atten < 0) then
    atten:=0
  else
  if (atten > 15) then atten:=15;
  port[$C0]:=($90 + (voice shl 5) + (atten and $0F));
end;

procedure sound_period(voice: voices;period: integer);
{ change the sound divider (period) of a voice }
begin
  port[$C0]:=($80 + (voice shl 5) + (period and $0F)); { lo 4 bits }
  port[$C0]:=((period shr 4) and $3F);                 { hi 6 bits }
end;

procedure sound_pitch(voice: voices;freq: real);
{ change the pitch (freq) of a voice }
var period: real;

  function chip_freq(freq: real): word;
  begin
    chip_freq:=round(((3.579 * 1000000) / (freq * 32)));
  end;

begin
  if (freq = 0.0) then
    period:=0
  else
    period:=chip_freq(freq);
  if (period <= 1) or (period > $3FF) then period:=1;
  sound_period(voice,round(period));
end;

procedure sound_off;
var v: voices;
begin
  for v:=0 to 3 do
  begin
    sound_level(v,15);
    sound_pitch(v,0);
  end;
end;

procedure extsound(freq,dur: integer;level: attenuations;voice: voices);
begin
  sound_level(voice,level div 4);
  if ((freq < A3) or (voice = 3)) then
    sound(freq)
  else
    sound_pitch(voice,freq);
  wait(dur);
end;

procedure plays(freq,dur: word;attack,decay: integer;voice: voices);
var i,j,k: integer;
begin
  if (dur < 4) then dur:=4;
  if (freq < A3) then
  begin
    sound(freq);
    wait(dur);
  end else
  begin
    sound_pitch(voice,freq);
    for i:=attack downto 0 do
    begin
      sound_level(voice,i);
      wait(2);
    end;
    wait(dur-(attack-decay)-4);
    for i:=0 to decay do
    begin
      sound_level(voice,i);
      wait(2);
    end;
  end;
end;

procedure chord(freq1,freq2,freq3,dur,level: integer);
var i,j,k: integer;
begin
  if (level > 15) then
  begin
    for i:=15 downto (level - 15) do
    begin
      extsound(freq1,dur div 2,i,0);
      extsound(freq2,dur div 2,i,1);
      extsound(freq3,dur div 2,i,2);
    end;
    extsound(freq1,dur,level,0);
    extsound(freq2,dur,level,1);
    extsound(freq3,dur,level,2);
    wait(dur);
    exit;
  end else
  for i:=1 to level do
  begin
    extsound(freq1,dur div 2,i,0);
    extsound(freq2,dur div 2,i,1);
    extsound(freq3,dur div 2,i,2);
  end;
  extsound(freq1,dur,level,0);
  extsound(freq2,dur,level,1);
  extsound(freq3,dur,level,2);
  wait(dur);
end;

procedure play(freq,dur: integer;v: voices;style: styles);
var zz,z,x,xx,i: integer;
begin
  x:=dur div 3;
  xx:=dur div 2;
  z:=xx-x;
  zz:=x div 2;
  case style of
    1: begin
         extsound(freq,z,3,v);
         for i:=15 downto 1 do extsound(freq,zz,i,v);
         for i:=1 to 13 do extsound(freq,zz,i,v);
         extsound(freq,xx,2,v);
         exit;
       end;
    2: begin
         extsound(freq,xx+z,4,v);
         for i:=1 to 15 do
         begin
           extsound(freq,zz,5 xor i,v);
           if (v >= 2) then
             extsound(freq,zz,i,v-1)
           else
             extsound(freq,zz,i,v+1);
         end;
         exit;
       end;
    3: begin
         for i:=15 downto 1 do
         begin
           extsound(freq*2,1,i,v);
           if (v >=2) then
             extsound(freq,zz,i,v-1)
           else
             extsound(freq,zz,i,v+1);
         end;
         extsound(freq,zz,10,v);
         for i:=15 downto 7 do extsound(freq,zz,i,v);
         extsound(freq,xx,2,v);
         exit;
       end;
    4: begin
         for i:=0 to 15 do extsound(freq,1,i,v);
         for i:=15 downto 0 do
         begin
           if (v >= 2) then
             extsound(freq*2,zz,i,v-1)
           else
             extsound(freq*2,zz,i,v+1);
         end;
         for i:=7 to 15 do extsound(freq,zz,i,v);
         extsound(freq,xx,10,v);
         exit;
       end;
  end;
end;

procedure noise(ch: char;sr,atten,dur: word);
var portpass1: integer;
begin
  portpass1:=224;
  if (ch in ['W','w']) then portpass1:=portpass1 + 4;
  case sr of
    10: portpass1:=portpass1 + 1;
    20: portpass1:=portpass1 + 2;
  end;
  port[$C0]:=240+atten;
  port[$C0]:=portpass1;
  wait(dur);
end;

procedure note1(freq,dura: word);
var x: integer;
begin
  if keyhit then
  begin
    quiet;
    exit;
  end;
  if (legato=true) then
  begin
    sound(freq); wait(dura-7); sound(freq); wait(7);
  end else
  if (stacatto=true) then
  begin
    sound(freq); wait(dura-11);
    nosound; wait(11);
  end else
  if (zetto=true) then
  begin
    x:=dura div 3;
    sound(freq); wait(x);
    nosound; wait(x*2);
  end else
  if (xetto=true) then
  begin
    x:=dura div 5;
    sound(freq); wait(x);
    nosound; wait(x*4);
  end else
  begin
    sound(freq); wait(dura);
    nosound;
  end;
end;

procedure note4(note,dura: integer);
var x: integer;
begin
  if keyhit then
  begin
    quiet; exit;
  end;
  if (legato=true) then
  begin
    extsound(note,dura-7,0,0);
    extsound(note,7,0,0);
  end else
  if (stacatto=true) then
  begin
    extsound(note,dura-11,0,0);
    sound_level(1,15);
    wait(11);
  end else
  if (zetto=true) then
  begin
    x:=dura div 3;
    extsound(note,x,0,0);
    sound_level(1,15); wait(x*2);
  end else
  if (xetto=true) then
  begin
    x:=dura div 5;
    extsound(note,x,0,0);
    sound_level(1,15); wait(x*4);
  end else
  begin
    extsound(note,dura,0,0);
    sound_level(1,15);
  end;
end;

procedure dubend(freq1,freq2,dt: integer);
var i: integer;
begin
  for i:=freq1 to freq2 do extsound(i,dt,1,0);
  sound_level(0,15);
end;

procedure bend(tone,tone1,tonedur,dur,reps: integer);
var i,j: integer;
begin
  if (tone1 > tone) then
  begin
    for i:=1 to reps do
    begin
      extsound(tone1,tonedur,1,0);
      dubend(tone,tone1,dur);
      sound_level(0,15); wait(10);
    end;
  end else
  if (tone > tone1) then
  begin
    for i:=1 to reps do
    begin
      for j:=tone downto tone1 do extsound(j,dur,1,0);
      extsound(tone1,tonedur,1,0);
    end;
    sound_level(0,15);
  end;
end;

procedure scale(freq1,freq2,freq3: integer;a,b,c,d,e,f,g,aa: integer);
begin
  chord(freq1,freq2,freq3,1,5);
  if (freq1 >= A3) then
  begin
    note1(a,dtime); note1(b,dtime); note1(c,dtime); note1(d,dtime);
    note1(e,dtime); note1(f,dtime); note1(g,dtime); note1(aa,dtime);
  end else
  if (freq1 < A3) then
  begin
    note4(a,dtime); note4(b,dtime); note4(c,dtime); note4(d,dtime);
    note4(e,dtime); note4(f,dtime); note4(g,dtime); note4(aa,dtime);
  end;
  quiet;
end;

procedure scale2(a,b,c,d,e,f,g,z: integer;dtime,attack,decay: integer;
                 v: voices);
begin
  plays(a,dtime,attack,decay,v);
  plays(b,dtime,attack,decay,v);
  plays(c,dtime,attack,decay,v);
  plays(d,dtime,attack,decay,v);
  plays(e,dtime,attack,decay,v);
  plays(f,dtime,attack,decay,v);
  plays(g,dtime,attack,decay,v);
  plays(z,dtime,attack,decay,v);
end;

procedure snd(Freq: integer);
var Count: integer;
begin
  Count:=$1B1AAA div Freq;
  Port[$C0]:=$A5;
  port[$C0]:=$15;
  port[$C0]:=$A0;
  port[$C0]:=$A5;
  port[$C0]:=hi(count);
  port[$C0]:=$A0;
end;

procedure snd2(Freq: integer);
var Count: integer;
begin
  Count:=$1B1AAA div Freq;
  Port[$C0]:=$C5;
  port[$C0]:=$15;
  port[$C0]:=$C0;
  port[$C0]:=$C5;
  port[$C0]:=hi(count);
  port[$C0]:=$C0;
end;

procedure Nosnd;
var sport: Byte;
begin
  SPort:=Port[$C0];
  port[$C0]:=$BF;
end;

procedure Nosnd2;
var sport: Byte;
begin
  SPort:=Port[$C0];
  port[$C0]:=$DF;
  port[$C0]:=$BF;
end;

procedure quiet;
begin
  nosound;
  nosnd; nosnd2;
  sound_off;
  port[$C0]:=$9F;
end;

function fkey: char;
var regs: registers;
begin
  regs.AH:=0;
  intr($16,regs);
  if regs.AL=0 then
    fkey:=chr(regs.AH+128)
  else
    fkey:=chr(regs.AL)
end;

function keyhit: boolean;
var regs: registers;
begin
  regs.AH:=1;
  intr($16,regs);
  keyhit:=(regs.flags and 64)=0;
end;

end.
