Unit IntvSound;

Interface

Uses Base;

Const
  AY_AFINE    = 0;
  AY_BFINE    = 1;
  AY_CFINE    = 2;
  AY_EFINE    = 3;
  AY_ACOARSE  = 4;
  AY_BCOARSE  = 5;
  AY_CCOARSE  = 6;
  AY_ECOARSE  = 7;

  AY_ENABLE   = 8;
  AY_NOISEPER = 9;
  AY_ESHAPE   = 10;

  AY_AVOL     = 11;
  AY_BVOL     = 12;
  AY_CVOL     = 13;

  AY_PORTA    = 14;
  AY_PORTB    = 15;

  MaxNumChips = 2;

{ default clock frequency, frequency in MHz * 100 }

{  AY8910_CLOCK = 1500000000;}       { 1.832727040 MHZ }

  AY8910_CLOCK = 1789772;{2000000;}

{  SAMPLE_RATE  = 22050;}
//  MAX_BUF_SIZE = sndBufSize;//{256;}128;{64;}
  Sound_Is_Init  : Boolean = False;
  Sample_Pos     : LongInt = 0;
  Sample_Counter : LongInt = 0;
  Sample_Mul     : LongInt = 4;
  _AYEnvForms : Packed Array[0..15,0..31] Of SmallInt = (
    ( 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ),
    ( 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ),
    ( 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ),
    ( 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ),
    (  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ),
    (  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ),
    (  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ),
    (  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ),
    ( 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,
      15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0 ),
    ( 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ),
    ( 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,
       0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 ),
    ( 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,
      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 ),
    (  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
       0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 ),
    (  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 ),
    (  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
      15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0 ),
    (  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 )
);

Var
//  Buf         : Packed Array[0..MAX_BUF_SIZE - 1] Of Byte; { Sound buffer }
  DToA        : Packed Array[0..15] Of Byte;
  AYRegs      : Packed Array[0..18,0..MaxNumChips - 1] Of Word;

  Incr0       : Packed Array[0..MaxNumChips - 1] Of SmallInt;
  Incr1       : Packed Array[0..MaxNumChips - 1] Of SmallInt;
  Incr2       : Packed Array[0..MaxNumChips - 1] Of SmallInt; 
  IncrEnv     : Packed Array[0..MaxNumChips - 1] Of LongInt;
  IncrNoise   : Packed Array[0..MaxNumChips - 1] Of LongInt;
  Counter0    : Packed Array[0..MaxNumChips - 1] Of SmallInt;
  Counter1    : Packed Array[0..MaxNumChips - 1] Of SmallInt;
  Counter2    : Packed Array[0..MaxNumChips - 1] Of SmallInt;
  CountEnv    : Packed Array[0..MaxNumChips - 1] Of LongInt;
  CountNoise  : Packed Array[0..MaxNumChips - 1] Of LongInt;
  NoiseGen    : Packed Array[0..MaxNumChips - 1] Of LongInt;
  Envelope    : Packed Array[0..MaxNumChips - 1] Of SmallInt;
  StateNoise  : Packed Array[0..MaxNumChips - 1] Of Byte;
  NumChips    : SmallInt;

  Vol0        : SmallInt;
  Vol1        : SmallInt;
  Vol2        : SmallInt;
  VolNoise    : Packed Array[0..MaxNumChips - 1] Of SmallInt;

  VoiceReady  : Boolean;
  VoicePtr    : Pointer;
  VoiceLen    : Word;
  VoicePos    : Word;
  VoiceFIFO   : Packed Array[0..63] Of Word;
  VoiceFRead  : Word;
  VoiceFWrite : Word;

//  SmallSoundBuf : Packed Array[0..2 * MAX_BUF_SIZE + 15] Of Byte;

  VolScale : Packed Array[0..16,0..31] Of Byte;

  VoiceBuf : Packed Array[0..SndBufSize - 1] Of Byte;

Procedure WriteToPSG(tAddr,Value,Chip: Word);
Procedure InitSound(Playback_Freq,Chips: Word; Reset: Boolean);
Procedure StopSound;
Procedure ProcessSound(Buf: Pointer; Amt: LongInt); 

Implementation

Uses HexWrite;

Const
  Last_Pos : LongInt = 80;

Var
  AYSoundRate : LongInt;         { Output rate (Hz) }
//  AYBufSize   : SmallInt;         { Size of sound buffer, in samples }

  BufPlaying                     : Byte;
  BufToFill                      : Byte;

  BufScan                        : LongInt;

Procedure ProcessSound(Buf: Pointer; Amt: LongInt); External;
{$L IPROCESS.OBJ}

Procedure AYResetChip(Reset: Boolean);
{ reset all chip registers. }
Var I,Chip: LongInt;
Begin
  If Reset Then
  Begin
