{Copyright or © or Copr. :

CNRS - Laboratoire Mouvement & Perception
franck.buloup@univmed.fr

This software is a computer program whose purpose is to highly simplify  
the use of ADwinPro realtime systems when you develop experimental
protocols  on life's sciences area.

This software is governed by the CeCILL  license under French law and
abiding by the rules of distribution of free software.  You can  use, 
modify and/ or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info". 

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability. 

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or 
data to be ensured and,  more generally, to use and operate it in the 
same conditions as regards security. 

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.}
unit UStimuliAppl;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, StdCtrls, ExtCtrls, StimuliClass, GeneratorClass;

type
  TStimuliApplForm = class(TForm)
    PageControl1: TPageControl;
    CsteTabSheet: TTabSheet;
    RampeTabSheet: TTabSheet;
    SinusTabSheet: TTabSheet;
    NivTabSheet: TTabSheet;
    SigCarTabSheet: TTabSheet;
    SigCarModTabSheet: TTabSheet;
    Panel1: TPanel;
    AppliquerButton: TButton;
    DeCsteLE: TLabeledEdit;
    ACsteLE: TLabeledEdit;
    DeRampeLE: TLabeledEdit;
    Amp1RampeLE: TLabeledEdit;
    ARampeLE: TLabeledEdit;
    Amp2RampeLE: TLabeledEdit;
    Label1: TLabel;
    Label2: TLabel;
    DeSinusLE: TLabeledEdit;
    ASinusLE: TLabeledEdit;
    AmpSinusLE: TLabeledEdit;
    FreqSinusLE: TLabeledEdit;
    PhaseSinusLE: TLabeledEdit;
    CsteSinusLE: TLabeledEdit;
    DeNivLE: TLabeledEdit;
    ANivLE: TLabeledEdit;
    DeSigCarLE: TLabeledEdit;
    ASigCarLE: TLabeledEdit;
    FreqSigCarLE: TLabeledEdit;
    TpsInitSigCarLE: TLabeledEdit;
    AddCheckBox: TCheckBox;
    Label3: TLabel;
    AmpNivCBox: TComboBox;
    AmpCsteLE: TLabeledEdit;
    Label4: TLabel;
    InitSigCarCBox: TComboBox;
    DESigCarModLE: TLabeledEdit;
    ASigCarModLE: TLabeledEdit;
    FmaxSigCarModLE: TLabeledEdit;
    FminSigCarModLE: TLabeledEdit;
    DMinPallierSigCarModLE: TLabeledEdit;
    TaillePallierSigCarModLE: TLabeledEdit;
    DSilenceSigCarModLE: TLabeledEdit;
    GroupBox1: TGroupBox;
    Label5: TLabel;
    Label6: TLabel;
    DTotaleSigCarModLabel: TLabel;
    DmaxBipSigCarModLabel: TLabel;
    TrackBar1: TTrackBar;
    Label7: TLabel;
    PercentLabel: TLabel;
    DbipSigCarModLabel: TLabel;
    ResultListBox: TListBox;
    procedure LabeledKeyPress(Sender: TObject; var Key: Char);
    procedure AppliquerButtonClick(Sender: TObject);
    procedure SigCarModLEChange(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Déclarations privées }
    procedure AppliquerCste;
    procedure AppliquerRampe;
    procedure AppliquerSinus;
    procedure AppliquerNiv;
    procedure AppliquerSigCar;
    procedure AppliquerSigCarMod;
  public
    { Déclarations publiques }
    CurentStimulus : TStimulus;
    CurentGenerator : TGenerator;
    Duree_Stim : double;
    CalledFromGenerator : boolean;
    procedure ImportFromTextFile(FileName : string);
    procedure ImportFromBinFile(FileName : string);
  end;

var
  StimuliApplForm : TStimuliApplForm;
  F,DureePalier : array of double;
  NBPaliers : integer;
  DE,A,DureeBip,DureeReelle : double;

implementation

uses UGenerator;

{$R *.dfm}

procedure TStimuliApplForm.LabeledKeyPress(Sender: TObject;
  var Key: Char);
begin
  if not(key in ['0'..'9','.','-',Char(VK_BACK),Char(VK_RETURN)]) then key := #0;
  if Key = Char(VK_RETURN) then AppliquerButtonClick(Self);
end;

procedure TStimuliApplForm.AppliquerButtonClick(Sender: TObject);
var SaveCursor : TCursor;
		Ok : boolean;
