unit loro_sc; interface // // C++ version (c) 2004 Russell Borogove / www.tinygod.com // Delphi Pascal Version ©2005, Thaddy de Koning / www.thaddy.com // // Lorenz/Rossler iterative function systems as LFOs // // // This module defines the classes TLorenzOsc and TRosslerOsc - low frequency // oscillators suitable for modeling 'analog drift' or other random-but-smooth // processes. Both classes have identical APIs - you could unify the interface // with virtual functions easily. // // SetSampleRate: // Sets the sample rate at which the Iterate function will be called. Only // meaningful in conjunction with the SetFreq function. // // SetFreq: // Sets the fundamental frequency of the oscillator. The Rossler oscillator // should exhibit harmonic peaks at multiples of that frequency; the Lorenz // oscillator has a linear frequency-amplitude relation, so SetFreq will // only control the scale of waveform features in a general way. // // Iterate: // Advances the clock by one sample period and returns the value of the // function at the current clock; it should be called once per sample-tick. // // GetCurrent: // Returns the same value returned by the latest call to Iterate. Useful // in cases where one generator modulates multiple destinations, for example. // // GetAlternate: // Returns a value separate from the current value but correlated with it; // these are the X and Y values used for the well-known "butterfly" plots // of the Lorenz and Rossler functions. You can use GetAlternate if you // want two separate LFOs which are related in mysterious ways at a low // cost - for example, you can fine-tune one audio oscillator with the return // from Iterate and another oscillator with the return from GetAlternate. // // Both the primary and alternate returns are calibrated to a -1.0 to +1.0 // range in normal usage. The implementation is discrete, though, so if the // sample rate is low or the frequency high, it may occasionally jump outside // that range -- the user is responsible for clamping if the range is // critical. // // // Lorenz function - very broad spectrum noise function with amplitude // decreasing with increasing frequency, but tight short-term correlation. // // The scale of waveform features will change somewhat with the set frequency // and sample rate, but not drastically - it's fairly fractal. In particular, // there will not be substantial spectral peaks at multiples of the frequency // selected by SetFreq. // uses {$IFDEF KOL} Kol{$ELSE} SysUtils{$ENDIF}; const LORENZ_SCALE:Single = 0.05107; LORENZ_ALT_SCALE:Single = 0.03679; type {$IFDEF KOL} PLorenzOsc = ^TLorenzOsc; TLorenzOsc = object(Tobj) {$ELSE} TLorenzOsc = class {$ENDIF} private mDX:Single; mDY:Single; mDZ:Single; mDT:Single; mFreq:Single; mX:Single; mY:Single; mZ:Single; mA:Single; mB:Single; mC:Single; mRate:Single; public {$IFDEF KOL} procedure init;virtual; {$ELSE} constructor create;virtual; {$ENDIF} procedure SetSampleRate(rate:Single ); procedure SetFreq(freq:Single ); function GetCurrent:Single; function GetAlternate:Single; function Iterate:Single; end; // // Rossler function - broad spectrum noise function with amplitude // decreasing with increasing frequency, and distinct harmonic peaks. The // peaks should occur at harmonics of the frequency set by SetFreq. // const ROSSLER_SCALE:Single = 0.05757; ROSSLER_ALT_SCALE:Single = 0.06028; type {$IFDEF KOL} PRosslerOsc = ^TRosslerOsc; TRosslerOsc = object(Tobj) {$ELSE} TRosslerOsc = class {$ENDIF} private mDX:Single; mDY:Single; mDZ:Single; mDT:Single; mFreq:Single; mX:Single; mY:Single; mZ:Single; mA:Single; mB:Single; mC:Single; mRate:Single; public {$IFDEF KOL} procedure init;virtual; {$ELSE} constructor create;virtual; {$ENDIF} procedure SetSampleRate(rate:Single ); procedure SetFreq(freq:Single); function GetCurrent:Single; function GetAlternate:Single; function Iterate:Single; end; {$IFDEF KOL} function NewLorenzOsc:PLorenzOsc; function NewRosslerOsc:PRosslerOsc; {$ENDIF} implementation {$IFDEF KOL} function NewLorenzOsc:PLorenzOsc; begin New(Result,Create); end; function NewRosslerOsc:PRosslerOsc; begin New(Result,Create); end; {$ENDIF} {$IFDEF KOL} procedure TLorenzOsc.init; {$ELSE} constructor Create;virtual; {$ENDIF} begin inherited; mA := 10.0; mB := 28.0; mC := 2.666; mDX := 0; mDY := 0; mDZ := 0; mX := 1; mY := 1; mZ := 1; mFreq := 440; SetSampleRate(44100); SetFreq(440); end; procedure TLorenzOsc.SetSampleRate(rate:Single ); begin mRate := rate; mDT := mFreq / rate; end; procedure TLorenzOsc.SetFreq(freq:Single ); begin mFreq := freq; mDT := freq / mRate; end; function TLorenzOsc.GetCurrent:Single; begin Result:= mX * LORENZ_SCALE; end; function TLorenzOsc.GetAlternate:Single; begin Result:= mY * LORENZ_ALT_SCALE; end; function TLorenzOsc.Iterate:Single; begin mDX := mA * (mY-mX); mDY := mX * (mB-mZ) - mY; mDZ := mX * mY - mC * mZ; mX := mX + mDX * mDT; mY := mY + mDY * mDT; mZ := mZ + mDZ * mDT; Result:= mX * LORENZ_SCALE; end; {$IFDEF KOL} procedure TRosslerOsc.init; {$ELSE} constructor TRosslerOsc.create;virtual; {$ENDIF} begin inherited; mA := 0.15; mB := 0.20; mC := 10; mDX := 0; mDY := 0; mDZ := 0; mX := 1; mY := 1; mZ := 1; mFreq := 440; SetSampleRate( 44100); SetFreq( 440); end; procedure TRosslerOsc.SetSampleRate(rate:Single ); begin mRate := rate; mDT := 2.91 * mFreq / rate; end; procedure TRosslerOsc.SetFreq(freq:Single); begin mFreq := freq; mDT := 2.91 * freq / mRate; end; function TRosslerOsc.GetCurrent:Single; begin Result:= mX * ROSSLER_SCALE; end; function TRosslerOsc.GetAlternate:Single; begin Result:= mY * ROSSLER_ALT_SCALE; end; function TRosslerOsc.Iterate:Single; begin mDX := -mY - mZ; mDY := mX + mA * mY; mDZ := mB + mZ * (mX - mC); mX := mX + mDX * mDT; mY := mY + mDY * mDT; mZ := mZ + mDZ * mDT; Result := mX * ROSSLER_SCALE; end; end.