UNIT TB303;

INTERFACE

CONST ENVINC=64;

TYPE TTB303=CLASS
      PRIVATE
       SampleRate:DOUBLE;
       CurrentCounter:INTEGER;
       MaxCounterValue:INTEGER;
       Mode:INTEGER;
       EnvPos:INTEGER;
       Waveform:INTEGER;
       IncValue:SINGLE;
       IncSrc:SINGLE;
       IncDest:SINGLE;
       InternalCounter:SINGLE;
       CutOff:SINGLE;
       EnvMod:SINGLE;
       EnvDecayInfo:SINGLE;
       EnvDecay:SINGLE;
       Resonance:SINGLE;
       ResonanceCoeff:SINGLE;
       GlideSpeed:SINGLE;
       AccAmt:SINGLE;
       Attack:SINGLE;
       Decay:SINGLE;
       A0:SINGLE;
       A1:SINGLE;
       A2:SINGLE;
       B1:SINGLE;
       C0:SINGLE;
       E0:SINGLE;
       E1:SINGLE;
       D1:SINGLE;
       D2:SINGLE;
       PROCEDURE SetCutOff(ACutOff:SINGLE);
       PROCEDURE SetResonance(AResonance:SINGLE);
       PROCEDURE SetEnvMod(AEnvMod:SINGLE);
       PROCEDURE SetEnvDecay(AEnvDecay:SINGLE);
       PROCEDURE ResetValues;
      PUBLIC
       CONSTRUCTOR Create(ASampleRate:DOUBLE);
       DESTRUCTOR Destroy; OVERRIDE;
       FUNCTION Process(InputValue:SINGLE):SINGLE;
       PROCEDURE NoteOn(Note:INTEGER;Acc,Glide:BOOLEAN);
       PROCEDURE NoteOff;
       PROPERTY SampleRate:DOUBLE READ SampleRate WRITE SampleRate;
       PROPERTY Waveform:INTEGER READ Waveform WRITE Waveform;
       PROPERTY GlideSpeed:SINGLE READ GlideSpeed WRITE GlideSpeed;
       PROPERTY Cutoff:SINGLE READ CutOff WRITE SetCutOff;
       PROPERTY Resonance:SINGLE READ Resonance WRITE SetResonance;
       PROPERTY EnvMod:SINGLE READ EnvMod WRITE SetEnvMod;
       PROPERTY EnvDecay:SINGLE READ EnvDecay WRITE SetEnvDecay;
       PROPERTY AccAmt:SINGLE READ AccAmt WRITE AccAmt;
     END;

IMPLEMENTATION

FUNCTION POW(Nummer,Exponent:EXTENDED):EXTENDED;
BEGIN
 RESULT:=EXP(Exponent*LN(Nummer));
END;

CONSTRUCTOR TTB303.Create(ASampleRate:DOUBLE);
BEGIN
 INHERITED Create;
 SampleRate:=ASampleRate;
 IncDest:=(440/SampleRate);
 IncValue:=IncDest;
 Waveform:=0;
 InternalCounter:=0;
 CutOff:=0;
 EnvMod:=0;
 Resonance:=0;
 IncDest:=0;
 EnvDecayInfo:=0;
 EnvPos:=ENVINC;
 A0:=0.5;
 A1:=0;
 A2:=0;
 B1:=0;
 C0:=0;
 D1:=0;
 D2:=0;
 E0:=0;
 E1:=0;
 Mode:=2;
 ResonanceCoeff:=1;
 Attack:=1-0.94406088;
 Decay:=0.99897516;
 AccAmt:=0.5;
 GideSpeed:=0.1;
 SetCutOff(0.9);
 SetResonance(0.1);
 SetEnvMod(1);
 SetEnvDecay(0.1);
 MaxCounterValue:=1;
END;

DESTRUCTOR TTB303.Destroy;
BEGIN
 INHERITED Destroy;
END;

