unit uSpectrumAnalyzer;

interface

{$REGION ' Header '}
{$REGION ' -Uses '}

uses
	Windows, Messages, Variants, Classes, Graphics, Controls, Forms, Dialogs,
	FileCtrl, StdCtrls, StrUtils,
	MMSystem, Buttons, SDL_sdlbase, SDL_rchart, SDL_onoffbut, SDL_fourier,
	SDL_gradfl, SDL_NumLab, SDL_stringl,
	OleCtrls, DBOleCtl, ExtCtrls, ComCtrls, Menus, AxCtrls, AbSwitch, AbNumEdit,
	DateUtils, AbOpHour, ExtDlgs,
	Registry, AbLED, Math, uSETICommon, _GClass, VrControls,
	VrRotarySwitch, JvBaseDlg, JvSelectDirectory, JvExComCtrls, JvComCtrls, JvProgressBar,

	uROClient, uROBINMessage, uROClientIntf, uROServer,
	uROPoweredByRemObjectsButton,
	uROIndyUDPServer, uROPostMessage, uROIndyEmailServer, uROIndyTCPServer,
	uROIndyHTTPServer, uROBaseHTTPServer, uROBaseConnection, uROCustomHTTPServer, uROComponent, uROMessage,
	uROTransportChannel,
	uROBaseHTTPClient, uROIndyHTTPChannel, uROJSONMessage,
	JSON, Vcl.WinXCtrls, JvExStdCtrls, JvShapedButton, uROServerMultiMessage, Jpeg, JvExControls, JvLED, WaveUtils, WaveStorage,
	WaveRecorders, WaveOut, WavePlayers, WaveIO, WaveIn, SysUtils, JvDialogs, JvExExtCtrls, JvExtComponent, JvRollOut, VrMeter,
		{Vcl.TMSLogging, TMSLoggingEventLogOutputHandler, TMSLoggingUtils, TMSLoggingTextOutputHandler,}
	uRiff, uRiffIF, System.TypInfo, uROServerIntf, uROCustomRODLReader;

{$ENDREGION}
{$REGION ' -Types '}

const

	CSaveLocation = 'C:\www';
	SETINetSpectrumAnalyzerKey = '\SOFTWARE\SETI Net\SpectrumAnalyzer';
	SETINetFTPKey = '\SOFTWARE\SETI Net\Passwords\FTP'; // Jim - SPACE in SETI Net IS Important...
	CDirID = 0;
	CDir = CDirID + 1;
	CFileID = CDir + 1;
	CFile = CFileID + 1;
	CAlertID = CFile + 1;
	CAlert = CAlertID + 1;

		// COscRubber = 11814; // Rubber the R7000 reported frequency to (close to) the actual 2014-07-17
	COscRubber = 11009; // Rubber the original HackRF 2016-12-15
		// Rubber the R7000 reported frequency to (close to) the actual 2014-07-17

type
	PBufArray = PByteARRAY;

type
	TWaterScale = Record

		Mouse: integer;
		RangeHigh: Double;
	end;

