unit PALFO;
//
// purpose: LUT based LFO
//  author: © 2004, Thaddy de Koning
// Remarks: Translated from c++ sources by Remy Mueller, www.musicdsp.org
//          Since it is based on a lookup table you can easily add any waveform
//          you like.

interface
uses
{$IFDEF KOL}
  Windows, Kol,KolMath;
{$ELSE}
  Windows, math;
{$ENDIF}

const
 k1Div24lowerBits = 1/(1 shl 24);

 WFStrings:array[0..4] of string =
 ('triangle','sinus', 'sawtooth', 'square', 'exponent');

type
   Twaveform = (triangle, sinus, sawtooth, square, exponent);



{$IFDEF KOL}
   PPaLfo = ^TPALfo;
   TPaLfo = object(TObj)
{$ELSE}
   TPaLfo = class
{$ENDIF}
   private
     FTable:array[0..256] of Single;// 1 more for linear interpolation
     FPhase,
     FInc:dword;
     FRate: Single;
     FSampleRate: Single;
     FWaveForm: TWaveForm;
     procedure SetRate(const Value: Single);
     procedure SetSampleRate(const Value: Single);
     procedure SetWaveForm(const Value: TWaveForm);
   public
{$IFNDEF KOL}
     constructor create(SampleRate:Single);virtual;
{$ENDIF}
      // increments the phase and outputs the new LFO value.
      // return the new LFO value between [-1;+1]
     function WaveformName:String;
     function Tick:Single;
     // The rate in Hz
     property Rate:Single read FRate write SetRate;
     // The Samplerate
     property SampleRate:Single read FSampleRate write SetSampleRate;
     property WaveForm:TWaveForm read FWaveForm write SetWaveForm;
   end;

{$IFDEF KOL}
function NewPaLfo(aSamplerate:Single):PPaLfo;
{$ENDIF}

implementation

{ TPaLfo }
{$IFDEF KOL}
function NewPaLfo(aSamplerate:Single):PPaLfo;
begin
  New(Result,Create);
  with Result^ do
  begin
    FPhase:=0;
    FSamplerate:=aSamplerate;
    SetWaveform(sinus);
    Rate:=1;
  end;
end;
{$ELSE}
constructor TPaLfo.create(SampleRate: Single);
begin
    inherited create;
    FPhase:=0;
    FSamplerate:=aSamplerate;
    SetWaveform(sinus);
    FRate:=1;
end;
{$ENDIF}


procedure TPaLfo.SetRate(const Value: Single);
begin
  FRate := Value;
  // the rate in Hz is converted to a phase increment with the following formula
  // f[ inc = (256*rate/samplerate) * 2^24]
  Finc :=  round((256 * Frate / Fsamplerate) * (1 shl 24));
end;

procedure TPaLfo.SetSampleRate(const Value: Single);
begin
  FSampleRate := Value;
end;

procedure TPaLfo.SetWaveForm(const Value: TWaveForm);
var
  i:integer;
begin
  FWaveForm := Value;
  Case Fwaveform of
  sinus:
	      for i:=0 to 256 do
	        FTable[i] := sin(2*pi*(i/256));
  triangle:
      begin
	      for i:=0 to 63 do
        begin
	        FTable[i] := i / 64;
	        FTable[i+64] :=(64-i) / 64;
	        FTable[i+128] := - i / 64;
	        FTable[i+192] := - (64-i) / 64;
	      end;
	      FTable[256] := 0;
	    end;
  sawtooth:
      begin
	      for i:=0 to 255 do
	        FTable[i] := 2*(i/255) - 1;
	      FTable[256] := -1;
      end;
  square:
      begin
	      for i:=0 to 127 do
	      begin
	        FTable[i]     :=  1;
	        FTable[i+128] := -1;
	      end;
	      FTable[256] := 1;
      end;
  exponent:
      begin
	      // symetric exponent similar to triangle
	      for i:=0 to 127 do
        begin
	        FTable[i] := 2 * ((exp(i/128) - 1) / (exp(1) - 1)) - 1  ;
	        FTable[i+128] := 2 * ((exp((128-i)/128) - 1) / (exp(1) - 1)) - 1  ;
	      end;
	      FTable[256] := -1;
      end;
  end;
end;

function TPaLfo.WaveformName:String;
begin
  result:=WFStrings[Ord(Fwaveform)];
end;



function TPaLfo.Tick: Single;
var
  i:integer;
  frac:Single;
begin
  // the 8 MSB are the index in the table in the range 0-255
  i := Fphase shr 24;
  // and the 24 LSB are the fractionnal part
  frac := (Fphase and $00FFFFFF) * k1Div24lowerBits;
  // increment the phase for the next tick
  Fphase :=FPhase + Finc; // the phase overflows itself
  Result:= Ftable[i]*(1-frac) + Ftable[i+1]* frac; // linear interpolation
end;

end.