PROCEDURE TTB303.SetCutOff(ACutOff:SINGLE);
BEGIN
 CutOff:=ACutOff;
 ResetValues;
END;

PROCEDURE TTB303.SetResonance(AResonance:SINGLE);
BEGIN
 Resonance:=AResonance;
 ResonanceCoeff:=EXP(-1.20+3.455*Resonance);
 ResetValues;
END;

PROCEDURE TTB303.SetEnvMod(AEnvMod:SINGLE);
BEGIN
 EnvMod:=AEnvMod;
 ResetValues;
END;

PROCEDURE TTB303.SetEnvDecay(AEnvDecay:SINGLE);
BEGIN
 EnvDecay:=AEnvDecay;
 EnvDecayInfo:=(0.2+(2.3*EnvDecay))*SampleRate;
 IF EnvDecayInfo<1 THEN EnvDecayInfo:=1;
 EnvDecayInfo:=POW(0.1,1/EnvDecayInfo*ENVINC);
END;

PROCEDURE TTB303.ResetValues;
BEGIN
 E1:=EXP(6.109+1.5876*EnvMod+2.1553*CutOff-1.2*(1.0-Resonance));
 E0:=EXP(5.613-0.8*EnvMod+2.1553*CutOff-0.7696*(1.0-Resonance));
 E0:=E0*PI/SampleRate;
 E1:=E1*PI/SampleRate;
 E1:=E1-E0;
 EnvPos:=ENVINC;
END;

FUNCTION TTB303.Process(InputValue:SINGLE):SINGLE;
VAR W,K,X:SINGLE;
    XCasted:LONGWORD ABSOLUTE X;
BEGIN
 IF EnvPos>=ENVINC THEN BEGIN
  W:=E0+C0;
  K:=EXP(-W/ResonanceCoeff);
  C0:=C0*EnvDecayInfo;
  A1:=2*COS(2*W)*K;
  B1:=-K*K;
  C0:=1-A1-B1;
  EnvPos:=0;
 END;
 IF Waveform>0 THEN BEGIN
  X:=InternalCounter;
  X:=LONGWORD((XCasted SHR 31) SHL 1);
  X:=((1-X)*0.5);
  RESULT:=A1*D1+B1*D2+C0*X*A2;
 END ELSE BEGIN
  RESULT:=A1*D1+B1*D2+C0*InternalCounter*A2;
 END;
 D2:=D1;
 EnvPos:=EnvPos+1;
 D1:=RESULT;

 INC(CurrentCounter);
 W:=CurrentCounter/MaxCounterValue;
 IF W<1 THEN BEGIN
  K:=IncSrc*(1-W)+W*IncDest;
 END ELSE BEGIN
  IncValue:=IncDest;
  K:=IncValue;
 END;

 InternalCounter:=InternalCounter+K;
 IF InternalCounter>0.5 THEN InternalCounter:=InternalCounter-1;

 CASE Mode OF
  0:A2:=A2+(A0-A2)*Attack;
  1:BEGIN
   A2:=A2*Decay;
   IF A2<(1/65536) THEN BEGIN
    A2:=0;
    Mode:=2;
   END;
  END;
  ELSE BEGIN
  END;
 END;
END;

PROCEDURE TTB303.NoteOn(Note:INTEGER;Acc,Glide:BOOLEAN);
BEGIN
 IncSrc:=IncValue;
 IncDest:=(440/SampleRate)*POW(2,(Note-57)*(1/12));
 MaxCounterValue:=ROUND(SampleRate*GlideSpeed);
 IF Glide THEN BEGIN
  CurrentCounter:=0;
 END ELSE BEGIN
  CurrentCounter:=MaxCounterValue-1;
 END;
 Mode:=0;
 C0:=E1;
 EnvPos:=ENVINC;
 A0:=0.5;
 IF Acc THEN A0:=A0+AccAmt*0.5;
END;

PROCEDURE TTB303.NoteOff;
BEGIN
 A2:=0;
 Mode:=2;
END;

END.