type
	TfrmSpecAna = class(TForm)

		sbSpecAna: TStatusBar;

		ROMessage: TROBinMessage;
		pcPageControl: TPageControl;
		tsSpecAna: TTabSheet;
		pnlControls: TPanel;
		pnlControl: TPanel;
		lblWeight: TLabel;
		lblWater: TLabel;
		nlAmplitude: TNumLab;
		nlAudioFreq: TNumLab;
		lblRunning: TLabel;
		cbWindowing: TComboBox;
		knobVerticalRange: TVrRotarySwitch;
		knobFFTSize: TVrRotarySwitch;
		tsStats: TTabSheet;
		ROServer: TROIndyHTTPServer;
		moSML: TMemo;
		ledBufFilled: TAbLED;
		swWater: TAbToggleSwitch;
		tsLog: TTabSheet;
		moLog: TMemo;
		ledLog: TAbLED;
		FFT: TFastFourier;
		lblFFTSize: TLabel;
		lblInputRange: TLabel;
		AudioIn: TLiveAudioRecorder;
		chart: TRChart;
		nlFrequencyReference: TNumLab;
		pnlDetectionLogic: TJvRollOut;
		pnlDetect: TPanel;
		gbMouse: TGroupBox;
		nlMouseAmbiant: TNumLab;
		nlMouse: TNumLab;
		gbDetectSetup: TGroupBox;
		lblPallettSelection: TLabel;
		lblThreshold: TLabel;
		lblSegSize: TLabel;
		lblIntegration: TLabel;
		lblLine: TLabel;
		nlFrameIndex: TNumLab;
		swShowAmplitudeLimit: TAbToggleSwitch;
		lblAmplitudeLimit: TLabel;
		lblThresholdPercent: TLabel;
		lblShowAmplitudeLimit: TLabel;
		nlLineLimit: TNumLab;
		cbPalettes: TComboBox;
		nsIntegration: TAbNumSpin;
		nsThreshold: TAbNumSpin;
		nsSegSize: TAbNumSpin;
		nsLinesPerFrame: TAbNumSpin;
		nsAmplitudeLimit: TAbNumSpin;
		btnLimitCapture: TJvShapedButton;
		gbET: TGroupBox;
		nlFrequencyET: TNumLab;
		ledETBin: TAbLED;
		nlETAmplitude: TNumLab;
		neETBin: TAbNumEdit;
		btnHelpAbout: TButton;
		ledEOFrame: TAbLED;
		btnRunHitBat: TButton;
		pnlAudioRecording: TJvRollOut;

		pnlAudio: TPanel;
		mtrVu: TVrMeter;
		pbFrameIndex: TJvProgressBar;
		StockAudioPlayer: TStockAudioPlayer;
		SaveDialog: TSaveDialog;
		Format: TComboBox;
		dlgOpen: TOpenDialog;
		recordOffLine: TStockAudioRecorder;
		btnTest: TButton;
		pnlRecord: TPanel;
		btnRecordStop: TBitBtn;
		btnRecordPause: TButton;
		btnRecord: TBitBtn;
		btnSelectRecordToFile: TButton;
		pnlPlayback: TPanel;
		btnPlayStop: TButton;
		btnPlayPause: TBitBtn;
		btnPlay: TBitBtn;
		btnSelectFileToPlay: TButton;
		swVuMeterInput: TToggleSwitch;
		lblVuMeterInput: TLabel;
		recordSETI: TStockAudioRecorder;
		pnlSMLControl: TPanel;
		dlgLoadSML: TOpenDialog;
		btnLoadSML: TButton;
		Panel1: TPanel;
		btnStartLogging: TButton;
		swRun: TAbToggleSwitch;
		btnTagSML: TButton;
		btnClearLog: TButton;
		cbStopOnEOF: TCheckBox;
		cbShowLongHints: TCheckBox;
		cbShowHints: TCheckBox;
		swScan: TAbToggleSwitch;
		lblScan: TLabel;
		ledScan: TAbLED;

		procedure FormCreate(Sender: TObject);
		procedure knobVerticalRangeChange(Sender: TObject);
		procedure cbWindowingSelect(Sender: TObject);
		procedure chartMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
		procedure chartMouseMoveInChart(Sender: TObject; InChart: Boolean; Shift: TShiftState; rMousePosX, rMousePosY: Double);
		procedure mnuExitClick(Sender: TObject);
		procedure FormClose(Sender: TObject; var Action: TCloseAction);
		procedure knobFFTSizeChange(Sender: TObject);
		procedure nsLinesPerFrameValueChanged(Sender: TObject);
		procedure dlgSaveDirClose(Sender: TObject);
		procedure cbPalettesChange(Sender: TObject);
		procedure ledETBinClick(Sender: TObject);
		procedure nsSegSizeValueChanged(Sender: TObject);
		procedure tsStatsMouseEnter(Sender: TObject);
		procedure tsStatsMouseLeave(Sender: TObject);
		procedure nsLinesPerFrameLimit(Sender: TObject);
		procedure btnHelpAboutClick(Sender: TObject);
		procedure swWaterClick(Sender: TObject);

		procedure nsThresholdValueChanged(Sender: TObject);
		procedure nsAmplitudeLimitValueChanged(Sender: TObject);
		procedure btnLimitCaptureClick(Sender: TObject);
		procedure swRunClick(Sender: TObject);
		procedure btnTestClick(Sender: TObject);
		procedure AudioInData(Sender: TObject; const Buffer: Pointer; BufferSize: Cardinal; var FreeIt: Boolean);
		procedure FormDestroy(Sender: TObject);
		procedure AudioInDeactivate(Sender: TObject);
		procedure AudioInActivate(Sender: TObject);
		procedure pnlDetectionLogicCollapse(Sender: TObject);
		procedure pnlDetectionLogicExpand(Sender: TObject);
		procedure btnRunHitBatClick(Sender: TObject);

		function setSML(SML: ANSIstring): Boolean;
		procedure AudioRecorder1Activate(Sender: TObject);
		procedure pnlAudioRecordingCollapse(Sender: TObject);
		procedure pnlAudioRecordingExpand(Sender: TObject);
		procedure btnRecordClick(Sender: TObject);
		procedure btnPlayClick(Sender: TObject);
		procedure btnPlayPauseClick(Sender: TObject);

		procedure StockAudioPlayerActivate(Sender: TObject);
		procedure StockAudioPlayerDeactivate(Sender: TObject);
		procedure StockAudioPlayerLevel(Sender: TObject; Level: integer);
		procedure recordOffLineActivate(Sender: TObject);
		procedure recordOffLineDeactivate(Sender: TObject);
		procedure recordOffLineLevel(Sender: TObject; Level: integer);
		procedure Button1Click(Sender: TObject);
			// procedure btnSelect(Sender: TObject);
		procedure FormatChange(Sender: TObject);
		procedure btnRecordStopClick(Sender: TObject);
		procedure btnSelectFileToPlayClick(Sender: TObject);
		procedure btnPlayStopClick(Sender: TObject);
		procedure btnSelectRecordToFileClick(Sender: TObject);
		procedure btnRecordPauseClick(Sender: TObject);
		procedure swVuMeterInputClick(Sender: TObject);

		procedure FormActivate(Sender: TObject);
		procedure btnLoadSMLClick(Sender: TObject);
		procedure btnStartLoggingClick(Sender: TObject);
		procedure btnTagSMLClick(Sender: TObject);
		procedure btnClearLogClick(Sender: TObject);
		procedure nsIntegrationValueChanged(Sender: TObject);
		procedure cbShowHintsClick(Sender: TObject);
		procedure swScanClick(Sender: TObject);
		procedure recordSETIActivate(Sender: TObject);
		procedure recordSETIDeactivate(Sender: TObject);

	protected
			{ protected declarations not accessible externally by class users }
		FLinesPerFrame: integer;
		FRcd: Boolean; // recording or not
		GuardLowPC, GuardHighPC: Single;
		CurrentWAV: String;
		pcm: TPCMFormat;
		FRunning: Boolean;

		FEOFrame: Boolean;
		FoundET: Boolean;

		bufLineSize: integer;

		bufLine: array of integer; // TLine; // One line of Bins
		bufLineLimit: array of integer;

		bufFrame: array of array of integer;
		FCaptureLimit: Boolean;
		FAmplitudeLimit: integer;
		FTest: Boolean;
		version: string;
		FFTPHost: String;
		FFTPUserName: String;
		FFTPPassword: String;
		FAmbiant: Single;
		FFrequencyReference: int64;
		FFrequencyET: int64;
		FSML: ANSIstring; // The SML to be hooked to the wav file
		FThreshold: integer; // alarm point
		ThresholdPercent: Single;
		FIntegration: integer; // Seconds

		FHzPerBin: Single;
		FSegmentSize: integer;

		ndxBufFrame: integer;
		ndxBufLine: integer;
		segmentAve: integer;

		LeftEdge, RightEdge: integer;
		LongScans: Boolean;

		SETINetReg: TRegistry;

		StatsOn: Boolean;

		FVerticalRange: Double;
		FUpdateRate: Single;
		FIIRa: real;

		FWaterScale: TWaterScale;

		OscRubber: integer;
		TCnt: integer;
		DataChunkIndex: integer;
		MousePosX, MousePosY: integer;
		FPeak: Double; // Maximum of integrated signal

		FSamplesPerSec: integer;

		ndxFrame: integer;

		BufferCount: integer;

		procedure DisplayHint(Sender: TObject);
		procedure setVuMeter(value: integer);
		function getLowerGuardpc: Single;
		procedure setLowerGuardpc(value: Single);
		function getUpperGuardpc: Single;
		procedure setUpperGuardpc(value: Single);

		function getReset: Boolean;
		procedure setResetRMT(value: Boolean);
		function getResetRMT: Boolean;
		procedure setReset(value: Boolean);

		function getThreshold: integer;
		function getThresholdRMT: integer;
		procedure setThreshold(value: integer);
		procedure setThresholdRMT(value: integer);

		procedure setAmplitudeLimit(value: integer);
		function getAmplitudeLimit: integer;

		procedure setVerticalRange(value: Double);
		function getVerticalRange: Double;
		procedure setVerticalRangeRMT(value: Double);
		function getVerticalRangeRMT: Double;

		function getLinesPerFrame: integer;
		procedure setLinesPerFrame(value: integer);
		function getLinesPerFrameRMT: integer;
		procedure setLinesPerFrameRMT(value: integer);

		function getIntegration: integer;
		procedure setIntegration(value: integer);
		procedure setIntegrationRMT(value: integer);

		procedure setFFTSize(value: integer);

		procedure setSegmentSize(value: integer);
		function getSegmentSize: integer;
		function getSegmentSizeRMT: integer;
		procedure setSegmentSizeRMT(value: integer);

		function getRunRMT: Boolean;
		procedure setRunRMT(value: Boolean);
		function getRun: Boolean;
		procedure setRun(value: Boolean);

		function getWaterRunRMT: Boolean;
		procedure setWaterRunRMT(value: Boolean);
		function getWaterRun: Boolean;
		procedure setWaterRun(value: Boolean);

		procedure setTerminateRMT(value: Boolean);

		procedure setWaterHeight(value: integer);

		procedure setScan(value: Boolean);
		function getScan: Boolean;
		procedure setScanRMT(value: Boolean);
		function getScanRMT: Boolean;

		procedure setFrequencyReference(value: int64);
		procedure setFrequencyReferenceRMT(value: int64);

		procedure WMsetScan(var MESSAGE: TMessage); message WM_setScan;
		procedure WMsetWaterRun(var MESSAGE: TMessage); message WM_setWATERRUN;
		procedure WMsetRun(var MESSAGE: TMessage); message WM_setRUN;
		procedure WMsetSML(var MESSAGE: TMessage); message WM_SETSML;

		procedure WMsetSnapShot(var MESSAGE: TMessage); message WM_SETSNAPSHOT;
		procedure WMsetReset(var MESSAGE: TMessage); message WM_setReset;
		procedure WMsetTerminate(var MESSAGE: TMessage); message WM_setTERMINATE;
		procedure WMDelFiles(var MESSAGE: TMessage); MESSAGE WM_DelFiles;

		procedure BuildAudioFormatList;

			// procedure saveWAV;
	private
		Riff: IRiff;
		FScan: Boolean;
		FSnapshotDir: string;
		function GetFFTSize: integer;
			// procedure DoOutput(Sender: TObject; AOutputInformation: TTMSLoggerOutputInformation);
		function getBinWidth: Single;
		function getBinWidthRMT: Single;
		procedure RunHitBat;

	public
		test: Boolean;

		FWaterfallTop: integer;
		FWaterfallLeft: integer;
		FWaterfallHeight: integer;
		FWaterfallWidth: integer;

		WAVFileNameAtEOFrame: String;
		JPGFileNameAtEOFrame: String;
		bufLineAve: TLine;
		ET: TET;

		function getSML: string;
		function setSMLRMT(value: ANSIstring): Boolean;
		function setDelFiles: Boolean;
		function setDelFilesRMT: Boolean;
		procedure Displaywidths;
		function CreateWAVFileName: String;
		function CreateJPGFileName: String;

		property FFTSizeRMT: integer read GetFFTSize;
		property FFTSize: integer read GetFFTSize;

		Property FrequencyETRMT: int64 read FFrequencyET; // write FFrequencyET;
		property FrequencyET: int64 read FFrequencyET; // write setFrequencyReference;

		Property Ambiant: Single read FAmbiant;

		procedure setSnapShot(value: string);
		function getSnapshot: string;
		procedure setSnapShotRMT(value: string);
		function getSnapShotRMT: string;

		property SamplesPerSec: integer read FSamplesPerSec write FSamplesPerSec;
		property WaterScale: TWaterScale read FWaterScale write FWaterScale;

		property BinWidthRMT: Single read getBinWidthRMT;
		property BinWidth: Single read getBinWidth;

		property SegmentSize: integer read getSegmentSize write setSegmentSize;
		property SegmentSizeRMT: integer read getSegmentSizeRMT write setSegmentSizeRMT;

		property ThresholdRMT: integer read getThresholdRMT write setThresholdRMT;
		property Threshold: integer read getThreshold write setThreshold;

		property Integration: integer read getIntegration write setIntegration;
		property IntegrationRMT: integer read getIntegration write setIntegrationRMT;

		property Water: Boolean read getWaterRun write setWaterRun;
		property WaterRunRMT: Boolean read getWaterRunRMT write setWaterRunRMT;
		property AmplitudeLimit: integer read getAmplitudeLimit write setAmplitudeLimit;

		property TerminateRMT: Boolean write setTerminateRMT;

		property VerticalRange: Double read getVerticalRange write setVerticalRange;
		property VerticalRangeRMT: Double read getVerticalRangeRMT write setVerticalRangeRMT;

		property LinesPerFrame: integer read getLinesPerFrame write setLinesPerFrame;
		property LinesPerFrameRMT: integer read getLinesPerFrameRMT write setLinesPerFrameRMT;

		property ScanRMT: Boolean read getScanRMT write setScanRMT;
		property Scan: Boolean read getScan write setScan;

		property Run: Boolean read getRun write setRun;
		property RunRMT: Boolean read getRunRMT write setRunRMT;

		property Snapshot: string read getSnapshot write setSnapShot;
		property SnapshotRMT: string read getSnapShotRMT write setSnapShotRMT;

		property Reset: Boolean read getReset write setReset;
		property ResetRMT: Boolean read getResetRMT write setResetRMT;

		property FrequencyReference: int64 read FFrequencyReference write setFrequencyReference;
		property FrequencyReferenceRMT: int64 read FFrequencyReference write setFrequencyReferenceRMT;

	end;
{$ENDREGION}

var
	frmSpecAna: TfrmSpecAna;
	WaveStorage: TWaveStorage;
{$ENDREGION}

		//
implementation

{$REGION ' C R E A T E '}
{$R *.dfm}
{$SETPEFLAGS IMAGE_FILE_LARGE_ADDRESS_AWARE}

uses uWaterFall, uTime, uRORTTIServerSupport, uAbout, IOUtils, ShellApi, uRecorderPlayer;

var
	numbuffers: integer = 4;

procedure TfrmSpecAna.Displaywidths;
begin
		// pnlDetectionLogic.Caption := 'Chart: ' + intToStr(chart.Width) + ' pnlControls: ' + intToStr(pnlControls.Width) + ' Logic : ' +
		// intToStr(pnlDetectionLogic.Width) + ' Audio Recording: ' + intToStr(pnlAudioRecording.Width) + ' frm: ' + intToStr(Width);
		// pnlAudioRecording.Caption := 'Chart: ' + intToStr(chart.Width) + ' PnlControls: ' + intToStr(pnlControls.Width) + ' Logic : ' +
		// intToStr(pnlDetectionLogic.Width) + ' Audio Recording: ' + intToStr(pnlAudioRecording.Width) + ' frm: ' + intToStr(Width);
end;

procedure TfrmSpecAna.FormCreate(Sender: TObject);
var
	DisableProcessWindowsGhostingProc: procedure;

