unit MainUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Menus, StdCtrls, ComCtrls, LibV, ToneEdDlg, SWEdDlg, ProcMath;

type
  TMainForm = class(TForm)
    GBTones: TGroupBox;
    MainMenu1: TMainMenu;
    N1: TMenuItem;
    N2: TMenuItem;
    LVTones: TListView;
    GBWindows: TGroupBox;
    N3: TMenuItem;
    N4: TMenuItem;
    N5: TMenuItem;
    N6: TMenuItem;
    N7: TMenuItem;
    N8: TMenuItem;
    N9: TMenuItem;
    N10: TMenuItem;
    ProcessMenu: TMenuItem;
    StartItem: TMenuItem;
    StopItem: TMenuItem;
    N14: TMenuItem;
    N15: TMenuItem;
    LVWindows: TListView;
    GroupBox1: TGroupBox;
    CBPad: TCheckBox;
    CBFixUp: TCheckBox;
    procedure N2Click(Sender: TObject);
    procedure N4Click(Sender: TObject);
    procedure N8Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ProcessMenuClick(Sender: TObject);
    procedure StartItemClick(Sender: TObject);
    procedure StopItemClick(Sender: TObject);
  private
    Processing      : vboolean;
    Frequencies     : vector_p;
    Amplitudes      : vector_p;
    Channel1Window  : vector_p;
    Channel2Window  : vector_p;
    procedure AddTone(freq,amp:vec_t;enabled:boolean);
    procedure AddWin(lfreq,hfreq:vec_t;channel:integer);
    procedure InitialState;
    procedure FreeAllVectors;
    procedure ApplyWindowSpan(Win:vector_p;freq_lo,freq_hi:vec_t);
    function  GatherInterfaceData(window_samples:u32_t):vboolean;
    procedure OnIdle(Sender:TObject;var Done:Boolean);
    function  StartProcessing:vboolean;
    procedure StopProcessing;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.DFM}
{$OPTIMIZATION OFF}

procedure TMainForm.OnIdle(Sender:TObject;var Done:Boolean);
begin
  ProcMath.IdleProc(Processing,Done);
end;

function TMainForm.StartProcessing:vboolean;
begin
  if Processing then
  begin
    Result := True;
    Exit;
  end;
  Result := GatherInterfaceData(ProcMath.INBLK_SIZE);
  if not Result then
    Exit;
  if ProcMath.BeginProcessing(Frequencies,Amplitudes,Channel1Window,
     Channel2Window,CBPad.Checked,CBFixUp.Checked) then
  begin
    LVTones.Enabled := False;
    LVWindows.Enabled := False;
  end
  else
    Result := False;
end;

procedure TMainForm.StopProcessing;
begin
  if not Processing then
    Exit;
  ProcMath.EndProcessing;
  LVTones.Enabled := True;
  LVWindows.Enabled := True;
  Processing := False;
end;

procedure TMainForm.ApplyWindowSpan(Win:vector_p;freq_lo,freq_hi:vec_t);
var
  wlen, i, sl, sh : integer;
  nf : vec_t;
begin
  nf := 22050;
  wlen := Win^.size shr 1;
  sl := trunc(wlen * freq_lo / nf);
  sh := 1+trunc(wlen * freq_hi / nf);
  if sl < 0 then
    sl := 0;
  if sh >= wlen then
    sh := wlen-1;
  if sh < sl then
    sh := sl;
  // writeln('SPAN: wlen: ',wlen,'; lo: ',sl,'; hi: ',sh);
  wlen := (wlen shl 1) - 1;
  for i:=sl to sh do
  begin
    Win^.elements^[i] := 1;
    Win^.elements^[wlen-i] := 1;
  end;
end;

function  TMainForm.GatherInterfaceData(window_samples:u32_t):vboolean;
var
  i, tones : u32_t;
  v : vector_p;
