unit uWaterFall;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, ComCtrls, AbCBitBt, Math, Mask, SDL_Math1, Buttons, Menus,
  SDL_NumLab, AbNumEdit, JPEG, FIleCtrl, SDL_sdlbase, _GClass, AbLED, uJPGComments, AbSwitch,
  SDL_stringl, uSETICommon, Vcl.WinXCtrls;

const

  PixelCountMax = 32800;
  CsbAutoSaveID = 0;
  CsbAutoSave = CsbAutoSaveID + 1;
  CsbSpace1 = CsbAutoSave + 1;

  CsbColorSystemID = CsbSpace1 + 1;
  CsbColorSystem = CsbColorSystemID + 1;
  CsbSpace2 = CsbColorSystem + 1;

  CsbLineID = CsbSpace2 + 1;
  CsbLineNum = CsbLineID + 1;
  CsbSpace3 = CsbLineNum + 1;

  CsbFrameID = CsbSpace3 + 1;
  CsbFrameNum = CsbFrameID + 1;
  CsbSpace4 = CsbFrameNum + 1;

  CsbFreqID = CsbSpace4 + 1;
  CsbFreq = CsbFreqID + 1;
  CsbSpace5 = CsbFreq + 1;

type
  TRGBTripleArray = ARRAY [Word] of TRGBTriple;
  pRGBTripleArray = ^TRGBTripleArray; // Use a PByteArray for pf8bit color.