begin
		// DisableProcessWindowsGhostingProc := GetProcAddress(GetModuleHandle('user32.dll'), 'DisableProcessWindowsGhosting');
		// if Assigned(DisableProcessWindowsGhostingProc) then
		// DisableProcessWindowsGhostingProc;

		// FFrequencyReference := 1420000000;
	FFrequencyReference := 0000000000;
		// doublebuffered:=true;  //  D O N ' T   D O   T H I S
	SETINetReg := TRegistry.Create;
	SETINetReg.LazyWrite := false;
	try
		SETINetReg.RootKey := HKEY_CURRENT_User;

			(* ================== F T P ================================================= *)
		if SETINetReg.KeyExists(SETINetFTPKey) then
		begin // Key Exists
			if SETINetReg.OpenKey(SETINetFTPKey, false) then
				// This one will NOT create the key
				// OpenKey will create the key if the second parameter is TRUE and the key does not already exist
			begin // N O R M A L   R E G   E N T R I E S
				try // open key
					with SETINetReg do
					begin // Setup the GUI here - All of it
						FFTPHost := ReadString('Host');
						FFTPUserName := ReadString('UserName');
						FFTPPassword := ReadString('Password');

					end // with SETINetReg
				except // an exception is raised if there isn't an entry
						// TMSLogger.Error('Password Fail FTP');
					Showmessage('Password Fail FTP');

				end; // Open Key
			end; // Open key
		end; // SETINetLocalHostkey exists

		if SETINetReg.KeyExists(SETINetSpectrumAnalyzerKey) then
		begin // Key Exists
			if SETINetReg.OpenKey(SETINetSpectrumAnalyzerKey, false) then
				// OpenKey will create the key if the second parameter is TRUE and the key does not already exist
				// This one will NOT create the key
			begin // N O R M A L   R E G   E N T R I E S
				try // open key
					with SETINetReg do
					begin // Setup the GUI here - All of it
						knobFFTSize.SwitchPosition := 0;
						Integration := 0;
						knobFFTSize.SwitchPosition := ReadInteger('knobFFTSize');

						VerticalRange := ReadFloat('VerticalRange');
						FSegmentSize := ReadInteger('SegmentSize');
						LinesPerFrame := ReadInteger('LinesPerFrame');
						nsLinesPerFrame.ValueAsInt := LinesPerFrame;

						GuardLowPC := ReadFloat('LowerGuard');
						GuardHighPC := ReadFloat('UpperGuard');

						if ((GuardLowPC < 0.01) OR (GuardLowPC > 1.0)) then
							GuardLowPC := 0.05;
						if ((GuardHighPC > 1.0) OR (GuardHighPC < 0.1)) then
							GuardHighPC := 0.95;

						chart.CrossHair2.PosX := GuardLowPC * (FFT.SpectrumSize DIV 2);
						chart.CrossHair3.PosX := GuardHighPC * (FFT.SpectrumSize DIV 2);

						frmSpecAna.Left := ReadInteger('Left');
						frmSpecAna.Top := ReadInteger('Top');
						Threshold := ReadInteger('Threshold');
						Integration := ReadInteger('Integration');
						FIIRa := 1 / (Integration * FUpdateRate);

						AmplitudeLimit := ReadInteger('LineLimit');
						FWaterfallTop := ReadInteger('WaterTop');
						FWaterfallLeft := ReadInteger('WaterLeft');

						FWaterfallWidth := ReadInteger('WaterWidth');
						FWaterfallHeight := ReadInteger('WaterHeight');
					end
				except
						// an exception is raised if there isn't a version entry
					SETINetReg.DeleteKey(SETINetSpectrumAnalyzerKey);
						// If the version value didn't exist delete the whole key

				end; // SETINetExamplekey exists
			end;

			if not SETINetReg.KeyExists(SETINetSpectrumAnalyzerKey) then
				// D E F A U L T   R E G   E N T R I E S
			begin // New User or incomplete registry entries so rebuild.
				with SETINetReg do
				begin
					OpenKey(SETINetSpectrumAnalyzerKey, True);
						// OpenKey will create the key if the second parameter is true and the key does not already exist
					nsIntegration.ValueAsInt := 1;
					knobFFTSize.SwitchPosition := 5;
					Threshold := 25;
					Integration := 15;
					VerticalRange := 32768;
					FSegmentSize := 10;
					LinesPerFrame := 666;
					nsLinesPerFrame.ValueAsInt := 1000;
					chart.CrossHair2.PosX := 0.25 * (FFT.SpectrumSize DIV 2);
					chart.CrossHair3.PosX := 0.75 * (FFT.SpectrumSize DIV 2);
					frmSpecAna.Left := 100;
						// Nominal left edge
					frmSpecAna.Top := 50; // Nominal top edge
					AmplitudeLimit := 10;
					FWaterfallTop := 300;
					FWaterfallLeft := 200;
					FWaterfallHeight := 575;
					FWaterfallWidth := 1000;

				end;
			end; // Key exists

		end; // Finished with Reg
	finally // Setup Example
		SETINetReg.Free;
	end;
	TCnt := 0;
	FSML := moSML.Lines.Text; // GeneratedSML in Resource
	OscRubber := COscRubber;
	setFFTSize(knobFFTSize.SwitchPosition);

	nsSegSize.ValueAsInt := SegmentSize;
	ROServer.Active := True;
	LinesPerFrame := nsLinesPerFrame.ValueAsInt;
	pbFrameIndex.Position := 0;
	pbFrameIndex.Max := LinesPerFrame;

	If ((Screen.MonitorCount = 1) AND (Left > 1000)) then
	begin
		Left := 100; // Reset for single monitor
	end;
	FWaterfallLeft := Left;

	FSamplesPerSec := 44100;

	begin
			//    TMSLogger.RegisterOutputHandlerClass(TTMSLoggerEventLogOutputHandler);
			//    TMSLogger.OnOutput := DoOutput;
			//    TMSLogger.Active := false;
			//    TMSLogger.Clear;
			//    moLog.Clear;
	end;
	Caption := Caption + '  Port: ' + intToStr(ROServer.Port);
	Caption := Caption + ' ' + intToStr(SizeOf(Pointer) * 8) + ' Bit Platform';
{$IF Defined( EUREKALOG )}
	Caption := Caption + (' + EurekaLog  , ');
	btnStartLoggingClick(Self);
{$IFEND}
	pnlDetectionLogic.Collapsed := True;
	pnlAudioRecording.Collapsed := True;
	Displaywidths;
	frmSpecAna.Width := chart.Width + pnlControl.Width + pnlDetectionLogic.Width + 58;

	FileName := 'Test.WAV';

	btnRecord.Enabled := True;
		// btnPlay.Enabled := FileExists(WaveFile.Caption);
	BuildAudioFormatList;
	Format.ItemIndex := Ord(AudioIn.PCMFormat) - 1;

	btnRecord.Enabled := True;
		// btnPlay.Enabled := FileExists(FileName);

		// Application.HintShortCuts := true;
		// Application.HintColor := clRed;   // color of your choice
	Application.HintPause := 500; // 250 mSec before hint is shown
	Application.HintHidePause := 2000; // hint disappears after 3 secs

	frmSpecAna.BringToFront;
	recordSETI.Stock := WaveStorage;
	recordOffLine.Stock := WaveStorage;
end { Create };

procedure TfrmSpecAna.FormDestroy(Sender: TObject);
begin
	Riff := nil;
end;

procedure TfrmSpecAna.BuildAudioFormatList;
var
	pcm: TPCMFormat;
	WaveFormat: TWaveFormatEx;
begin
	with Format.Items do
	begin
		BeginUpdate;
		try
			Clear;
			for pcm := Succ(Low(TPCMFormat)) to High(TPCMFormat) do
			begin
				SetPCMAudioFormatS(@WaveFormat, pcm);
				Add(GetWaveAudioFormat(@WaveFormat));
			end;
		finally
			EndUpdate;
		end;
	end;
end;

procedure TfrmSpecAna.Button1Click(Sender: TObject);
begin
	with dlgOpen do
		if Execute then
		begin
			ShowMessage('Dude');
		end; // if
end;

{$ENDREGION}
	//
{$REGION ' DO Log '}
	//
	//procedure TfrmSpecAna.DoOutput(Sender: TObject; AOutputInformation: TTMSLoggerOutputInformation);
	//begin
	//  moLog.Lines.Insert(0, TTMSLoggerUtils.StripHTML(TTMSLoggerUtils.GetConcatenatedLogMessage(AOutputInformation, false)));
	//end;
{$ENDREGION}
	//
{$REGION ' Create File Names '}

function TfrmSpecAna.CreateJPGFileName: String;
begin
	result := ReplaceStr(WAVFileNameAtEOFrame, '.wav', '.jpg');
end;

function TfrmSpecAna.CreateWAVFileName: String;
var
	tempDIR: string;