begin
	Ok := ((not CalledFromGenerator) and (CurentStimulus <> nil)) or
				(CalledFromGenerator and (CurentGenerator <> nil));
	if Ok then
	begin
	SaveCursor := Screen.Cursor;
		try
			Screen.Cursor := crHourGlass;
			try
				if PageControl1.ActivePage = CsteTabSheet then AppliquerCste;
				if PageControl1.ActivePage = RampeTabSheet then AppliquerRampe;
				if PageControl1.ActivePage = SinusTabSheet then AppliquerSinus;
				if PageControl1.ActivePage = NivTabSheet then AppliquerNiv;
				if PageControl1.ActivePage = SigCarTabSheet then AppliquerSigCar;
				if PageControl1.ActivePage = SigCarModTabSheet then AppliquerSigCarMod;
			except
				on E : EConvertError
				do ShowMessage('Erreur de convertion : ' + E.ClassName + #10 + E.Message);
				on E : ERangeError
				do ShowMessage('Erreur d''étendue : ' + E.ClassName + #10 + E.Message);
			end;
		finally
			Screen.Cursor := SaveCursor;
			if not CalledFromGenerator then CurentStimulus.Modified := true;
		end;
	end;
end;

procedure TStimuliApplForm.AppliquerCste;
var DE,A,AMP : double;
    i : integer;
begin
  DE := strtofloat(DeCsteLE.Text);
  A  := strtofloat(ACsteLE.Text);
  AMP := strtofloat(AmpCsteLE.Text);
  if A > Duree_Stim then A := Duree_Stim;
  if (DE <= A) then
  begin
    if not CalledFromGenerator then
    begin
      for i := trunc(CurentStimulus.Fe*DE/1000) to trunc(CurentStimulus.Fe*A/1000) do
      begin
        if AddCheckBox.Checked
        then CurentStimulus.YValue[i] := CurentStimulus.YValue[i] + AMP
				else CurentStimulus.YValue[i] := AMP;
      end;
    end else
    begin
      for i := trunc(CurentGenerator.Fe*DE/1000) to trunc(CurentGenerator.Fe*A/1000) do
      begin
        if AddCheckBox.Checked
				then CurentGenerator.Values[i] := CurentGenerator.Values[i] + AMP
        else CurentGenerator.Values[i] := AMP;
      end;
      GeneratorForm.MAJIHM;
    end;
  end;
end;

procedure TStimuliApplForm.AppliquerRampe;
var DE,A,AMP1,AMP2,temp : double;
    i : integer;
begin
  DE := strtofloat(DeRampeLE.Text);
  A  := strtofloat(ARampeLE.Text);
  AMP1 := strtofloat(Amp1RampeLE.Text);
  AMP2 := strtofloat(Amp2RampeLE.Text);
  if A > Duree_Stim then A := Duree_Stim;
  if (DE <= A) then
  begin
    if not CalledFromGenerator then
    begin
      for i := trunc(CurentStimulus.Fe*DE/1000) to trunc(CurentStimulus.Fe*A/1000) do
      begin
        temp := (AMP2-AMP1)*1000/(A-DE)*i/CurentStimulus.Fe + (AMP1*A-AMP2*DE)/(A-DE);
        if AddCheckBox.Checked
        then CurentStimulus.YValue[i] := CurentStimulus.YValue[i] + temp
        else CurentStimulus.YValue[i] := temp;
      end;
    end else
    begin
      for i := trunc(CurentGenerator.Fe*DE/1000) to trunc(CurentGenerator.Fe*A/1000) do
      begin
        temp := (AMP2-AMP1)*1000/(A-DE)*i/CurentGenerator.Fe + (AMP1*A-AMP2*DE)/(A-DE);
        if AddCheckBox.Checked
        then CurentGenerator.Values[i] := CurentGenerator.Values[i] + temp
        else CurentGenerator.Values[i] := temp;
      end;
      GeneratorForm.MAJIHM;
    end;
  end;
end;

procedure TStimuliApplForm.AppliquerSinus;
var DE,A,AMP,FREQ,PHASE,CSTE,temp : double;
    i : integer;
begin
  DE := strtofloat(DeSinusLE.Text);
  A  := strtofloat(ASinusLE.Text);
  AMP := strtofloat(AmpSinusLE.Text);
  FREQ := strtofloat(FreqSinusLE.Text);
  PHASE := strtofloat(PhaseSinusLE.Text);
  CSTE := strtofloat(CsteSinusLE.Text);
  if A > Duree_Stim then A := Duree_Stim;
  if (DE <= A) then
  begin
    if not CalledFromGenerator then
    begin
      for i := trunc(CurentStimulus.Fe*DE/1000) to trunc(CurentStimulus.Fe*A/1000) do
      begin
        temp := AMP*sin(2*4*arctan(1)*FREQ*(i/CurentStimulus.Fe-DE/1000)+PHASE);
        temp := CSTE + temp;
        if AddCheckBox.Checked
        then CurentStimulus.YValue[i] := CurentStimulus.YValue[i] + temp
        else CurentStimulus.YValue[i] := temp;
      end;
    end else
    begin
      for i := trunc(CurentGenerator.Fe*DE/1000) to trunc(CurentGenerator.Fe*A/1000) do
      begin
        temp := AMP*sin(2*4*arctan(1)*FREQ*(i/CurentGenerator.Fe-DE/1000)+PHASE);
        temp := CSTE + temp;
        if AddCheckBox.Checked
        then CurentGenerator.Values[i] := CurentGenerator.Values[i] + temp
        else CurentGenerator.Values[i] := temp;
      end;
      GeneratorForm.MAJIHM;
    end;
  end;
end;

procedure TStimuliApplForm.AppliquerNiv;
var DE,A,AMP : double;
    i : integer;
begin
  DE := strtofloat(DeNivLE.Text);
  A  := strtofloat(ANivLE.Text);
  AMP := strtofloat(AmpNivCBox.Text);
  if A > Duree_Stim then A := Duree_Stim;
  if (DE <= A) then
  begin
    if not CalledFromGenerator then
    begin
      for i := trunc(CurentStimulus.Fe*DE/1000) to trunc(CurentStimulus.Fe*A/1000)
      do CurentStimulus.YValue[i] := AMP;
    end else
    begin
      for i := trunc(CurentGenerator.Fe*DE/1000) to trunc(CurentGenerator.Fe*A/1000)
      do CurentGenerator.Values[i] := AMP;
      GeneratorForm.MAJIHM;
    end;
  end;
end;

procedure TStimuliApplForm.AppliquerSigCar;
var DE,A,FREQ,VALINIT,VALFIN,TPSINIT : double;
    i : integer;
begin
  DE := strtofloat(DeSigCarLE.Text);
  A  := strtofloat(ASigCarLE.Text);
  FREQ := strtofloat(FreqSigCarLE.Text);
  VALINIT := strtofloat(InitSigCarCBox.Text);
  if VALINIT = 0 then VALFIN := 1 else VALFIN := 0;
  TPSINIT := strtofloat(TpsInitSigCarLE.Text);
  if A > Duree_Stim then A := Duree_Stim;
  if (DE <= A) then
  begin
    if not CalledFromGenerator then
    begin
      for i := trunc(CurentStimulus.Fe*DE/1000) to trunc(CurentStimulus.Fe*A/1000) do
      begin
        if (frac(i*FREQ/CurentStimulus.Fe)<=(TPSINIT*FREQ/1000))
        then CurentStimulus.YValue[i] := VALINIT
        else CurentStimulus.YValue[i] := VALFIN;
      end;
    end else
    begin
      for i := trunc(CurentGenerator.Fe*DE/1000) to trunc(CurentGenerator.Fe*A/1000) do
      begin
        if (frac(i*FREQ/CurentGenerator.Fe)<=(TPSINIT*FREQ/1000))
        then CurentGenerator.Values[i] := VALINIT
        else CurentGenerator.Values[i] := VALFIN;
      end;
      GeneratorForm.MAJIHM;
    end;
  end;
end;

procedure TStimuliApplForm.SigCarModLEChange(Sender: TObject);
var OK : boolean;
    Fmax,Fmin,Fpalier,DminPalier,DSilence,DMaxBip : double;
    i,j : integer;
    NbBip : array of double;
begin
  OK := (DESigCarModLE.Text <> '');
  OK := OK and (ASigCarModLE.Text <> '');
  OK := OK and (FMinSigCarModLE.Text <> '');
  OK := OK and (FMaxSigCarModLE.Text <> '');
  OK := OK and (TaillePallierSigCarModLE.Text <> '');
  OK := OK and (DMinPallierSigCarModLE.Text <> '');
  OK := OK and (DSilenceSigCarModLE.Text <> '');
  if OK then
  begin
    DE := strtofloat(DESigCarModLE.Text);
    A := strtofloat(ASigCarModLE.Text);
    if A > Duree_Stim then A := Duree_Stim;
    
    DMaxBip := strtofloat(FmaxSigCarModLE.Text);
    DMaxBip := 1000/(2*DMaxBip);
    DmaxBipSigCarModLabel.Caption := floattostr(DMaxBip);

    //Fmax,Fmin,Palier,NBPAlier
    Fmin := strtofloat(FMinSigCarModLE.Text);
    Fmax := strtofloat(FMaxSigCarModLE.Text);
    Fpalier := strtofloat(TaillePallierSigCarModLE.Text);
    NBPaliers := trunc((Fmax-Fmin)/Fpalier)+1;
    NBPaliers := 2*NBPaliers-1;

    //duree min palier et duree silence
    DminPalier := strtofloat(DMinPallierSigCarModLE.Text)/1000.0;
    DSilence := strtofloat(DSilenceSigCarModLE.Text)/1000.0;

    if (DE <= A) then
    begin
      SetLength(F,NBPaliers);
      SetLength(NbBip,NBPaliers);
      SetLength(DureePalier,NBPaliers);
      DureeReelle := (A-DE)/1000.0;
      ResultListBox.Clear;

      for i := 0 to NBPaliers - 1 do
      begin
        if i mod 2 = 0 then
        begin
          j := (i div 2) + (i mod 2);
          F[i] := Fmin + j*Fpalier;
          DureePalier[i] := round(DminPalier*F[i]+0.4999)/F[i];
          NbBip[i] := DureePalier[i]*F[i];
          ResultListBox.Items.Add('Le palier à '+floattostr(F[i])+' Hz dure '+floattostr(DureePalier[i]) + ' s');
          ResultListBox.Items.Add('Il y a '+floattostr(NBBip[i])+' bips dans ce palier');
          ResultListBox.Items.Add('-----');
        end else
        begin
          F[i] := 0;
          DureePalier[i] := DSilence;
          NbBip[i] := 0;
        end;
      end;
      for i := 1 to NBPaliers - 1 do DureePalier[i] := DureePalier[i-1]+DureePalier[i];
      DureeReelle := DureePalier[NBPaliers - 1];
      DTotaleSigCarModLabel.Caption := floattostr(DureeReelle*1000);
      A := DE + 1000*DureeReelle;
      TrackBAr1.Position := 100;
      TrackBAr1.SelEnd := TrackBAr1.Position;
      DbipSigCarModLabel.Caption := floattostr(TrackBar1.Position/100*strtofloat(DmaxBipSigCarModLabel.Caption))+ ' ms';
      DureeBip := TrackBar1.Position/100*strtofloat(DmaxBipSigCarModLabel.Caption)/1000;

    end;
    NbBip := nil;
  end;
end;

procedure TStimuliApplForm.TrackBar1Change(Sender: TObject);
begin
  TrackBar1.SelEnd := TrackBar1.Position;
  DbipSigCarModLabel.Caption := floattostr(TrackBar1.Position/100*strtofloat(DmaxBipSigCarModLabel.Caption))+ ' ms';
  DureeBip := TrackBar1.Position/100*strtofloat(DmaxBipSigCarModLabel.Caption)/1000;
end;

procedure TStimuliApplForm.AppliquerSigCarMod;
var i : integer;

    function WhatStep(t : double) : integer;
    var m : integer;
    begin
      m := 0;
      while(t>=DureePalier[m]) do
      begin
        inc(m);
        if m=NBPaliers then Break;
      end;
      result := m;
    end;

    function Signal(t : double):double;
    var DT,MemTemps : double;
        m : integer;
    begin
      m := WhatStep(t);
      if m>0 then MemTemps := DureePalier[m-1] else MemTemps := 0;
      DT := t - MemTemps;
      //if (DT-trunc(DT*Freq(t))/Freq(t))<1/(2*Freq(t))
      if m<NBPaliers then
      begin
        if F[m] <> 0 then
        begin
          if (DT-trunc(DT*F[m])/F[m])<DureeBip
          then Result := 1
          else Result := 0;
        end else Result :=0;
      end else Result := 0;
    end;

begin
  if not CalledFromGenerator then
  begin
    for i := trunc(CurentStimulus.Fe*DE/1000) to trunc(CurentStimulus.Fe*A/1000)
    do CurentStimulus.YValue[i] := Signal(i/CurentStimulus.Fe - DE/1000.0);
  end else
  begin
    for i := trunc(CurentGenerator.Fe*DE/1000) to trunc(CurentGenerator.Fe*A/1000)
    do CurentGenerator.Values[i] := Signal(i/CurentGenerator.Fe - DE/1000.0);
    GeneratorForm.MAJIHM;
  end;
end;

procedure TStimuliApplForm.ImportFromTextFile(FileName : string);
begin
  if not CalledFromGenerator
  then CurentStimulus.ImportFromTextFile(FileName)
  else begin
    CurentGenerator.ImportFromTextFile(FileName);
    GeneratorForm.MAJIHM;
  end;
end;

procedure TStimuliApplForm.ImportFromBinFile(FileName : string);
begin
  if not CalledFromGenerator
  then CurentStimulus.ImportFromBinFile(FileName)
  else begin
    CurentGenerator.ImportFromBinFile(FileName);
    GeneratorForm.MAJIHM;
  end;
end;

procedure TStimuliApplForm.FormClose(Sender: TObject;
  var Action: TCloseAction);  
begin
  F := nil;
  DureePalier := nil;
  CalledFromGenerator := false;
end;

procedure TStimuliApplForm.FormCreate(Sender: TObject);
begin
  CalledFromGenerator := false;
end;

end.