begin
  Result := False;
  FreeAllVectors;
  { count active tones }
  tones := 0;
  for i:=0 to LVTones.Items.Count-1 do
  begin
    if LVTones.Items[i].Checked then
      tones := tones + 1;
  end;
  if tones = 0 then
    Exit;
  { collect tones' parameters into 2 separate arrays }
  Frequencies := vecAlloc(tones);
  Amplitudes := vecAlloc(tones);
  tones := 0;
  for i:=0 to LVTones.Items.Count-1 do
  begin
    if LVTones.Items[i].Checked then
    begin
      v := vector_p(LVTones.Items[i].Data);
      Frequencies^.elements^[tones] := v^.elements^[0];
      Amplitudes^.elements^[tones] := v^.elements^[1];
      tones := tones + 1;
      // writeln(v^.elements^[0],v^.elements^[1]);
    end;
  end;
  { allocate windows }
  Channel1Window := vecAlloc(window_samples);
  Channel2Window := vecAlloc(window_samples);
  memZero(Channel1Window^.elements,window_samples * sizeof(vec_t));
  memZero(Channel2Window^.elements,window_samples * sizeof(vec_t));
  { apply windows sequentially }
  for i:=0 to LVWindows.Items.Count-1 do
  begin
    if not LVWindows.Items[i].Checked then
      continue;
    v := vector_p(LVWindows.Items[i].Data);
    if v^.elements^[2] > 0.5 then
      ApplyWindowSpan(Channel2Window,v^.elements^[0],v^.elements^[1])
    else
      ApplyWindowSpan(Channel1Window,v^.elements^[0],v^.elements^[1]);
  end;
  Result := True;
end;

procedure TMainForm.InitialState;
begin
  Frequencies := nil;
  Amplitudes := nil;
  Channel1Window := nil;
  Channel2Window := nil;
  Processing := False;
end;

procedure TMainForm.FreeAllVectors;
begin
  if Frequencies <> nil then
    memFree(Frequencies);
  if Amplitudes <> nil then
    memFree(Amplitudes);
  if Channel1Window <> nil then
    memFree(Channel1Window);
  if Channel2Window <> nil then
    memFree(Channel2Window);
  Frequencies := nil;
  Amplitudes := nil;
  Channel1Window := nil;
  Channel2Window := nil;
end;

procedure TMainForm.N2Click(Sender: TObject);
begin
  StopProcessing;
  Application.OnIdle := nil;
  Close;
end;

procedure TMainForm.AddTone(freq,amp:vec_t;enabled:boolean);
var
  fa : vector_p;
  NewItem : TListItem;
begin
  NewItem := LVTones.Items.Add;
  NewItem.Caption := FloatToStr(freq);
  NewItem.Checked := enabled;
  NewItem.Subitems.Add(FloatToStr(amp));
  fa := vecAlloc(2);
  NewItem.Data := fa;
  fa.elements^[0] := freq;
  fa.elements^[1] := amp;
end;

procedure TMainForm.AddWin(lfreq,hfreq:vec_t;channel:integer);
var
  lh : vector_p;
  NewItem : TListItem;
begin
  NewItem := LVWindows.Items.Add;
  NewItem.Caption := FloatToStr(lfreq);
  NewItem.Subitems.Add(FloatToStr(hfreq));
  NewItem.Subitems.Add(IntToStr(channel));
  NewItem.Checked := True;
  lh := vecAlloc(3);
  NewItem.Data := lh;
  lh.elements^[0] := lfreq;
  lh.elements^[1] := hfreq;
  lh.elements^[2] := channel;
end;

procedure TMainForm.N4Click(Sender: TObject);
begin
  if TEDialog.ShowModal = mrOk then
    AddTone(StrToFloat(TEDialog.EFreq.Text),
      StrToFloat(TEDialog.EAmp.Text),
      TEDialog.CBActive.Checked);
end;

procedure TMainForm.N8Click(Sender: TObject);
begin
  if WEDialog.ShowModal = mrOk then
    AddWin(StrToFloat(WEDialog.EFreqLow.Text),
      StrToFloat(WEDialog.EFreqHi.Text),
      WEDialog.SEChannel.Value);
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  InitialState;
  Application.OnIdle := Self.OnIdle;
end;

procedure TMainForm.ProcessMenuClick(Sender: TObject);
begin
  StartItem.Enabled := not Processing;
  StopItem.Enabled := Processing;
end;

procedure TMainForm.StartItemClick(Sender: TObject);
begin
  Processing := StartProcessing;
end;

procedure TMainForm.StopItemClick(Sender: TObject);
begin
  StopProcessing;
end;

end.