begin
	tempDIR := uTime.SETIDate;
	tempDIR := ReplaceStr(tempDIR, '-', '\');
	tempDIR := CCaptureDrive + tempDIR;
	if NOT { System.SysUtils. } ForceDirectories(tempDIR) then
		sbSpecAna.Panels[CDir].Text := tempDIR + '\' + uTime.SETIDateTime + '.wav';
	result := tempDIR + '\' + uTime.SETIDateTime + '.wav';
end;
{$ENDREGION}
	//
{$REGION ' get/setRUN '}

procedure TfrmSpecAna.swRunClick(Sender: TObject);

begin

	RunRMT := swRun.Checked;
end;

procedure TfrmSpecAna.swScanClick(Sender: TObject);
begin
	Scan := swScan.Checked;

end;

procedure TfrmSpecAna.swVuMeterInputClick(Sender: TObject);
begin
	if swVuMeterInput.State = tssOn then
		mtrVu.Caption := 'Record/Playback'
	else
		mtrVu.Caption := 'Spectrum';
end;

procedure TfrmSpecAna.setRun(value: Boolean);
begin
	if value then
	begin // START
		Reset := True;
		ndxFrame := 0;
		knobFFTSize.Enabled := false;
		nsLinesPerFrame.Enabled := false;
			// nsIntegration.OnValueChanged(Self);
		if NOT AudioIn.Active then
		begin
			AudioIn.PCMFormat := TPCMFormat(Format.ItemIndex + 1);
			recordOffLine.PCMFormat := TPCMFormat(Format.ItemIndex + 1);
			recordSETI.PCMFormat := recordOffLine.PCMFormat;
		end;
			// TMSLogger.InfoFormat('set Run: {%b }   ', [value]);

		swRun.Checked := True;
			// swDetect.ColorOn := clSilver;
		if swWater.Checked then
			Water := True;

			// Capture := True;
			// Detect := True;

		AudioIn.Active := True;
			// EOFrame := false;

	end
	else
	begin // STOP    knobFFTSize.Enabled := True;
		nsLinesPerFrame.Enabled := True;
		knobFFTSize.Enabled := True;
		swRun.Checked := false;
		Water := false;
		Scan := false;
		ledBufFilled.Checked := false;
		AudioIn.Active := false;
			// EOFrame := True;
			// TMSLogger.InfoFormat('setRun: {%b }', [value]);
		pbFrameIndex.Position := 0;
	end;
end;

function TfrmSpecAna.getRun: Boolean;
begin
	result := FRunning;
end;

function TfrmSpecAna.getRunRMT: Boolean;
begin
	result := Run;
		// result := Run;
end;

procedure TfrmSpecAna.setRunRMT(value: Boolean);
begin

		// TMSLogger.InfoFormat('setRunMT: {%b}', [value]);
	try
		SendMessage(Handle, WM_setRUN, 0, integer(value));
	except
		on E: System.SysUtils.Exception do
				//TMSLogger.Error(E.ClassName + ': ' + E.MESSAGE);
			Showmessage(E.ClassName + ': ' + E.MESSAGE);
	end;

end;

procedure TfrmSpecAna.WMsetRun(var MESSAGE: TMessage); { message WM_SETRUN_MESSAGE; }

begin
		// TMSLogger.InfoFormat('WMsetRun is: {%d}', [MESSAGE.LParam]);

	try
		Run := Boolean(MESSAGE.LParam);
	except
		on E: System.SysUtils.Exception do
				// TMSLogger.Error(E.ClassName + ': ' + E.MESSAGE);
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;

end;

procedure TfrmSpecAna.AudioInActivate(Sender: TObject);
begin
	SetLength(bufLineAve, (FFT.SpectrumSize div 2));
	SetLength(bufFrame, LinesPerFrame, Length(bufLine));
	FRunning := True
end;

procedure TfrmSpecAna.AudioInDeactivate(Sender: TObject);
begin
	SetLength(bufLineAve, 0);
	SetLength(bufFrame, 0, 0); // two dim buffer
	FRunning := false;

end;

procedure TfrmSpecAna.AudioRecorder1Activate(Sender: TObject);
begin

end;

{$ENDREGION}
	//
{$REGION ' get/setScan '}
{$ENDREGION}
	//
{$REGION ' get/setAmplitudeLimit '}

procedure TfrmSpecAna.setAmplitudeLimit(value: integer);
begin
	FAmplitudeLimit := value;
	nsAmplitudeLimit.ValueAsInt := FAmplitudeLimit;
end;

function TfrmSpecAna.getAmplitudeLimit: integer;
begin
	result := FAmplitudeLimit;
end;

{$ENDREGION}
	//
{$REGION ' get/setReset '}

procedure TfrmSpecAna.setReset(value: Boolean);
begin

	with ET do
	begin // Clear out any Hit hanging around
		Hit := false;
		Bin := 0;
		FrequencyET := 0;
		AudioFreq := 0;
		Amplitude := 0;
		Ambiant := 0;
		FFTSize := 0;
		HzPerBin := 0;
		Threshold := 0.0;
		SegmentSize := 0;
		Integration := 0;
		LinesPerFrame := 0;
		WAVFileName := '';
		JPGFileName := '';
		AudioFreq := 0;
	end;
	ndxFrame := 0;
	ledETBin.Checked := false;
end;

function TfrmSpecAna.getReset: Boolean;
begin
		//
end;

function TfrmSpecAna.getResetRMT: Boolean;
begin
		//
end;

Procedure TfrmSpecAna.setResetRMT(value: Boolean);
begin
	try

		SendMessage(Handle, WM_setReset, 0, integer(value));
	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;
end;

procedure TfrmSpecAna.WMsetReset(var MESSAGE: TMessage); { message WM_RESETHIT; }
var
	incomming: Boolean;
begin
	try
		incomming := Boolean(MESSAGE.LParam);

	except
		on E: System.SysUtils.Exception do
				//TMSLogger.Error(E.ClassName + ': ' + E.MESSAGE);
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);

	end;

	Reset := incomming;

end;

{$ENDREGION}
	//
{$REGION ' get/setLinesperFrame '}

procedure TfrmSpecAna.setLinesPerFrameRMT(value: integer);
begin
	LinesPerFrame := value;
end;

procedure TfrmSpecAna.setLinesPerFrame(value: integer);
begin
	FLinesPerFrame := value;
	pbFrameIndex.Position := 0;
	pbFrameIndex.Max := LinesPerFrameRMT
end;

function TfrmSpecAna.getLinesPerFrame: integer;
begin
	result := FLinesPerFrame
end;

function TfrmSpecAna.getLinesPerFrameRMT: integer;
begin
	result := FLinesPerFrame
end;
{$ENDREGION}
	//
{$REGION ' get/setIntegration '}

procedure TfrmSpecAna.nsIntegrationValueChanged(Sender: TObject);
begin
	if (nsIntegration.ValueAsInt < 0) then
		nsIntegration.ValueAsInt := 0;
	if ((nsIntegration.value = 0) or (FUpdateRate = 0)) then
		FIIRa := 1
	else
		FIIRa := 1 / (nsIntegration.value * FUpdateRate);
	if (FIIRa > 1) then
			// Limit the 'a' to less than 1
		FIIRa := 1;
	FIntegration := nsIntegration.ValueAsInt;
end;

function TfrmSpecAna.getIntegration: integer;
begin
	result := nsIntegration.ValueAsInt;
end;

procedure TfrmSpecAna.setIntegration(value: integer);
begin
	nsIntegration.ValueAsInt := value;
end;

procedure TfrmSpecAna.setIntegrationRMT(value: integer);
begin
	ShowMessage('Why setIntegrationRMT');
	Integration := (value);
end;
{$ENDREGION}
	//
{$REGION ' DelFiles '}

function TfrmSpecAna.setDelFilesRMT: Boolean;

begin
	try
		SendMessage(Handle, WM_DelFiles, 0, 0);
	except
		on E: System.SysUtils.Exception do
				//TMSLogger.Error(E.ClassName + ': ' + E.MESSAGE);
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;

end;

procedure TfrmSpecAna.WMDelFiles(var MESSAGE: TMessage);

begin
		// TMSLogger.Info('Enter WMDelFiles ');
	try
			// setDelFiles(Boolean(Message.LParam));
		setDelFiles;
	except
		on E: System.SysUtils.Exception do
				// TMSLogger.Error(E.ClassName + ': ' + E.MESSAGE);
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;
		// TMSLogger.Info('Exit WMDelFiles ');
end;

function TfrmSpecAna.setDelFiles: Boolean;
var
	resWAV, resJPG: Boolean;

begin
		//TMSLogger.Info('Enter DelFiles ');
	if FileExists(WAVFileNameAtEOFrame) then
	begin
			// TMSLogger.Info('Enter DeleteFile(WAVFile  ');
		while FileInUse(WAVFileNameAtEOFrame) do
		begin
				//   TMSLogger.Info('Enter Delay for  WAVFile busy ');
			Delay(1000);
		end;
		resWAV := DeleteFile(WAVFileNameAtEOFrame);
			//  TMSLogger.Info('Exit DeleteFile(WAVFile  ')

	end;
	if FileExists(JPGFileNameAtEOFrame) then
	begin
			//  TMSLogger.Info('Enter DeleteFile(JPGFile  ');
		While FileInUse(JPGFileNameAtEOFrame) do
		begin
				//  TMSLogger.Info('Enter Delay for JPGFile busy ');
			Delay(1000);
		end;
		resJPG := DeleteFile(PChar(JPGFileNameAtEOFrame));
			// TMSLogger.Info('Exit DeleteFile(JPGFile  ')
	end;

	if ((Not resWAV) OR (Not resJPG)) then
		result := false
	else
	begin
		result := True;
	end;
		// TMSLogger.Info('Exit DelFiles ');
		// TMSLogger.Info('-------------');
end;

{$ENDREGION}
	//
{$REGION ' get/setSML '}

function TfrmSpecAna.setSML(SML: ANSIstring): Boolean;

var
	SMLChunkIndex: integer;
		// SML: ANSIString;
begin
	result := false;
	if FileExists(WAVFileNameAtEOFrame) then
	begin
		If Not FileInUse(WAVFileNameAtEOFrame) then
		begin
			Riff := TRiff.Create;
			try
				Riff.LoadFromFile(WAVFileNameAtEOFrame); // Exception here.
			except
				sbSpecAna.Panels[CAlert].Text := 'Error - cannot load file.'; // D:\www\seti_data\2018\05\30
				Riff.Clear;
				exit;
			end;
			SMLChunkIndex := Riff.IndexOf('SML ');
			If (SMLChunkIndex <> -1) then // Kill the SML if it exists
				Riff.DeleteChunkByIndex(SMLChunkIndex);

			Riff.AddChunk('SML ');
			SMLChunkIndex := Riff.IndexOf('SML ');
			Riff.Chunk[SMLChunkIndex].WriteBuffer(Pointer(SML)^, Length(SML));
				// ^   ---- the trick ---   ^
			Riff.SaveToFile(WAVFileNameAtEOFrame);
			sbSpecAna.Panels[CAlert].Text := ' SML Written to File: ' + ' complete. ';

			moSML.Text := SML;
			result := True;
			Riff.Clear;
		end
		else
			sbSpecAna.Panels[CAlert].Text := 'File: ' + FileName + ' IN USE. ';
	end
	else
		sbSpecAna.Panels[CAlert].Text := 'File: ' + FileName + ' Does not exist. ';
end;

function TfrmSpecAna.getSML: String;
begin
	result := FSML;
end;

function TfrmSpecAna.setSMLRMT(value: ANSIstring): Boolean;
var
	LParam: string;
	pLParam: PChar;
begin
	result := True;
	LParam := value;
	Length(LParam);
	GetMem(pLParam, (Length(LParam) + 1) * SizeOf(Char));
	Move(LParam[1], pLParam^, (Length(LParam) + 1) * SizeOf(Char));
	try
			// PostMessage(Handle, WM_SETSML, 0, integer(pLParam));
		SendMessage(Handle, WM_SETSML, 0, integer(pLParam));
	except
		on E: System.SysUtils.Exception do
		begin
				// TMSLogger.Error(E.ClassName + ': ' + E.MESSAGE);
			result := false;
		end;
	end;
		// if FileExists(WAVFileNameAtEOFrame) then
		// setWavSML(WAVFileNameAtEOFrame);
end;

procedure TfrmSpecAna.WMsetSML(var MESSAGE: TMessage); { message WM_SETSML_MESSAGE; }
var
	pLParam: PChar;

begin
	try
		pLParam := PChar(MESSAGE.LParam);
		begin
				// moSML.Lines.Add(pLParam);
			setSML(pLParam);
		end;
		Freemem(pLParam);
	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;

	frmSpecAna.sbSpecAna.Panels[CAlert].Text := 'setSML';

end;
{$ENDREGION}
	//
{$REGION ' get/setBinWidth '}