type
  TfrmWaterFall = class(TForm)
    imageRGB: TImage;

    procedure imageRGBMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);

    procedure imageRGBMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure imageRGBMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure FormResize(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormActivate(Sender: TObject);

  private { Private declarations }

    LineNumber: Integer;
    BitMap: TBitMap;
    FrameComplete: boolean;
    // MouseStartX, MouseStartY: Integer;
    FRunning: boolean;
    IntegrationType: (NONE, MOVING); // 0 = None, 1 = Moving Average
    procedure setRun(value: boolean);
    function getRun: boolean;
    procedure setLineNumber(value: Integer);
    function getImageRGBWidth: Integer;
  public
    { Public declarations }

    ColumnAverage: array of Integer;
    Palette: array [1 .. 256] of TRGBTriple;
    FMouseFreq: Int64;

    procedure SaveJPG;
    procedure FillLine(Buffer: array of Integer);
    property MouseFreq: Int64 read FMouseFreq write FMouseFreq;

    property Run: boolean read getRun write setRun;
    property ImageRGBWidth: Integer read getImageRGBWidth;
  end;

var
  MouseStartX, MouseStartY: Integer;
  frmWaterFall: TfrmWaterFall;
  bitmapColorChart, bitmapAverage: TBitMap;

implementation

{$R *.DFM}

uses
  uSpectrumAnalyzer, StrUtils, uTime;
{$REGION ' Get''s Set''s'}

function TfrmWaterFall.getImageRGBWidth: Integer;
begin
  result := imageRGB.Width;
end;

procedure TfrmWaterFall.FormResize(Sender: TObject);
begin
  if assigned(BitMap) then
    BitMap.Height := frmWaterFall.Height;
end;

function TfrmWaterFall.getRun: boolean;
begin
  // SpecAna.swWaterfall.Checked := FRunning;
  result := FRunning;
end;

(* s e t  W a t e r   R u n n i n g *)
procedure TfrmWaterFall.setRun(value: boolean);
begin
  if value = Run then
    exit; // Already running

  if value = True then
  begin
    FrameComplete := False;
    frmWaterFall.AutoScroll := True;

    BitMap := TBitMap.Create; // Create main waterfall
    BitMap.PixelFormat := pf24bit; // TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);
    BitMap.Width := frmSpecAna.FFT.SpectrumSize div 2;
    BitMap.Height := imageRGB.Height;
    IntegrationType := NONE;
    frmSpecAna.cbPalettesChange(Self);
    Width := frmSpecAna.FFT.SpectrumSize div 2;
    imageRGB.Width := frmSpecAna.FFT.SpectrumSize div 2;
    // Don't change - causes flashing

    setLineNumber(1);
    frmWaterFall.Visible := True;
    frmSpecAna.BringToFront;
    FRunning := True;
  end
  else
  begin
    if assigned(BitMap) then
    begin
      BitMap.Free;
    end;
    frmSpecAna.swWater.Checked := False;
    frmWaterFall.Visible := value;
    FRunning := False;
  end;

end;
{$ENDREGION}
{$REGION ' SAVE JPG      '}

procedure TfrmWaterFall.SaveJPG;
var
  JPGImage: TJPEGImage;
  tempDIR: string;
  XML: String;
  SS: TStringStream;
begin
  while FileInUse(frmSpecAna.JPGFileNameAtEOFrame) do
  begin
    frmSpecAna.sbSpecAna.Panels[CAlert].Text := 'Wait ' + frmSpecAna.JPGFileNameAtEOFrame;
    delay(1000);
  end;
  if (frmSpecAna.Water) then
  begin
    JPGImage := TJPEGImage.Create;
    try
      JPGImage.Assign(BitMap);
      JPGImage.SaveToFile(frmSpecAna.JPGFileNameAtEOFrame);
    finally
      JPGImage.Free;
    end;

    XML := frmSpecAna.getSML;
    SS := TStringStream.Create('');
    try
      SS.Position := 0;
      SS.WriteString(XML);
    finally
      AddComment(frmSpecAna.JPGFileNameAtEOFrame, SS);
    end;
    SS.Free; // Jim added
  end;
  if Not FileInUse('D:\www\waterfall.jpg') then
    CopyFile(PChar(frmSpecAna.JPGFileNameAtEOFrame), PChar('D:\www\waterfall.jpg'), False)
  else
    ShowMessage('waterfall.jpg in use ');
end;
{$ENDREGION}
{$REGION ' Line Number    '}

procedure TfrmWaterFall.setLineNumber(value: Integer);
var
  BufSize: Integer;
  BaseFileName: String;
begin

  BufSize := (frmSpecAna.FFT.SpectrumSize div 2);
  SetLength(ColumnAverage, BufSize); // Make the average buffer usable
  LineNumber := value;
  if (LineNumber = 1) then
    FrameComplete := False;
  if (LineNumber >= imageRGB.Height - 1) then
  // See if we have completed one frame yet
  begin
    SaveJPG;
    setLineNumber(1);
  end;
end;

(* F I L L   L i n e *)
procedure TfrmWaterFall.FillLine(Buffer: array of Integer);
var
  column, ScaledSignal: Integer;
  p: pRGBTripleArray;
  X: Integer;
begin
  X := Length(Buffer);
  Try
    p := BitMap.ScanLine[LineNumber]; // get the current image line
  except
    LineNumber := 1;
    beep;
  End;

  begin
    for column := 0 to Length(Buffer) - 1 do
    begin
      with p[column] do
      begin
        ScaledSignal := (Buffer[column]) + 1;

        With frmSpecAna.WaterScale do
        begin
          if (mouse > 0) then
            ScaledSignal := Round(ScaledSignal * (256 / mouse))
          else
            ScaledSignal := Round(ScaledSignal * (256 / RangeHigh));
        end;
        if ScaledSignal > 256 then
          ScaledSignal := 256;
        if ScaledSignal < 1 then
          ScaledSignal := 1;
        Try
          p[column].rgbtRed := Palette[ScaledSignal].rgbtRed;
          p[column].rgbtGreen := Palette[ScaledSignal].rgbtGreen;
          p[column].rgbtBlue := Palette[ScaledSignal].rgbtBlue;
        except
          on E: Exception do
            ShowMessage(E.ClassName + ' error raised, with message : ' + E.MESSAGE);
        End;
      end;
    end;
    setLineNumber(LineNumber + 1);
    try
      p := BitMap.ScanLine[LineNumber]; // Draw marker
    except
      // on E: Exception do
      begin
        ShowMessage('end');
        // ShowMessage(E.ClassName + 'Water near line 272 error raised with message: ' + E.Message);
      end;
    end;
    for column := 0 to Length(Buffer) - 1 do
    begin
      p[column].rgbtRed := Palette[128].rgbtBlue;
      // Reverse to make the marker visible
      p[column].rgbtGreen := Palette[128].rgbtGreen;
      p[column].rgbtBlue := Palette[128].rgbtRed;
    end;
    imageRGB.Picture.Graphic := BitMap;
    // displayed after each line.
  end;
  // SpecAna.neTest.value := imageRGB.Left;
end { DrawLine };
{$ENDREGION}
{$REGION ' Mouse'}

procedure TfrmWaterFall.imageRGBMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if assigned(imageRGB) then
  begin
    MouseStartX := X;
    MouseStartY := Y;
  end;
end;

procedure TfrmWaterFall.imageRGBMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if assigned(BitMap) then
  begin
    BitMap.Canvas.MoveTo(MouseStartX, MouseStartY);
    BitMap.Canvas.LineTo(X, Y);
    imageRGB.Picture.Graphic := BitMap;
    // displayed after each line.
  end;
end;

(* M o u s e   M o v e *)
procedure TfrmWaterFall.imageRGBMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if assigned(imageRGB) then
  begin
    // frmSpecAna.nlTest.Value := imageRGB.Width;
    frmSpecAna.nlMouse.value := X;
    with frmSpecAna do
    begin

      chart.CrossHair4.PosX := X;
      nlAudioFreq.value := Trunc(FFT.FreqOfLine(X, 1 / frmSpecAna.SamplesPerSec));
      MouseFreq := Round(nlAudioFreq.value);
      // WaterFall.HorzScrollBar.Position := Round(nlAudioFreq.value);
      // MouseAmbiant := frmSpecAna.bufLineAve[X];
      // nlMouseAmbiant.value := MouseAmbiant;
      if Run then
      begin
        nlMouseAmbiant.value := frmSpecAna.bufLineAve[X];
        nlAmplitude.value := FFT.Magnitude[X];
      end;

    end;
  end;
end;
{$ENDREGION}

procedure TfrmWaterFall.FormActivate(Sender: TObject);

begin
  frmWaterFall.Height := frmSpecAna.FWaterfallHeight;
  frmWaterFall.Left := frmSpecAna.FWaterfallLeft;
  frmWaterFall.Top := frmSpecAna.FWaterfallTop;
  frmWaterFall.Width := frmSpecAna.FWaterfallWidth;

end;

procedure TfrmWaterFall.FormClose(Sender: TObject; var Action: TCloseAction);
begin

  // BitMap.FreeImage; // Causes problems
  // If assigned(BitMap) then // Causes problems
  // BitMap.Free;
  // imageRGB.FreeInstance;   // Causes problems
  frmSpecAna.swWater.Checked := False;
  FRunning := False;
  frmWaterFall.Visible := False;
end;

end.