//    FillChar(Buf,AYBufSize,#0);
    For Chip := 0 To NumChips - 1 Do
    Begin

      { initialize hardware registers }

      For I := 0 To 15 Do AYRegs[I,Chip] := 0;

      AYRegs[AY_ENABLE,Chip] := $3F;{077;}
{      AYRegs[AY_AVOL,Chip]   := 8;
      AYRegs[AY_BVOL,Chip]   := 8;
      AYRegs[AY_CVOL,Chip]   := 8;}
      NoiseGen[Chip]         := 1;
      Envelope[Chip]         := 0;{15;}
      StateNoise[Chip]       := 0;
      Incr0[Chip]            := 0;
      Incr1[Chip]            := 0;
      Incr2[Chip]            := 0;
      IncrEnv[Chip]          := 0;
      IncrNoise[Chip]        := 0;
      CountNoise[Chip]       := 0;
      Counter0[Chip]         := 0;
      Counter1[Chip]         := 0;
      Counter2[Chip]         := 0;
      CountEnv[Chip]         := 0;
    End; { For Chip }
  End;
  BufPlaying := 0;
End; { AYResetChip }

Procedure AYInit(Rate: LongInt; Reset: Boolean);
Begin
  AYSoundRate := Rate;
//  AYBufSize   := BufSize;
  AYResetChip(Reset);
End; { AYInit }

Procedure AYShutdown;
Begin
//  AYBufSize   := 0;
End; { AYShutdown }

Function SoundInit(Playback_Freq: Word; Reset: Boolean): Boolean;
Begin
  Sound_Is_Init := True;
  AYInit(Playback_Freq,Reset);
  SoundInit := True;
End; { SoundInit }

Procedure SoundRestore;
Begin
  If Not Sound_Is_Init Then Exit;
  Sound_Is_Init := False;
  AYShutdown;
End; { SoundRestore }

Procedure WriteToPSG(tAddr,Value,Chip: Word);
Var X: LongInt;
Begin
  AYRegs[tAddr,Chip] := Value And $FF;
  Case tAddr Of
    AY_AVOL,AY_BVOL,AY_CVOL:
    Begin
      AYRegs[tAddr,Chip] := AYRegs[tAddr,Chip] And $1F;       { mask volume }
    End;
    AY_EFINE,AY_ECOARSE:
    Begin
      CountEnv[Chip] := 0;
      AYRegs[AY_ESHAPE,Chip] := AYRegs[AY_ESHAPE,Chip] And $F;
    End;
    AY_ESHAPE:
    Begin
      CountEnv[Chip] := 0;
      AYRegs[AY_ESHAPE,Chip] := AYRegs[AY_ESHAPE,Chip] And $F;
    End;
  End; { Case }

  X := AYRegs[AY_AFINE,Chip] + (Word(AYRegs[AY_ACOARSE,Chip] And $F) Shl 8);
  If X <> 0 Then Incr0[Chip] := Trunc((65536 / AYSoundRate) * (AY8910_CLOCK / 16) / X) Else Incr0[Chip] := 0;

  X := AYRegs[AY_BFINE,Chip] + (Word(AYRegs[AY_BCOARSE,Chip] And $F) Shl 8);
  If X <> 0 Then Incr1[Chip] := Trunc((65536 / AYSoundRate) * (AY8910_CLOCK / 16) / X) Else Incr1[Chip] := 0;

  X := AYRegs[AY_CFINE,Chip] + (Word(AYRegs[AY_CCOARSE,Chip] And $F) Shl 8);
  If X <> 0 Then Incr2[Chip] := Trunc((65536 / AYSoundRate) * (AY8910_CLOCK / 16) / X) Else Incr2[Chip] := 0;

  X := AYRegs[AY_NOISEPER,Chip] And $1F;
  If X <> 0 Then IncrNoise[Chip] := Trunc((65536 / AYSoundRate) * (AY8910_CLOCK / 16) / X) Else IncrNoise[Chip] := 0;

  X := AYRegs[AY_EFINE,Chip] + (Word(AYRegs[AY_ECOARSE,Chip]) Shl 8);
  If X <> 0
   Then IncrEnv[Chip] := Trunc(($200000 / AYSoundRate) * (AY8910_CLOCK / 256) / X)
   Else IncrEnv[Chip] := Trunc(($200000 / AYSoundRate) * (AY8910_CLOCK / 256) / 1);
End; { WriteToPSG }

Procedure InitSound(Playback_Freq,Chips: Word; Reset: Boolean);

  Procedure ExtractWord(Var St: String; Blaster: String; I: Word);
  Var Ch: Char;
  Begin
    St := '';
    Repeat
      Ch := Blaster[I];
      Inc(I);
      If Ch <> ' ' Then St := St + Ch;
    Until (Ch = ' ') Or (I > Length(Blaster));
  End; { ExtractWord }

Begin
  NumChips := Chips;
  SoundInit(Playback_Freq,Reset);
End; { InitSound }

Procedure StopSound;
Begin
  SoundRestore;
End; { StopSound }

Procedure InitDToA;
Var
  I   : Integer;
  J,K : Double;

Begin
  J := 1;
  K := 1;
  For I := 1 To 15 Do K := K * Sqrt(2);
  For I := 0 To 15 Do
  Begin
    DToA[I] := Round((J / K) * 31);
    J       := J * Sqrt(2);
  End; { For I }
End; { InitDToA }

Procedure InitVolScale;
Var I,J: Integer;
Begin
  For I := 0 To 16 Do
   For J := 0 To 31 Do VolScale[I,J] := Round(I * J / 16);
End; { InitVolScale }

Initialization
  InitDToA;
  InitVolScale;
End.