function TfrmSpecAna.getBinWidthRMT: Single;
begin
	result := BinWidth;
end;

function TfrmSpecAna.getBinWidth: Single;
begin
	result := FHzPerBin;
end;

{$ENDREGION}
	//
{$REGION ' get/setSegmentSize '}

function TfrmSpecAna.getSegmentSizeRMT: integer;
begin
	result := FSegmentSize;
end;

function TfrmSpecAna.getSegmentSize: integer;
begin
	result := FSegmentSize;
end;

procedure TfrmSpecAna.setSegmentSize(value: integer);
begin
	FSegmentSize := value;
	nsSegSize.ValueAsInt := FSegmentSize;
end;

procedure TfrmSpecAna.setSegmentSizeRMT(value: integer);
begin
	SegmentSize := value;
end;

{$ENDREGION}
	//
{$REGION ' get/setWaterRun ' }

procedure TfrmSpecAna.swWaterClick(Sender: TObject);
begin
	Water := swWater.Checked;
end;

procedure TfrmSpecAna.setWaterRun(value: Boolean);
begin
	frmWaterFall.Run := value;
end;

function TfrmSpecAna.getWaterRun: Boolean;
begin
	result := frmWaterFall.Run;
end;

function TfrmSpecAna.getWaterRunRMT: Boolean;
begin
	result := Water;
end;

Procedure TfrmSpecAna.setWaterRunRMT(value: Boolean);
begin
	try
		SendMessage(Handle, WM_setWATERRUN, 0, integer(value));
	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;
end;

procedure TfrmSpecAna.WMsetWaterRun(var MESSAGE: TMessage); { message WM_SETWATERRUNNING_MESSAGE; }
var
	Run: Boolean;
begin
	try
		Run := Boolean(MESSAGE.LParam);

	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;
	swWater.Checked := Run;
	If (Water <> Run) Then
		Water := Run
end;

procedure TfrmSpecAna.setWaterHeight(value: integer);
begin
	frmWaterFall.Height := value;
end;
{$ENDREGION}
	//
{$REGION ' get/setTerminate '}

Procedure TfrmSpecAna.setTerminateRMT(value: Boolean);
begin
	try
		SendMessage(Handle, WM_setTERMINATE, 0, integer(value));
	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;
end;

procedure TfrmSpecAna.WMsetTerminate(var MESSAGE: TMessage);

begin
	ShowMessage('Why setTerminate');
	try
		Run := false;
		Application.Terminate;

	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;

end;
{$ENDREGION}
	//
{$REGION ' get/setScan '}

function TfrmSpecAna.getScan: Boolean;
begin
	result := FScan;
end;

procedure TfrmSpecAna.setScan(value: Boolean);
begin

	if value then
	begin

		WAVFileNameAtEOFrame := CreateWAVFileName;
		JPGFileNameAtEOFrame := CreateJPGFileName;
			// recordSETI.Active := True;    // This causes a exception - so don't do it.
		recordSETI.RecordToFile(WAVFileNameAtEOFrame); // Exception thrown here.

	end
	else
		recordSETI.Active := false;
	swScan.Checked := value;

end;

function TfrmSpecAna.getScanRMT: Boolean;
begin
	result := getScan;

end;

procedure TfrmSpecAna.setScanRMT(value: Boolean);
begin
	try
		SendMessage(Handle, WM_setScan, 0, integer(value));
	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;

end;

procedure TfrmSpecAna.WMsetScan(var MESSAGE: TMessage) { message WM_SETDECT_MESSAGE };
var
	incomming: Boolean;
begin

	try
		incomming := Boolean(MESSAGE.LParam);

	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;
	if (Scan <> incomming) then
		Scan := incomming;
end;
{$ENDREGION}
	//
{$REGION ' get/setThreshold '}

procedure TfrmSpecAna.nsThresholdValueChanged(Sender: TObject);
begin
	if Sender = nsThreshold then
	begin
		Threshold := nsThreshold.ValueAsInt;
	end;
end;

procedure TfrmSpecAna.pnlAudioRecordingCollapse(Sender: TObject);
begin
	frmSpecAna.Width := chart.Width + pnlControl.Width + pnlAudioRecording.Width + pnlDetectionLogic.Width + 27;
	Displaywidths;
end;

procedure TfrmSpecAna.pnlAudioRecordingExpand(Sender: TObject);
begin
	frmSpecAna.Width := chart.Width + pnlControl.Width + pnlAudioRecording.Width + pnlDetectionLogic.Width + 27;
	Displaywidths;
end;

procedure TfrmSpecAna.pnlDetectionLogicCollapse(Sender: TObject);
begin
	frmSpecAna.Width := chart.Width + pnlControl.Width + pnlDetectionLogic.Width + pnlAudioRecording.Width + 27;
	Displaywidths;
end;

procedure TfrmSpecAna.pnlDetectionLogicExpand(Sender: TObject);
begin
	frmSpecAna.Width := chart.Width + pnlControl.Width + pnlDetectionLogic.Width + pnlAudioRecording.Width + 27;
	Displaywidths;
end;

function TfrmSpecAna.getThreshold: integer;
begin
	result := FThreshold;
end;

function TfrmSpecAna.getThresholdRMT: integer;
begin
	result := FThreshold;
end;

procedure TfrmSpecAna.setThresholdRMT(value: integer);
begin
	ShowMessage('Why setThresholdRMT');
	setThreshold(value);
end;

procedure TfrmSpecAna.setThreshold(value: integer);
begin
	FThreshold := value;
	nsThreshold.value := FThreshold;
	ThresholdPercent := value / 100;
end;
{$ENDREGION}
	//
{$REGION ' get/setReferenceFrequency '}

procedure TfrmSpecAna.setFrequencyReference(value: int64);
begin

	nlFrequencyReference.Visible := True;
	nlFrequencyReference.value := value;
	FFrequencyReference := value;
end;

procedure TfrmSpecAna.setFrequencyReferenceRMT(value: int64);
begin
	if (FrequencyReference <> value) then
		FrequencyReference := value;
