Реализация эффекта «Флэнджер»
Как-то один начинающий программер-звукач спросил у меня :
- Васян, а можно-ли сделать на Delphi нормальный Флэнжер? А то скачал несколько исходников, и везде там звук косячный: треск какой-то время от времени!
- Сам когдато эти исходники материл (точнее их Авторов) но разобравшись в математике процесса, действительно выяснилось, что из-за неграмотной интерполяции и возникают эти косяки!

Ниже привожу листинг Флэнжера, дающего нормальный, без косяков звук (хотя можно в нём применить другой принцип LFO).
Обработку звука делает функция Process.
В приведённом листинге имеется параметр Phase, с помощью которого можно регулировать фазу LFO, если использовать стереовариант.
Перед коммутацией эффекта не забывайте обнулять линию задержки процедурой Flush.
Если изменить параметры констант линии задержки то можно получить эффект Хорус.

unit Flange;

interface

const
MaxFLABUFLEN = 4000;
FLABUFLEN = 400;
pi2 = 2*pi;
pid2 = pi/2;
pi32 = 3*pid2;

type

  TFlanger = class(TObject)
    private
      fFLLine: array[0..MaxFLABUFLEN-1] of Single;
      fDelay: Single;
      fDepth: Single;
      fLFOInc: Single;
      fLFOPhase: Single;
      fFeedBack: Single;
      fRate: Single;
      fCount: integer;
      fPhase: Single;
      fSampleRate: Single;
      fWet: Single;
      fdry: Single;
      procedure SetSampleRate(v:Single);
      procedure SetRate(v:Single);
      procedure SetPhase(v:Single);
      function Tri(x:single):single; //Функция треугольного LFO
    public
      constructor Create;
      destructor Destroy; override;
      procedure Flush;
      function Process(const v:single):single;
      property SampleRate : Single read fSampleRate write SetSampleRate;
      property Depth: Single read fDepth write fDepth; //0..1
      property Feedback: Single read fFeedback write fFeedback; // 0..<1
      property Rate: Single read fRate write SetRate;
      property Phase: Single read fPhase write SetPhase;
      property Wet: Single read fwet write fwet;
      property Dry: Single read fdry write fdry;
    end;

implementation

constructor TFlanger.Create;
begin
inherited;
fSampleRate:=44100;
fFeedBack:=0;
fLFOPhase:=0;
fDepth:=1;
fRate:=0.5;
Rate:=0.5;
fPhase:=0;
fCount:=0;
fwet:=1;
fdry:=1;
Flush;
end;

destructor TFlanger.Destroy;
begin
inherited;
end;

function TFlanger.Tri(x:single):single; 
begin
if x>=pi2 then x:=x-pi2*trunc(x/pi2);
if x<0 then x:=x+pi2*trunc(1-x/pi2);
result:=x*2/pi;
if x>pid2 then result:=2-result;
if x>pi32 then result:=-2-result;
end;

procedure TFlanger.Flush;
begin
ZeroMemory(@fFLLine,SizeOf(fFLLine));
end;

procedure TFlanger.SetRate(v:Single);
begin
fRate:=v;
fLFOInc:=2*Pi*((v+fPhase)/SampleRate);
end;

procedure TFlanger.SetPhase(v:Single);
begin
fPhase:=v;
SetRate(fRate);
end;

procedure TFlanger.SetSampleRate(v:Single);
begin
fSampleRate:=v;
end;

function TFlanger.Process(const v:single):single;
var
back:double;
index0,index_1,index1,index2:integer;
c0,c1,c2,c3,x,y_1,y0,y1,y2:single;
begin
fDelay:=FLABUFLEN/2+(FLABUFLEN-FLABUFLEN/2)*fDepth*((tri(fLFOPhase)+1)/2); //Сюда можно поставить любой другой LFO
fLFOPhase:=fLFOPhase+fLFOInc;
if fLFOPhase>=Pi*2
then fLFOPhase:=fLFOPhase-Pi*2;
back:=fCount-fDelay;
if back<0.0 then back:=FLABUFLEN+back;
index0:=Trunc(back); //тут начинается самое ответственное - интерполяция
index_1:=index0-1;
index1:=index0+1;
index2:=index0+2;
if index_1<0 then index_1:=FLABUFLEN-1;
if index1>=FLABUFLEN then index1:=0;
if index2>=FLABUFLEN then index2:=0;
y_1:=fFLLine[index_1];
y0:=fFLLine[index0];
y1:=fFLLine[index1];
y2:=fFLLine[index2];
x:=back-index0;
c0:=y0;
c1:=0.5*(y1-y_1);
c2:=y_1-2.5*y0+2.0*y1-0.5*y2;
c3:=0.5*(y2-y_1)+1.5*(y0-y1);
result:=fwet*(((c3*x+c2)*x+c1)*x+c0)+v*fdry;
fFLLine[fCount]:=v+result*fFeedBack;
inc(fCount);
if fCount>=FLABUFLEN then fCount:=0;
end;

end.



Hosted by uCoz