end;
{$ENDREGION}
	//
{$REGION ' CollectSnapshot }

function TfrmSpecAna.getSnapshot: string;
begin
	result := FSnapshotDir;
end;

	// S E T   S N A P S H O T
procedure TfrmSpecAna.setSnapShot(value: string);
var
	JPGImage: TJPEGImage;
	procedure Bmp2Jpeg(const BmpFileName, JPGFileName: string);
	var
		Bmp: TBitmap;
		Jpg: TJPEGImage;
	begin
		Bmp := TBitmap.Create;
		Jpg := TJPEGImage.Create;
		try
			Bmp.LoadFromFile(BmpFileName);
			Jpg.Assign(Bmp);
			try
				Jpg.SaveToFile(JPGFileName);
			except
				exit;
			end;
		finally
			Jpg.Free;
			Bmp.Free;
		end;
	end;

begin

	if Run then
	begin
		if NOT FileInUse(CSaveLocation + '\specana.bmp') then
		begin
			try
				chart.CopyToBMP(CSaveLocation + '\specana.bmp', false);
			except
				on E: System.SysUtils.Exception do
					ShowMessage(E.ClassName + ': ' + E.MESSAGE);
			end;
			if NOT FileInUse(CSaveLocation + '\specana.jpg') then
				Bmp2Jpeg(CSaveLocation + '\specana.bmp', CSaveLocation + '\specana.jpg');
		end;
	end;

end;

function TfrmSpecAna.getSnapShotRMT: string;
begin
		//
end;

procedure TfrmSpecAna.setSnapShotRMT(value: string);
var
	LParam: string;
	pLParam: PChar;
begin // Snapshot

	LParam := value;
	Length(LParam);
	GetMem(pLParam, (Length(LParam) + 1) * SizeOf(Char));
	Move(LParam[1], pLParam^, (Length(LParam) + 1) * SizeOf(Char));
	try
		SendMessage(Handle, WM_SETSNAPSHOT, 0, integer(pLParam));
	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;
end;

procedure TfrmSpecAna.WMsetSnapShot(var MESSAGE: TMessage) { message   WM_SETSNAPSHOT_MESSAGE };
var
	pLParam: PChar;

begin

	try
		pLParam := PChar(MESSAGE.LParam);
		begin
			setSnapShot(pLParam);
		end;
		Freemem(pLParam);
	except
		on E: System.SysUtils.Exception do
			ShowMessage(E.ClassName + ': ' + E.MESSAGE);
	end;

end;

{$ENDREGION}
	//
{$REGION 'S P E C   S I Z E '}

procedure TfrmSpecAna.setFFTSize(value: integer);
begin
	chart.ActiveLayer := 1;
	case value of
		0:
			begin
				FFT.SpectrumSize := 256;
				FUpdateRate := 160;
			end;
		1:
			begin
				FFT.SpectrumSize := 512;
				FUpdateRate := 80;
			end;
		2:
			begin
				FFT.SpectrumSize := 1024;
				FUpdateRate := 40;
			end;
		3:
			begin
				FFT.SpectrumSize := 2048;
				FUpdateRate := 20;
			end;
		4:
			begin
				FFT.SpectrumSize := 4096;
				FUpdateRate := 10;
			end;
		5:
			begin
				FFT.SpectrumSize := 8192;
				FUpdateRate := 5;
			end;
		6:
			begin
				FFT.SpectrumSize := 16384;
				FUpdateRate := 2.5;
			end;
		7:
			begin
				FFT.SpectrumSize := 32768;
				FUpdateRate := 1.25;
			end;
		8:
			begin
				FFT.SpectrumSize := 64;
					// Test Entry
				FUpdateRate := 160;
			end
	else
		ShowMessage('Error in setFFT size');
	end;

	chart.Scale1X.RangeHigh := FFT.SpectrumSize DIV 2;

	FHzPerBin := 22000 / (FFT.SpectrumSize / 2);
	lblFFTSize.Caption := 'FFT Size (' + FloatToStrF(22000 / (FFT.SpectrumSize / 2), ffNumber, 4, 1) + ' Hz/Bin';

	SetLength(bufLine, 0);
	SetLength(bufLine, (FFT.SpectrumSize div 2));
	SetLength(bufLineLimit, 0);
	SetLength(bufLineLimit, (FFT.SpectrumSize div 2));

	chart.CrossHair2.PosX := (FFT.SpectrumSize DIV 2) * GuardLowPC;
	chart.CrossHair3.PosX := (FFT.SpectrumSize DIV 2) * GuardHighPC;
	nsIntegrationValueChanged(Self);
		// knobFFTSize.SwitchPosition := value;
end;
{$ENDREGION}
	//
{$REGION ' V E R T I C A L   R A N G E '}

procedure TfrmSpecAna.knobVerticalRangeChange(Sender: TObject);
begin
	VerticalRange := Power(2, (20 - knobVerticalRange.SwitchPosition));
		// 2^(20-SwPosition)
end;

function TfrmSpecAna.getVerticalRange: Double;
begin
	result := FVerticalRange;
end;

function TfrmSpecAna.getVerticalRangeRMT: Double;
begin
	result := FVerticalRange;
end;

procedure TfrmSpecAna.setVerticalRangeRMT(value: Double);
begin
	setVerticalRange(value);
end;

procedure TfrmSpecAna.setVerticalRange(value: Double);
var
	VertRangeIn: integer;
	procedure setKnobPosition(RangeIn: integer);
	var
		posdub: Single;
		pos: integer;
	begin
		posdub := Round(Log10(RangeIn) / Log10(2));
			// SwPos := 20 -(log RangeIn / Log 2)
		pos := 20 - Trunc(posdub);
		knobVerticalRange.SwitchPosition := pos;
	end;

begin
	with chart do
	begin
		chart.ActiveLayer := 1;
		VertRangeIn := Round(value); // 2^(20-SwPosition)
		case VertRangeIn of
			1048576:
				begin
						// 1048576:
					with Scale1Y do
					begin
						RangeHigh := 16000; // chartSpectrum.Scale1Y.RangeHigh := 16000;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(V)';
						RangeHigh := 1.024;
						RangeLow := 0.00;
						DecPlaces := 2;
					end;
				end;
			524288:
				begin
						// 524288
					with Scale1Y do
					begin
						RangeHigh := 8000;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(mV)';
						RangeHigh := 512;
						RangeLow := 0;
						DecPlaces := 0;
					end;
				end;
			262144:
				begin // 262144
					with Scale1Y do
					begin
						RangeHigh := 4000;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(mV)';
						RangeHigh := 256;
						RangeLow := 0;
						DecPlaces := 0;
					end;
				end;
			131072:
				begin // 131072
					with Scale1Y do
					begin
						RangeHigh := 2000;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(mV)';
						RangeHigh := 128;
						RangeLow := 0;
						DecPlaces := 0;
					end;
				end;
			65536:
				begin // 65536
					with Scale1Y do
					begin
						RangeHigh := 1000;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(mV)';
						RangeHigh := 64;
						RangeLow := 0;
						DecPlaces := 0;
					end;
				end;
			32768:
				begin // 32768
					with Scale1Y do
					begin
						RangeHigh := 500;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(mV)';
						RangeHigh := 32;
						RangeLow := 0;
						DecPlaces := 0;
					end;
				end;
			16384:
				begin // 16384
					with Scale1Y do
					begin
						RangeHigh := 250;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(mV)';
						RangeHigh := 16;
						RangeLow := 0;
						DecPlaces := 0;
					end;
				end;
			8192:
				begin // 8192
					with Scale1Y do
					begin
						RangeHigh := 125;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(mV)';
						RangeHigh := 8;
						RangeLow := 0;
						DecPlaces := 0;
					end;
				end;
			4096:
				begin // 4096
					with Scale1Y do
					begin
						RangeHigh := 63;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(mV)';
						RangeHigh := 4;
						RangeLow := 0;
						DecPlaces := 2;
					end;
				end;
			2048:
				begin // 2048
					with Scale1Y do
					begin
						RangeHigh := 31;
						RangeLow := 0;
						DecPlaces := 2;
					end;
					with Scale2Y do
					begin
						Caption := '(mV)';
						RangeHigh := 2;
						RangeLow := 0;
						DecPlaces := 2;
					end;
				end;
			1024:
				begin // 1024
					with Scale1Y do
					begin
						RangeHigh := 16;
						RangeLow := 0;
						DecPlaces := 3;
					end;
					with Scale2Y do
					begin
						Caption := '(uV)';
						RangeHigh := 1024;
						RangeLow := 0;
						DecPlaces := 0;
					end;
				end;
			512:
				begin // 512
					with Scale1Y do
					begin
						RangeHigh := 8;
						RangeLow := 0;
						DecPlaces := 3;
					end;
					with Scale2Y do
					begin
						Caption := '(uV)';
						RangeHigh := 512;
						RangeLow := 0;
						DecPlaces := 0;
					end;
				end;
		else
			begin

				FVerticalRange := 1048576;
				exit;
			end
		end;

		FWaterScale.RangeHigh := chart.Scale1Y.RangeHigh;
		chart.CrossHair2.PosY := WaterScale.RangeHigh;
		FVerticalRange := value;

		try // Setup the Line Limit buffer
			SetLength(bufLineLimit, 0);
			SetLength(bufLineLimit, (FFT.SpectrumSize div 2));
		Except
			on E: System.SysUtils.Exception do
				ShowMessage(E.ClassName + ': ' + E.MESSAGE);
		end;
		setKnobPosition(VertRangeIn);
	end;
end;
{$ENDREGION}
	//
{$REGION ' A U D I O   I N '}

procedure TfrmSpecAna.AudioInData(Sender: TObject; const Buffer: Pointer; BufferSize: Cardinal; var FreeIt: Boolean);
Var
	SP: ^SmallInt;
	i, ndxBin, N, v, j: integer;
	SegAveLow, SegAveHi: integer;
	Y, Z: integer;
	FreqOffset: Double;
	intFreqOffset: int64;
	intFrequencyET: int64;
	currentAve: integer;
	currentLimit: integer;
	X: integer;
	WaveFormatSize: integer;
	WaveFormat: PWaveFormatEx;
	TstBuf: Array [0 .. 1000] of byte;
	tstIndx: ^SmallInt;
begin
		// WaveFormat :=  AudioIn.PCMFormat ;
	LeftEdge := SegmentSize + Trunc(chart.CrossHair2.PosX) { + BinShift };
	RightEdge := Trunc(chart.CrossHair3.PosX) - SegmentSize;
	ledBufFilled.Checked := Not ledBufFilled.Checked;

	N := BufferSize Div 2;
	SP := Pointer(Buffer);

	tstIndx := Pointer(Buffer);
	for i := 0 to 1000 do
	begin
		TstBuf[i] := tstIndx^;
		inc(tstIndx);

	end;

	FFT.Clear;
	for i := 1 to N do
		// Second - Move the buffer into the FFT
	begin
		v := SP^;
		FFT.RealSpec[i] := v;
		inc(SP);
	end;
		// ============= DO THE  F F T  F F T   F F T  F F T   F F T  F F T
	FFT.Transform;
		// =============

	chart.SuppressPaint := True;
	chart.ClearGraf; // initialize chart
		// chart.ActiveLayer := 1;
	chart.DataColor := clBlue;
	for ndxBin := 0 to (FFT.SpectrumSize div 2) do // Only the left half of the buffer is good to use
	begin
		bufLine[ndxBin] := Trunc((FIIRa * FFT.Magnitude[ndxBin]) + ((1 - FIIRa) * bufLine[ndxBin]));
			// bufLine[ndxBin] := Trunc((FIIRa * FFT.RealSpec[ndxBin]) + ((1 - FIIRa) * bufLine[ndxBin  ]));
		chart.DrawTo(ndxBin + 1, bufLine[ndxBin]);
	end;
		//
	if FCaptureLimit then
	begin
		for ndxBin := 0 to FFT.SpectrumSize div 2 do
			bufLineLimit[ndxBin { + BinShift } ] := AmplitudeLimit + bufLine[ndxBin];
		btnLimitCapture.Color := clSkyBLue;
		FCaptureLimit := false;
	end;

	if swShowAmplitudeLimit.Checked then
	begin
		chart.DataColor := clRed;
		for ndxBin := 0 to FFT.SpectrumSize div 2 do
			// Only the left half of the buffer is good to use
		begin
				// bufLineLimit[ndxBin + BinShift] := Trunc((FIIRa * FFT.Magnitude[ndxBin]) + ((1 - FIIRa) * bufLineLimit[ndxBin + BinShift]));
			chart.DrawTo(ndxBin + 1, bufLineLimit[ndxBin]);
		end;
	end;
	chart.SuppressPaint := false; // chart.ShowGraf;  Replaced by SuppressPaint

	if (frmWaterFall.Run) then // Fifth - Send the FFT to the Waterfall
		frmWaterFall.FillLine(bufLine);

	if Scan then
	begin
		i := Length(bufLine);
		for i := 0 to Length(bufLine) - 1 do
			bufFrame[ndxFrame, i] := bufLine[i];
		ndxFrame := ndxFrame + 1;
		nlFrameIndex.value := ndxFrame;
		pbFrameIndex.Position := ndxFrame;

		if (ndxFrame >= Length(bufFrame)) then // E N D   O F   S C A M       E N D   O F  S C A M
		begin

			Scan := false;
			if cbStopOnEOF.Checked then
			begin
				Run := false; // Shut down for the control panel
				Scan := false;

				Water := false;
			end;
			ledETBin.Checked := false;

			for Z := 0 to Length(bufFrame) - 1 do
				for Y := 0 to Length(bufLine) - 1 do
					bufLineAve[Y] := bufLineAve[Y] + bufFrame[Z, Y];
				// add up all the values in columns save in BufLineAve
				// Sum the bins by columns

			for Y := 0 to Length(bufLine) - 1 do
				bufLineAve[Y] := bufLineAve[Y] DIV Length(bufFrame); // Calculate the average weight of each bin

			for i := LeftEdge to RightEdge do
			begin
				SegAveLow := 0;
				for j := i - SegmentSize to i - 1 do
					SegAveLow := SegAveLow + bufLineAve[j];
				SegAveLow := SegAveLow DIV SegmentSize;

				SegAveHi := 0;
				for j := i + 1 to i + SegmentSize do
					SegAveHi := SegAveHi + bufLineAve[j];

				SegAveHi := SegAveHi Div SegmentSize;
				segmentAve := (SegAveLow + SegAveHi) div 2;
				currentAve := bufLineAve[i];
				currentLimit := bufLineLimit[i];

				if NOT(swShowAmplitudeLimit.Checked) then
					If (currentAve > (segmentAve + (segmentAve * (ThresholdPercent)))) then
						FoundET := True
					else
						FoundET := false;

				if (swShowAmplitudeLimit.Checked) then
					if (currentAve > (segmentAve + (segmentAve * (ThresholdPercent)))) then
					begin
						if (currentAve < currentLimit) then
							FoundET := True
						else
							FoundET := false;
					end;
				ET.WAVFileName := ExtractFileName(WAVFileNameAtEOFrame);
				if Water then
					ET.JPGFileName := ExtractFileName(JPGFileNameAtEOFrame);
				if FoundET then
				begin
						// =================   F O U N D   E.T.  =================
					ET.Hit := True;
					ledETBin.Checked := True;
						// saveWAV;
					with ET do
					begin
						Hit := True;
						ET.Bin := i;
						FreqOffset := FFT.FreqOfLine(ET.Bin, 1 / FSamplesPerSec);

						FFrequencyET := FrequencyReference + Trunc(FreqOffset);
						nlFrequencyET.value := FFrequencyET;

						ET.FrequencyET := FFrequencyET;

						ET.AudioFreq := Round(ET.Bin * FHzPerBin);

						ET.Amplitude := bufLineAve[i];

						frmSpecAna.SegmentSize := FSegmentSize;
						frmSpecAna.Integration := FIntegration;
						frmSpecAna.LinesPerFrame := FLinesPerFrame;

						sbSpecAna.Panels[CFile].Text := WAVFileName;
					end;
					nlETAmplitude.value := ET.Amplitude;

					neETBin.ValueAsInt := ET.Bin;

					ndxFrame := 0;
					RunHitBat;
					break;
						// Found ET so stop scanning left to right in the average column
				end
				else
				begin
					ET.Hit := false;

				end;
			end; // Left to Right

			BufferCount := (0);
			ndxFrame := 0;
			FoundET := false;

		end; // end of frame

	end; // if detect
		// FreeIt := true;   // WTF Jim
end;

	// procedure TfrmSpecAna.saveWAV;
	// var
	// FiletoSave: ANSIString;
	// // WAVFileName: Ansistring;
	// tempDIR: ANSIString;
	// begin
	//
	// WAVFileNameAtEOFrame := uTime.SETIDateTime + '.WAV';
	// ET.WAVFile := WAVFileNameAtEOFrame; //
	// tempDIR := CCaptureDrive + uTime.SETIDate;
	// tempDIR := ReplaceStr(tempDIR, '-', '\');
	// // if NOT DirectoryExists(CCaptureDrive + '\' + ReplaceStr(uTime.SETIDate, '-', '\')) then
	// // ForceDirectories(CCaptureDrive + '\' + ReplaceStr(uTime.SETIDate, '-', '\'));
	// if NOT DirectoryExists(tempDIR) then
	// if SysUtils.ForceDirectories(tempDIR) then
	// // ShowMessage('Dir: ' + tempDIR + ' added alright')
	// else
	// ShowMessage('Dir: ' + tempDIR + ' add FAILED');
	//
	// FiletoSave := CCaptureDrive + '\' + ReplaceStr(uTime.SETIDate, '-', '\') + '\' + WAVFileNameAtEOFrame;
	//
	// RiffWrite.AddChunk('SML ').WriteBuffer(Pointer(FSML)^, Length(FSML));
	// RiffWrite.SaveToFile(FiletoSave);
	//
	// RiffWrite.Clear; // Clear Buffer for next file
	// RiffWrite := CreateRiffRW;
	// RiffWrite.FourCC := 'WAVE';
	// with RiffWrite.AddChunk('fmt ') do
	// Write(AudioIn.FWaveFmtEx, SizeOf(AudioIn.FWaveFmtEx));
	// RiffWrite.AddChunk('data');
	// DataChunkIndex := RiffWrite.IndexOf('data');
	// end;
{$ENDREGION}
	//
{$REGION ' A U D I O   R e c o r d / P l a y '}

procedure TfrmSpecAna.setVuMeter(value: integer);
begin
	mtrVu.Position := value;
end;

procedure TfrmSpecAna.btnSelectRecordToFileClick(Sender: TObject);
begin
	WAVFileNameAtEOFrame := CreateWAVFileName;
	JPGFileNameAtEOFrame := CreateJPGFileName;
	SaveDialog.InitialDir := ExtractFileDir(WAVFileNameAtEOFrame);
	SaveDialog.FileName := ExtractFileName(WAVFileNameAtEOFrame);
	if SaveDialog.Execute(Handle) then
	begin
		btnRecord.Enabled := True;
		WAVFileNameAtEOFrame := SaveDialog.FileName;
	end;
end;

procedure TfrmSpecAna.btnStartLoggingClick(Sender: TObject);
begin
		//  TMSLogger.Active := Not TMSLogger.Active;
		//  if TMSLogger.Active then
		//  begin
		//
		//    TMSLogger.LogSystemInformation;
		//    TMSLogger.Info('Start Logging');
		//    ledLog.Checked := True;
		//    btnStartLogging.Caption := 'Logging ON';
		//  end
		//  else
		//  begin
		//    btnStartLogging.Caption := 'Logging OFF';
		//    ledLog.Checked := false;
		//  end;

end;

procedure TfrmSpecAna.btnRecordClick(Sender: TObject);
begin
	recordOffLine.RecordToFile(SaveDialog.FileName);
	sbSpecAna.Panels[CDir].Text := 'Record: ' + SaveDialog.FileName;
	Run := True;
	pnlControl.Enabled := false;
		// swRecord.Enabled := false;
		// swDetect.Enabled := false;

end;

procedure TfrmSpecAna.btnRecordPauseClick(Sender: TObject);
begin
	if recordOffLine.Active then
		recordOffLine.Paused := not recordOffLine.Paused
end;

procedure TfrmSpecAna.recordOffLineActivate(Sender: TObject);
begin

	Format.Enabled := false;
	btnSelectRecordToFile.Enabled := false;
	btnRecordPause.Enabled := True;
	btnRecordStop.Enabled := True;
	btnRecord.Enabled := false;
	btnPlay.Enabled := false;
	pnlPlayback.Enabled := false;
	btnRecordStop.SetFocus;
end;

procedure TfrmSpecAna.recordOffLineDeactivate(Sender: TObject);
begin
	Format.Enabled := True;
	pnlPlayback.Enabled := True;
	btnSelectRecordToFile.Enabled := True;
	btnSelectFileToPlay.Enabled := True;
	btnRecord.Enabled := True;

	btnRecordPause.Enabled := false;
	btnRecordStop.Enabled := false;

	if FileExists(WAVFileNameAtEOFrame) then
		setSML(WAVFileNameAtEOFrame);

	if Water then
	begin
		frmWaterFall.SaveJPG;

	end;

	sbSpecAna.Panels[CFile].Text := '';
	sbSpecAna.Panels[CDir].Text := '';
	pnlControl.Enabled := True;
		// swRecord.Enabled := True;
		// swDetect.Enabled := True;
end;

procedure TfrmSpecAna.recordOffLineLevel(Sender: TObject; Level: integer);
begin
	sbSpecAna.Panels[CFile].Text := MS2Str(recordOffLine.Position, msAh) + ' Sec.';
	if swVuMeterInput.State = tssOn then
		setVuMeter(Level);
end;

procedure TfrmSpecAna.recordSETIActivate(Sender: TObject);
begin
	sbSpecAna.Panels[CDir].Text := WAVFileNameAtEOFrame;
	FScan := True;
	ledScan.Checked := FScan;
		// TMSLogger.InfoFormat('RecordSETIActivate: {%b }', [True]);
end;

procedure TfrmSpecAna.recordSETIDeactivate(Sender: TObject);
begin
	FScan := false;
	if Water then

		frmWaterFall.SaveJPG;
	ledScan.Checked := FScan;
end;

procedure TfrmSpecAna.btnPlayPauseClick(Sender: TObject);
begin
	if StockAudioPlayer.Active then
		StockAudioPlayer.Paused := not StockAudioPlayer.Paused
end;

procedure TfrmSpecAna.btnPlayClick(Sender: TObject);
begin
	StockAudioPlayer.PlayFile(dlgOpen.FileName);
end;

procedure TfrmSpecAna.btnPlayStopClick(Sender: TObject);
begin
	if StockAudioPlayer.Active then
		StockAudioPlayer.Active := false
end;

procedure TfrmSpecAna.btnSelectFileToPlayClick(Sender: TObject);
begin
	if dlgOpen.Execute then
	begin
		btnPlay.Enabled := FileExists(dlgOpen.FileName);
		btnPlay.SetFocus;
	end;
end;

procedure TfrmSpecAna.StockAudioPlayerActivate(Sender: TObject);
begin
	Format.Enabled := false;
	btnSelectRecordToFile.Enabled := false;
	btnPlayPause.Enabled := True;
	btnPlayStop.Enabled := True;
	btnRecord.Enabled := false;
	btnPlay.Enabled := false;
	sbSpecAna.Panels[CDir].Text := 'Playing: ' + dlgOpen.FileName;
	pnlRecord.Enabled := false;
	btnPlayStop.SetFocus;
end;

procedure TfrmSpecAna.StockAudioPlayerDeactivate(Sender: TObject);
begin
	Format.Enabled := True;
	btnSelectRecordToFile.Enabled := True;
	btnRecord.Enabled := True;
	btnPlay.Enabled := True;
	btnPlayPause.Enabled := false;
	btnPlayStop.Enabled := false;
	sbSpecAna.Panels[CDir].Text := '';
	sbSpecAna.Panels[CFile].Text := '';
	pnlRecord.Enabled := True;
	btnPlay.SetFocus;
end;

procedure TfrmSpecAna.StockAudioPlayerLevel(Sender: TObject; Level: integer);
begin
	sbSpecAna.Panels[CFile].Text := MS2Str(StockAudioPlayer.Position, msAh) + ' Sec.';
	if swVuMeterInput.State = tssOn then
		setVuMeter(Level);
end;

{$ENDREGION}
	//
{$REGION ' U I  B U T T O N S '}

function TfrmSpecAna.GetFFTSize: integer;
begin
	result := FFT.SpectrumSize;
end;

function TfrmSpecAna.getLowerGuardpc: Single;
begin

		// result := chart.CrossHair2.PosX / (FFT.SpectrumSize DIV 2);
end;

procedure TfrmSpecAna.setLowerGuardpc(value: Single);
var
	test: Single;
begin
		// Test := value * (FFT.SpectrumSize DIV 2);
		//
		// if value < 0.05 then
		// chart.CrossHair2.PosX := 0.07 * (FFT.SpectrumSize DIV 2)
		// else
		// chart.CrossHair2.PosX := value * (FFT.SpectrumSize DIV 2);
end;

function TfrmSpecAna.getUpperGuardpc: Single;
begin
		// result := chart.CrossHair3.PosX / (FFT.SpectrumSize DIV 2);
end;

procedure TfrmSpecAna.setUpperGuardpc(value: Single);
begin
		// if value > 0.95 then
		// chart.CrossHair3.PosX := 0.9 * (FFT.SpectrumSize DIV 2)
		// else
		// chart.CrossHair3.PosX := value * (FFT.SpectrumSize DIV 2);

end;

procedure TfrmSpecAna.knobFFTSizeChange(Sender: TObject);
begin
	setFFTSize(knobFFTSize.SwitchPosition);

end;

procedure TfrmSpecAna.nsAmplitudeLimitValueChanged(Sender: TObject);
begin
	AmplitudeLimit := nsAmplitudeLimit.ValueAsInt;
end;

procedure TfrmSpecAna.nsLinesPerFrameLimit(Sender: TObject);
begin
		// LinesPerFrame := 1000;
		// Beep;
end;

procedure TfrmSpecAna.nsLinesPerFrameValueChanged(Sender: TObject);
begin
	LinesPerFrame := nsLinesPerFrame.ValueAsInt;

end;

procedure TfrmSpecAna.nsSegSizeValueChanged(Sender: TObject);
begin
	SegmentSize := frmSpecAna.nsSegSize.ValueAsInt;
end;

procedure TfrmSpecAna.ledETBinClick(Sender: TObject);
begin
	ResetRMT := True; // Reset the alarm
end;

	(* ==== S A V E   W A V  =============== *)

procedure TfrmSpecAna.cbPalettesChange(Sender: TObject);
type
	TMember = record
		R: byte;
		G: byte;
		B: byte;
	end;
var
	i: integer;
	Tokens: TOpenStringArray;
var
	List: TStringList;
	Stream: TResourceStream;
	cbItem: string;

begin
	cbItem := cbPalettes.Items[cbPalettes.ItemIndex];
	Stream := TResourceStream.Create(HInstance, cbItem, RT_RCDATA);
	try
		List := TStringList.Create;
		try
			List.LoadFromStream(Stream);
			i := List.Count - 1;
			while i > 0 do

			begin

				If (Tokenize(List[i], ' ', Tokens) = 3) then
				begin
					frmWaterFall.Palette[i].rgbtRed := byte(StrToInt(Tokens[0]));
					frmWaterFall.Palette[i].rgbtGreen := byte(StrToInt(Tokens[1]));
					frmWaterFall.Palette[i].rgbtBlue := byte(StrToInt(Tokens[2]));
					Dec(i);
				end
				else
				begin

					exit;
				end;
			end;
		finally
			List.Free
		end;
	finally
		Stream.Free;
	end;

end;

procedure TfrmSpecAna.cbWindowingSelect(Sender: TObject);
begin
	FFT.WeightingWindow := TFastFourierWgtWin(cbWindowing.ItemIndex);
end;

procedure TfrmSpecAna.chartMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
var
	percent: real;
begin
	if Run then
	begin
		if Button = mbRight then
		begin
			chart.CrossHair4.PosY := MousePosY;
			FWaterScale.RangeHigh := chart.CrossHair4.PosY;
		end;
		if Button = mbLeft then
		begin
			percent := ((MousePosX - 20) / (FFTSize DIV 2));
				// nlTest.value := frmWaterFall.HorzScrollBar.Size;
			frmWaterFall.HorzScrollBar.Position := Trunc(frmWaterFall.ImageRGBWidth * percent);
		end;
	end;
end;

procedure TfrmSpecAna.chartMouseMoveInChart(Sender: TObject; InChart: Boolean; Shift: TShiftState; rMousePosX, rMousePosY: Double);

begin
	if InChart then
	begin

		MousePosX := Round(rMousePosX);
		MousePosY := Trunc(rMousePosY);
		chart.CrossHair4.PosX := rMousePosX;
		chart.CrossHair4.PosY := rMousePosY;

		nlAudioFreq.value := FFT.FreqOfLine(MousePosX, 1 / FSamplesPerSec); //
		nlFrequencyReference.value := FrequencyReference + nlAudioFreq.value;

		frmWaterFall.FMouseFreq := Trunc(nlAudioFreq.value);
		nlAmplitude.value := FFT.Magnitude[MousePosX];
		if Run then
			nlMouseAmbiant.value := frmSpecAna.bufLineAve[MousePosX];

		if swVuMeterInput.State = tssOff then
				// setVuMeter(Trunc(FFT.Magnitude[MousePosX]));
				// setVuMeter(Trunc(FFT.PowerSpec[MousePosX]));
				// setVuMeter(Trunc(FFT.RealSpec[MousePosX]));
			setVuMeter(Trunc(FFT.RMS(MousePosX - 5, MousePosX + 5)));
			// nlTest.value := MousePosX;
		nlMouse.value := MousePosX;

	end;
end;

procedure TfrmSpecAna.dlgSaveDirClose(Sender: TObject);
begin
		// sbSpecAna.Panels[CDir].Text := MinimizeName(ReplaceStr(uTime.SETIDate, '-', '\'), sbSpecAna.Canvas, 250);
		// sbSpecAna.Panels[CFile].Text := MinimizeName(uTime.SETITime, sbSpecAna.Canvas, 250);
end;

procedure TfrmSpecAna.mnuExitClick(Sender: TObject);
begin
	Close
end;

procedure TfrmSpecAna.tsStatsMouseEnter(Sender: TObject);
begin
	StatsOn := True;
end;

procedure TfrmSpecAna.tsStatsMouseLeave(Sender: TObject);
begin
	StatsOn := false;
end;

procedure TfrmSpecAna.btnClearLogClick(Sender: TObject);
begin
	moLog.Clear;
end;

procedure TfrmSpecAna.btnHelpAboutClick(Sender: TObject);
begin
	frmAbout.ShowVersion;
end;

procedure TfrmSpecAna.btnLimitCaptureClick(Sender: TObject);
begin
	FCaptureLimit := True;
	btnLimitCapture.Color := clRed;
end;

procedure TfrmSpecAna.btnLoadSMLClick(Sender: TObject);
begin
	if dlgLoadSML.Execute then
	begin
		moSML.Lines.LoadFromFile(dlgLoadSML.FileName);
	end;
end;

procedure TfrmSpecAna.RunHitBat;
var
	BatName: string;
begin
	BatName := 'HitBat.bat';
	if FileExists(BatName) then
	begin
		ShellExecute(Application.Handle, 'open', PChar(BatName), '', '', SW_SHOWNORMAL);
	end
	else
	begin
		sbSpecAna.Panels[CAlert].Text := 'Cannot find ' + BatName + ' in the installation dir '
	end;
end;

procedure TfrmSpecAna.btnRunHitBatClick(Sender: TObject);

begin
	RunHitBat;
end;

procedure TfrmSpecAna.btnRecordStopClick(Sender: TObject);
begin
	recordOffLine.Active := false;

end;

procedure TfrmSpecAna.btnTagSMLClick(Sender: TObject);
begin
	setSML(WAVFileNameAtEOFrame);
end;

procedure CropBitmap(InBitmap, OutBitMap: TBitmap; X, Y, W, H: integer);
begin
	OutBitMap.PixelFormat := InBitmap.PixelFormat;
	OutBitMap.Width := W;
	OutBitMap.Height := H;
	BitBlt(OutBitMap.Canvas.Handle, 0, 0, W, H, InBitmap.Canvas.Handle, X, Y, SRCCOPY);
end;

procedure TfrmSpecAna.btnTestClick(Sender: TObject);
var
	TestStr: ANSIstring;

begin
		// function TfrmSpecAna.setSML(SML: ANSIstring): Boolean;
	TestStr := 'Now is the time for all good men...';
	setSML(TestStr);

end;

{$ENDREGION}
	//
{$REGION ' C L O S E  '}

procedure TfrmSpecAna.FormatChange(Sender: TObject);
begin
	recordOffLine.PCMFormat := TPCMFormat(Format.ItemIndex + 1);
end;

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

begin
	Run := false;
	AudioIn.Active := false;
	SETINetReg := TRegistry.Create;
		// S A V E  R E G   E N T R I E S
	try
		with SETINetReg do
		begin
			RootKey := HKEY_CURRENT_User;
			DeleteKey(SETINetSpectrumAnalyzerKey);
				// Kill the whole key every time.
			OpenKey(SETINetSpectrumAnalyzerKey, True); // Then write it back
				// OpenKey will create the key if the second parameter is true and the key does not already exist

			WriteInteger('Integration', Integration);
			WriteInteger('knobFFTSize', knobFFTSize.SwitchPosition);
			WriteFloat('VerticalRange', VerticalRange);
			WriteInteger('SegmentSize', SegmentSize);
			WriteInteger('LinesPerFrame', LinesPerFrame);

			WriteFloat('LowerGuard', chart.CrossHair2.PosX / (FFT.SpectrumSize DIV 2));
			WriteFloat('UpperGuard', chart.CrossHair3.PosX / (FFT.SpectrumSize DIV 2));
			WriteInteger('Threshold', ThresholdRMT);
			WriteInteger('Left', frmSpecAna.Left);
			WriteInteger('Top', frmSpecAna.Top);
			WriteInteger('LineLimit', AmplitudeLimit);

				//
			WriteInteger('WaterTop', frmWaterFall.Top);
			WriteInteger('WaterLeft', frmWaterFall.Left);
			WriteInteger('WaterHeight', frmWaterFall.Height);
			WriteInteger('WaterWidth', frmWaterFall.Width);

				// cbFormat.ItemIndex := ReadInteger();
			CloseKey;
		end;
			// SETINetReg
	finally
		SETINetReg.Free;
	end; // try..finally

end;

	//
procedure TfrmSpecAna.DisplayHint(Sender: TObject);

begin
	if cbShowLongHints.Checked then
		sbSpecAna.Panels[CAlert].Text := GetLongHint(Application.Hint);
end;

procedure TfrmSpecAna.FormActivate(Sender: TObject);
begin
		{ Assign the application’s OnHint event handler at runtime
			because the Application is not available in the Object Inspector at design time }
	Application.OnHint := DisplayHint;

end;

procedure TfrmSpecAna.cbShowHintsClick(Sender: TObject);
begin
	Application.ShowHint := cbShowHints.Checked;
		// frmSpecAna.ShowHint := cbShowHints.Checked;
end;

initialization

uRORTTIServerSupport.RODLLibraryName := 'NewLibrary';
{$ENDREGION}

end.
