unit CommandAndControlU;

//Uses SAPI 5.1 early bound Automation
// large portions of code are directly modified from Brian Long's article

interface

uses
  ActiveX,  Tr85u,
{$ifdef CONDITIONALEXPRESSIONS}
  Variants,
{$endif}
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, OleServer, SpeechLib_TLB, ShellAPI, OoMisc,
  AdPort, Menus, ExtCtrls, AdPacket, unit1, Buttons;

type
  TfrmCommandAndControl = class(TForm)
    lstProgress: TListBox;
    GroupBox1: TGroupBox;
    ProgressBar: TProgressBar;
    lblMinVU: TLabel;
    lblMaxVU: TLabel;
    lblVU: TLabel;
    Label3: TLabel;
    SpSharedRecoContext: TSpSharedRecoContext;
    btnEngineProps: TButton;
    btnUserSettings: TButton;
    btnLexicon: TButton;
    btnTrainGeneral: TButton;
    btnTrainMic: TButton;
    btnAudioProps: TButton;
    btnAudioVol: TButton;
    Button1: TButton;
    ComPort1: TApdComPort;
    Label1: TLabel;
    Button3: TButton;
    CheckBox1: TCheckBox;
    SpVoice1: TSpVoice;
    ComboBox1: TComboBox;
    Memo1: TMemo;
    Mode: TComboBox;
    MainMenu1: TMainMenu;
    EXIT1: TMenuItem;
    Exit2: TMenuItem;
    About1: TMenuItem;
    Aboutthesoftware1: TMenuItem;
    CheckBox2: TCheckBox;
    Label2: TLabel;
    Image1: TImage;
    KVFOA: TApdDataPacket;
    Label4: TLabel;
    KVFOB: TApdDataPacket;
    Label5: TLabel;
    Label6: TLabel;
    Edit1: TEdit;
    StatusBar1: TStatusBar;
    DataPacket1: TApdDataPacket;
    Memo2: TMemo;
    ComboBox2: TComboBox;
    RadioGroup1: TRadioGroup;
    Label7: TLabel;
    Button6: TButton;
    Button7: TButton;
    Help1: TMenuItem;
    Help2: TMenuItem;
    Preferences1: TMenuItem;
    HideTraining1: TMenuItem;
    ShowEngineLog1: TMenuItem;
    ShowRS232Stream1: TMenuItem;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Button2: TButton;
    Button8: TBitBtn;
    Button9: TBitBtn;
    Button4: TBitBtn;
    Button5: TBitBtn;
    ArtificialSpeechAnnouncements1: TMenuItem;
    Timer1: TTimer;
    Edit2: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure SpSharedRecoContextAudioLevel(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant;
      AudioLevel: Integer);
    procedure SpSharedRecoContextFalseRecognition(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant;
      var Result: OleVariant);
    procedure SpSharedRecoContextHypothesis(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant;
      var Result: OleVariant);
    procedure SpSharedRecoContextInterference(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant;
      Interference: TOleEnum);
    procedure SpSharedRecoContextPhraseStart(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant);
    procedure SpSharedRecoContextRecognition(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant;
      RecognitionType: TOleEnum; var Result: OleVariant);
    procedure SpSharedRecoContextRecognitionForOtherContext(
      Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant);
    procedure SpSharedRecoContextRequestUI(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant;
      var UIType: OleVariant);
    procedure SpSharedRecoContextSoundStart(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant);
    procedure SpSharedRecoContextSoundEnd(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant);
    procedure SpSharedRecoContextStartStream(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant);
    procedure SpSharedRecoContextEndStream(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant;
      StreamReleased: WordBool);
    procedure btnEnginePropsClick(Sender: TObject);
    procedure btnUserSettingsClick(Sender: TObject);
    procedure btnLexiconClick(Sender: TObject);
    procedure btnTrainGeneralClick(Sender: TObject);
    procedure btnTrainMicClick(Sender: TObject);
    procedure btnAudioPropsClick(Sender: TObject);
    procedure btnAudioVolClick(Sender: TObject);
    procedure SpSharedRecoContextAdaptation(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant);
    procedure SpSharedRecoContextBookmark(Sender: TObject;
      StreamNumber: Integer; StreamPosition, BookmarkId: OleVariant;
      Options: TOleEnum);
    procedure SpSharedRecoContextEnginePrivate(Sender: TObject;
      StreamNumber: Integer; StreamPosition, EngineData: OleVariant);
    procedure SpSharedRecoContextPropertyNumberChange(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant;
      var PropertyName: OleVariant; NewNumberValue: Integer);
    procedure SpSharedRecoContextPropertyStringChange(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant; var PropertyName,
      NewStringValue: OleVariant);
    procedure SpSharedRecoContextRecognizerStateChange(Sender: TObject;
      StreamNumber: Integer; StreamPosition: OleVariant;
      NewState: TOleEnum);
    procedure Button1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Aboutthesoftware1Click(Sender: TObject);
    procedure ComboBox1Change(Sender: TObject);
    procedure Exit2Click(Sender: TObject);
    procedure KVFOAStringPacket(Sender: TObject; Data: String);
    procedure KVFOBStringPacket(Sender: TObject; Data: String);
    procedure Edit1Change(Sender: TObject);
    procedure ModeChange(Sender: TObject);
    procedure DataPacket1Packet(Sender: TObject; Data: Pointer;
      Size: Integer);

    procedure HexLog(p: pointer; len: integer; cm: string) ;
    procedure ComboBox2Change(Sender: TObject);
    procedure RadioGroup1Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure Button9Click(Sender: TObject);
    procedure ShowEngineLog1Click(Sender: TObject);
    procedure HideTraining1Click(Sender: TObject);
    procedure CheckBox2Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure ArtificialSpeechAnnouncements1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Edit2Change(Sender: TObject);
    procedure Help2Click(Sender: TObject);

  private
    SRGrammar: ISpeechRecoGrammar;
    procedure InvokeUI(const TypeOfUI, Caption: WideString);
  public
    procedure Log(const Msg: String); overload;
    procedure Log(const Msg: String; const Args: array of const); overload;
  end;

var
  frmCommandAndControl: TfrmCommandAndControl;
  confidence:boolean;  //how sure is the recognizer
  interference1:boolean;
  Imodel:variant; //Icom model

  rShift:integer; //shift freq

  Radio: TfrmCommandAndControl ;            { main form }
  ICOMS: Icom ;              { radio state }
  ts: string ;               { temp string }

  ASpeech:boolean; //use artificial speech capabilities?

implementation

{$R *.dfm}

uses
  SAPI, ComObj;

{ TfrmVoiceDictationAPI }


procedure MemPCopy(p: PChar; s: string) ;
var
  i: integer ;
  q: PChar ;
begin
  q := p ;
  for i := 1 to length(s) do
    begin
      q^ := s[i] ;
      inc(q) ;
    end ;
end ;

function MemCopy(p: PChar; len: integer): string ;
var
  i: integer ;
  s: string ;
begin
  SetLength(s,len) ;
  for i := 1 to len do
    s[i] := p[i-1] ;
  result := s ;
end ;

procedure TfrmCommandAndControl.HexLog(p: pointer; len: integer; cm: string) ;
var
  c: byte ;
  i: integer ;
  t: string ;
  pc: PChar ;
  frequency:string;
begin
  /// Log.Lines.Add(Format('%s (%d bytes follow): ',[cm,len])) ;
  pc := PChar(p) ;
  t := '' ;
  for i := 0 to len-1 do
    begin
      c := ord(pc[i]) ;
      t := t + format('%.2x ',[c]) ;
     end ;

   memo2.Lines.Add(t);
   edit1.text:=t;
end ;


function MyExitWindows(RebootParam: Longword): Boolean;
var
  TTokenHd: THandle;
  TTokenPvg: TTokenPrivileges;
  cbtpPrevious: DWORD;
  rTTokenPvg: TTokenPrivileges;
  pcbtpPreviousRequired: DWORD;
  tpResult: Boolean;
const
  SE_SHUTDOWN_NAME = 'SeShutdownPrivilege';
begin
  if Win32Platform = VER_PLATFORM_WIN32_NT then
  begin
    tpResult := OpenProcessToken(GetCurrentProcess(),
      TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,
      TTokenHd);
    if tpResult then
    begin
      tpResult := LookupPrivilegeValue(nil,
                                       SE_SHUTDOWN_NAME,
                                       TTokenPvg.Privileges[0].Luid);
      TTokenPvg.PrivilegeCount := 1;
      TTokenPvg.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
      cbtpPrevious := SizeOf(rTTokenPvg);
      pcbtpPreviousRequired := 0;
      if tpResult then
        Windows.AdjustTokenPrivileges(TTokenHd,
                                      False,
                                      TTokenPvg,
                                      cbtpPrevious,
                                      rTTokenPvg,
                                      pcbtpPreviousRequired);
    end;
  end;
  Result := ExitWindowsEx(RebootParam, 0);
end;


procedure TfrmCommandAndControl.Log(const Msg: String);
begin
  if not Assigned(lstProgress) then
    Exit;
  lstProgress.Items.Add(Msg);
  lstProgress.ItemIndex := lstProgress.Items.Count - 1
end;

procedure TfrmCommandAndControl.Log(const Msg: String; const Args: array of const);
begin
  Log(Format(Msg, Args))
end;

procedure TfrmCommandAndControl.FormCreate(Sender: TObject);
begin
  //OnAudioLevel event i
  TR85u.ICOM.Create ;//s not fired by default - this changes that
  SpSharedRecoContext.EventInterests := SREAllEvents;
  SRGrammar := SpSharedRecoContext.CreateGrammar(0);
  SRGrammar.CmdLoadFromFile('C and C Grammar.xml', SLODynamic);
  SRGrammar.CmdSetRuleIdState(0, SGDSActive);

  Combobox1.text:=Combobox1.Items[0]; //radio type
  Combobox2.text:=Combobox2.Items[4]; // 20 mtr band
  Mode.text:=Mode.Items[1]; //manual mode change

  RadioGroup1.ItemIndex := 0; //default to small shift
  //ICOM stuff
  tr85u.ICOM.Create ;  //required to create functions for radio state

   ASpeech:=false; //don't use artificial speech capabilities at start-up
end;

procedure TfrmCommandAndControl.SpSharedRecoContextAudioLevel(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  AudioLevel: Integer);
begin
  ProgressBar.Position := AudioLevel;
  lblVU.Caption := IntToStr(AudioLevel)
end;

procedure TfrmCommandAndControl.SpSharedRecoContextFalseRecognition(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  var Result: OleVariant);
begin
  Log('OnFalseRecognition: %s, SAPI confidence: %s, engine confidence: %s',
    [Result.PhraseInfo.GetText,
     Result.PhraseInfo.Rule.Confidence,
     Result.PhraseInfo.Rule.EngineConfidence]);
end;

procedure TfrmCommandAndControl.SpSharedRecoContextHypothesis(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  var Result: OleVariant);
begin
  Log('OnHypothesis: %s', [Result.PhraseInfo.GetText]);
end;

procedure TfrmCommandAndControl.SpSharedRecoContextInterference(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  Interference: TOleEnum);
var
  S: String;
begin
  interference1:=false;
  case Interference of
    SINone:     S := 'none';
    SINoise:    S := 'noise';
    SINoSignal: S := 'no signal';
    SITooLoud:  S := 'too loud for discrete recognition';
    SITooQuiet: S := 'too quiet for discrete recognition';
    SITooFast:  S := 'too fast for discrete recognition';
    SITooSlow:  S := 'too slow';
  end;
  Log('OnInterference: %s', [S]);
  If interference in [SINoise, SINoSignal, SITooLoud,SITooQuiet,SITooFast,SITooSlow] then
    interference1:=true  //eliminate the garbage
  else interference1:=false;
end;

procedure TfrmCommandAndControl.SpSharedRecoContextPhraseStart(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant);
begin
  Log('OnPhraseStart');
end;

function GetProp(Props: ISpeechPhraseProperties;
  const Name: String): ISpeechPhraseProperty; overload;
var
  I: Integer;
  Prop: ISpeechPhraseProperty;
begin
  Result := nil;
  for I := 0 to Props.Count - 1 do
  begin
    Prop := Props.Item(I);
    if CompareText(Prop.Name, Name) = 0 then
    begin
      Result := Prop;
      Break
    end
  end
end;

function GetPropValue(SRResult: ISpeechRecoResult;
  const Path: array of String): OleVariant;
var
  Prop: ISpeechPhraseProperty;
  PathLoop: Integer;
begin
  for PathLoop := Low(Path) to High(Path) do
  begin
    if PathLoop = Low(Path) then //top level property
      Prop := GetProp(SRResult.PhraseInfo.Properties, Path[PathLoop])
    else //nested property
      Prop := GetProp(Prop.Children, Path[PathLoop]);
    if not Assigned(Prop) then
    begin
      Result := Unassigned;
      Exit;
    end
  end;
  Result := Prop.Value
end;

procedure TfrmCommandAndControl.SpSharedRecoContextRecognition(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  RecognitionType: TOleEnum; var Result: OleVariant);
var
  SRResult: ISpeechRecoResult;
begin
  SRResult := IDispatch(Result) as ISpeechRecoResult;
  with SRResult.PhraseInfo do
    begin
      Log('OnRecognition: %s', [GetText(0, -1, True)]); //0 is the place it
      //starts recognizing; -1 tells it to recognize all words - a 1 would
      //recognize one word, a 2 would recognize two words etc.

      if (checkbox2.checked) and (interference1 = false) then
        case GetPropValue(SRResult, ['chosencommand', 'commandvalue']) of
          1: begin
               Color := clRed;
               label1.caption:='Red';
             end;
          2: begin
               Color := clBlue;
               label1.caption:='Blue';
             end;
          3: begin
               Color := clGreen;
               label1.caption:='Green';
             end;
          4: begin
               Color := clYellow;
               label1.caption:='Yellow';
             end;
          5: begin
               Color := clBlack;
               label1.caption:='Black';
             end;
          6: begin
               Color := clGray;
               label1.caption:='Gray';
             end;
          7: begin
               Color := clNavy;
               label1.caption:='Navy';
             end;
          8: begin  //LSB
               Color := clBtnFace;
               Try
                 Case Combobox1.itemindex of
                   1: begin  //for Kenwood  mode
                        comport1.PutString('MD1;'); //LSB   mode
                       end;
                   2..11: begin   //for ICOM
                            ts := Icoms.SetMode(trmLSB);
                            comport1.Putstring(ts);
                           end;
                   else begin
                          // ignore, do nothing
                        end;
                   end; //end case

                except
                 // do nothing
                end;
               label1.caption:='LSB';
             end;
           9: begin //USB
                Color := clBtnFace;
                label1.caption:='USB';
                Try
                  Case Combobox1.itemindex of
                  1: begin  //for Kenwood
                       comport1.PutString('MD2;');   //USB
                     end;
                  2..11:begin   //for ICOM
                          ts := Icoms.SetMode(trmUSB);
                          comport1.Putstring(ts);
                        end;
                  else begin
                       end;
                   end; //end case

                 except

               end;

            end;
      10: begin   //CW
            Color := clBtnFace;

            Try
              Case Combobox1.itemindex of
                1: begin  //for Kenwood
                     comport1.PutString('MD3;');   //cw
                   end;
                2..11:begin   //for ICOM
                        ts := Icoms.SetMode(trmCW);
                        comport1.Putstring(ts);
                      end;
                else begin
                     end;
               end; //end case

              except

              end;
             //CW
            label1.caption:='CW';
          end;
      11: begin
            Color := clBtnFace;
            //FM
            Try
              Case Combobox1.itemindex of
                1: begin  //for Kenwood
                     comport1.PutString('MD4;');   //FM
                    end;
                2..11: begin   //for ICOM
                         ts := Icoms.SetMode(trmFM);
                         comport1.Putstring(ts);
                        end;
               else begin
                    end;
               end; //end case

              except

              end;

            label1.caption:='FM';
          end;
      12: begin
            Color := clBtnFace;
            //AM
            label1.caption:='AM';
            Try
              Case Combobox1.itemindex of
                1: begin  //for Kenwood
                     comport1.PutString('MD5;');   //AM
                    end;
                2..11:begin   //for ICOM
                        ts := Icoms.SetMode(trmAM);
                        comport1.Putstring(ts);
                      end;
                else begin
                     end;
               end; //end case

              except

              end;

          end;
      13: begin
            Color := clBtnFace;
            //RTTY
              Try
                Case Combobox1.itemindex of
                  1: begin  //for Kenwood
                       comport1.PutString('MD6;');   //RTTY
                     end;
                  2..11: begin   //for ICOM
                           ts := Icoms.SetMode(trmRTTY);
                           comport1.Putstring(ts);
                         end;
               else begin
                    end;
               end; //end case

              except

              end;

           label1.caption:='RTTY';
          end;
      14: begin
            Color := clBtnFace;
            //increase freq
            button4.click;  //icom and kenwood up routines
            label1.caption:='Increase frequency';
          end;
      15: begin //decrease freq
            Color := clBtnFace;
            button5.click;  //icom and kenwood down routines
            label1.caption:='Decrease frequency';
          end;
      16: begin  //windows shut down
            Color := clBtnFace;
            button2.click;
            label1.caption:='Windows shut-down';
          end;
      17: begin  //windows sleep
            Color := clBtnFace;
            label1.caption:='Windows Sleep';
            SendMessage(Handle, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
          end;
      18: begin  //shiftselect small
            Color := clBtnFace;
            label1.caption:='Shift Small';
            RadioGroup1.ItemIndex :=0;
          end;
      19: begin //shift select medium
            Color := clBtnFace;
            label1.caption:='Shift Medium';
            RadioGroup1.ItemIndex :=1;
          end;
      20: begin //shift select large
            Color := clBtnFace;
            label1.caption:='Shift Large';
            RadioGroup1.ItemIndex :=2;
          end;
      21: begin  //kenwood band up
            Color := clBtnFace;
            label1.caption:='Band Up';
            Button8.click;
          end;
      22: begin //kenwood band down
            Color := clBtnFace;
            label1.caption:='Band down';
            Button9.click;
          end;

     else
        begin
          Color := clBtnFace;
        end;
    end ;
    interference1 := false
  end
end;

procedure TfrmCommandAndControl.SpSharedRecoContextRecognitionForOtherContext(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant);
begin
  Log('OnRecognitionForOtherContext')
end;

procedure TfrmCommandAndControl.SpSharedRecoContextRequestUI(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  var UIType: OleVariant);
begin
  Log('OnRequestUI: %s', [UIType])
end;

procedure TfrmCommandAndControl.SpSharedRecoContextSoundStart(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant);
begin
  Log('OnSoundStart')
end;

procedure TfrmCommandAndControl.SpSharedRecoContextSoundEnd(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant);
begin
  Log('OnSoundEnd')
end;

procedure TfrmCommandAndControl.SpSharedRecoContextStartStream(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant);
begin
  Log('OnStartStream: stream %d', [StreamNumber])
end;

procedure TfrmCommandAndControl.SpSharedRecoContextEndStream(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  StreamReleased: WordBool);
begin
  Log('OnEndStream: stream %d', [StreamNumber])
end;

procedure TfrmCommandAndControl.btnEnginePropsClick(Sender: TObject);
begin
  //Only available in SAPI 5.0; not in SAPI 5.1 or later
  InvokeUI(SPDUI_EngineProperties, 'Engine Properties')
end;

procedure TfrmCommandAndControl.btnUserSettingsClick(Sender: TObject);
begin
  InvokeUI(SPDUI_RecoProfileProperties, 'User Settings')
end;

procedure TfrmCommandAndControl.btnLexiconClick(Sender: TObject);
begin
  InvokeUI(SPDUI_AddRemoveWord, 'Add/Remove Word')
end;

procedure TfrmCommandAndControl.btnTrainGeneralClick(Sender: TObject);
begin
  InvokeUI(SPDUI_UserTraining, 'Speaker Training')
end;

procedure TfrmCommandAndControl.btnTrainMicClick(Sender: TObject);
begin
  InvokeUI(SPDUI_MicTraining, 'Microphone Setup')
end;

procedure TfrmCommandAndControl.btnAudioPropsClick(Sender: TObject);
begin
  InvokeUI(SPDUI_AudioProperties, 'Audio Properties')
end;

procedure TfrmCommandAndControl.btnAudioVolClick(Sender: TObject);
begin
  InvokeUI(SPDUI_AudioVolume, 'Audio Volume')
end;

procedure TfrmCommandAndControl.InvokeUI(const TypeOfUI, Caption: WideString);
var
  U: OleVariant;
begin
  U := Unassigned;
  if SpSharedRecoContext.Recognizer.IsUISupported(TypeOfUI, U) then
    SpSharedRecoContext.Recognizer.DisplayUI(Handle, Caption, TypeOfUI, U)
end;

procedure TfrmCommandAndControl.SpSharedRecoContextAdaptation(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant);
begin
  Log('OnAdaptation')
end;

procedure TfrmCommandAndControl.SpSharedRecoContextBookmark(
  Sender: TObject; StreamNumber: Integer; StreamPosition,
  BookmarkId: OleVariant; Options: TOleEnum);
begin
  Log('OnBookmark: %d', [Integer(BookmarkId)])
end;

procedure TfrmCommandAndControl.SpSharedRecoContextEnginePrivate(
  Sender: TObject; StreamNumber: Integer; StreamPosition,
  EngineData: OleVariant);
begin
  Log('OnEnginePrivate')
end;

procedure TfrmCommandAndControl.SpSharedRecoContextPropertyNumberChange(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  var PropertyName: OleVariant; NewNumberValue: Integer);
begin
  Log('OnPropertyNumberChange: %s=%d', [WideString(PropertyName), NewNumberValue])
end;

procedure TfrmCommandAndControl.SpSharedRecoContextPropertyStringChange(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  var PropertyName, NewStringValue: OleVariant);
begin
  Log('OnPropertyStringChange: %s=%s', [WideString(PropertyName), WideString(NewStringValue)])
end;

procedure TfrmCommandAndControl.SpSharedRecoContextRecognizerStateChange(
  Sender: TObject; StreamNumber: Integer; StreamPosition: OleVariant;
  NewState: TOleEnum);
var
  S: String;
begin
  case NewState of
    SRSInactive:          S := 'inactive';
    SRSActive:            S := 'active';
    SRSActiveAlways:      S := 'active always';
    SRSInactiveWithPurge: S := 'inactive with purge';
  end;
  Log('OnRecognizerStateChange')
end;

procedure TfrmCommandAndControl.Button1Click(Sender: TObject);
var
  MyWin: array[0..255] of Char;
const
  Size: Integer = MAX_PATH;
begin
  try
    begin
      GetWindowsDirectory(MyWin, Size);
      ShellExecute(
      frmCommandAndControl.Handle,
      'open',
      'sndvol32.exe',
      nil,
      MyWin,
      SW_SHOWNORMAL);
    end
  except
  end;
end;

procedure TfrmCommandAndControl.Button3Click(Sender: TObject);
begin
  Try
    ComPort1.PutString('TC 1;');  //packet off
    sleep(20);
    ComPort1.PutString('AI2;');
    comport1.PutString('MD2;');//mode  set as USB
    ComPort1.PutString('CM0;');//PCT off
    ComPort1.PutString('FC;');
    //   ComPort1.PutString('FB;');
    //   ComPort1.PutString('FB;');
    ComPort1.PutString('FA;');
    Comport1.PutString('FR0;'); //start withVFOA always  to keep the code simple
    ComPort1.PutString('MF0;');//set the 'A' MENU
  except
    //
  end;
end;

procedure TfrmCommandAndControl.CheckBox1Click(Sender: TObject);
var    //main preamp
PAs:string;
PA:string;
begin
    If checkbox1.checked= true then
      begin
        comport1.PutString('PA1;');
        // if aspeech then SpVoice1.Speak('pre amp on' ,SVSFLAGsAsync);
      end
    else
      begin
        comport1.PutString('PA0;');
        // if aspeech then SpVoice1.Speak('pre amp off' ,SVSFLAGsAsync);
      end;
end;

procedure TfrmCommandAndControl.Button4Click(Sender: TObject);
var
freqfloat:real;
begin    //freq move up
  Try
   Case Combobox1.itemindex of
   1: begin  //for Kenwood
         // If aspeech=true then SpVoice1.Speak('Frequency Up' ,SVSFLAGsAsync);
         //comport1.PutString('UP'+'99'+';');
         //comport1.PutString('UP'+'01'+';');   //Frequency  UP 1
         button6.click;   //a better way to change
      end;
   2..4:begin   //for ICOM
          ts:=Icoms.SetFreq(strtoint(label5.caption)+rShift) ;  //shift 1000
          Comport1.PutString(ts);
          Comport1.PutString(ts);
        
          label5.caption:=  inttostr(strtoint(label5.caption)+rShift);
          freqfloat:=(strtoint(label5.caption)*0.000001 );
          label7.caption:= Format('%3.6f', [(freqfloat)]);//   Edit1.text:='';
        end;
   else begin
         end;
   end; //end case

  except

  end;
end;

procedure TfrmCommandAndControl.Button5Click(Sender: TObject);
var
freqfloat:real;
begin        // Frequency down
  Try
   Case Combobox1.itemindex of
   1: begin  //for Kenwoods
         // comport1.PutString('DN'+'99'+';');
         // comport1.PutString('DN'+'01'+';');   //Frequency  down 1 kHz (the two commands)
         button7.click; //a better way
       end;
    2..4:begin   //For ICOM radios
           ts:=Icoms.SetFreq(strtoint(label5.caption)-rShift) ;
           Comport1.PutString(ts);
           Comport1.PutString(ts);
           label5.caption:=  inttostr(strtoint(label5.caption)-rShift);
           freqfloat:=(strtoint(label5.caption)*0.000001 );
           label7.caption:= Format('%3.6f', [(freqfloat)]); end;
    else
    end; //end case

    except

   end;
end;

procedure TfrmCommandAndControl.Aboutthesoftware1Click(Sender: TObject);
begin
 Application.MessageBox('  Copyright © 2006 Stephen J. Gradijan, WB5KIA'+#10+#13+'                    Command and Control'+#10+#13+ #10+#13+'                         wb5kia@arrl.net'+#10+#13+ #10+#13+'       Based on SAPI link code by Brian Long  ', 'About Command and Control',
        MB_OK ) ;
end;

procedure TfrmCommandAndControl.ComboBox1Change(Sender: TObject);
var
freqfloat:real;
begin
  label8.visible:=false;
  label9.visible:=false;
  label10.visible:=false;
  label11.visible:=false;
  label12.visible:=false;
  case combobox1.itemindex of
  0: begin
       Label4.visible:=false;
       Label5.visible:=false;
       Label7.visible:=false;
       button4.visible:=false;
       button5.visible:=false;
       button8.visible:=false;
       button9.visible:=false;
       mode.visible:=false;
       combobox2.visible:=false;
       Radiogroup1.visible:=false;
     end;

  1: begin
       button3.click; //to set up Kenwood automatic data transferal
       button4.visible:=true;
       button5.visible:=true;
       button8.visible:=true;
       button9.visible:=true;
       mode.visible:=true ;
       Label4.visible:=true;
       Label5.visible:=false;
       Label7.visible:=false;
       datapacket1.autoenable:=false;
       datapacket1.enabled:=false;
        kvfoa.Autoenable:=True;
      kvfoA.Enabled:=true;
       combobox2.visible:=false;
       Radiogroup1.visible:=true;
       //Kenwood
     end;
  2: begin
       button4.visible:=true;
       button5.visible:=true;
       mode.visible:= true;
       datapacket1.enabled:=true;
       Imodel:= $58 ;// 706MKIIG
       Radiogroup1.visible:=true;
      end;
  3: begin
       button4.visible:=true;
       button5.visible:=true;
       mode.visible:= true;
       datapacket1.enabled:=true;
       Imodel:= $56;  //746
       Radiogroup1.visible:=true;
     end;
  4: begin
       button4.visible:=true;
       button5.visible:=true;
       mode.visible:= true;
       datapacket1.enabled:=true;
       Imodel:= $5C;  //756P
       Radiogroup1.visible:=true;
     end;

  5: begin
       button3.click;
       button4.visible:=true;
       button5.visible:=true;
       mode.visible:=true;
       datapacket1.enabled:=true;
       Imodel:= $48; //706
       Radiogroup1.visible:=true;
     end;
  6: begin
       button4.visible:=true;
       button5.visible:=true;
       mode.visible:= true;
       datapacket1.enabled:=true;
       Imodel:= $4E; //706MKII
       Radiogroup1.visible:=true;
      end;
  7: begin
       button4.visible:=true;
       button5.visible:=true;
       mode.visible:= true;
       datapacket1.enabled:=true;
       Imodel:= $66  ; //746P
       Radiogroup1.visible:=true;
     end;
  8: begin
       button4.visible:=true;
       button5.visible:=true;
       mode.visible:= true;
       datapacket1.enabled:=true;
       Imodel:= $50; //756
       Radiogroup1.visible:=true;
     end;

   9: begin
      //  button3.click;
        button4.visible:=true;
        button5.visible:=true;
        mode.visible:=true;
        datapacket1.enabled:=true;
        Imodel:= $64; //756PII
        Radiogroup1.visible:=true;
      end;
  10: begin
        button4.visible:=true;
        button5.visible:=true;
        mode.visible:= true;
        datapacket1.enabled:=true;
        Imodel:= $6E; //756PIII
        Radiogroup1.visible:=true;
      end;
  11: begin
        button4.visible:=true;
        button5.visible:=true;
        mode.visible:= true;
        datapacket1.enabled:=true;
        Imodel:= $6A  ; //7800
        Radiogroup1.visible:=true;
     end;
  12: begin
        button4.visible:=true;
        button5.visible:=true;
        mode.visible:= true;
        datapacket1.enabled:=true;
        Imodel:= $68; //703
        Radiogroup1.visible:=true;
     end;
  else
    // do nothing
  end;//case

  label6.caption:=Imodel;
  unit1.Form1.Label1.caption:=Imodel;

  if combobox1.itemindex > 1 then
    begin
      ComPort1.ComNumber := 1;
      ComPort1.Baud := 19200;
      ComPort1.DataBits := 8;
      ComPort1.StopBits :=  1;
      ComPort1.Parity := pNone ;

      kvfoA.Enabled:=false;
      kvfob.enabled:=false;
      button8.visible:=false;
      button9.visible:=false;
      combobox2.visible:=true;
      label4.visible:=false;
      label7.visible:=true;
      combobox2.SetFocus;
      freqfloat:=(strtoint(label5.caption)*0.000001 );
      label7.caption:= Format('%3.6f', [(freqfloat)]);
      StatusBar1.Panels[0].Text := 'Connection opened (19200 bps).' ;
    end
    else
    begin
      ComPort1.ComNumber := 1;
      ComPort1.Baud := 57600;
      ComPort1.DataBits := 8;
      ComPort1.StopBits :=  1;
      ComPort1.Parity := pNone ;
      StatusBar1.Panels[0].Text := 'Connection opened (57600 bps).' ;
      kvfoa.Autoenable:=True;
      kvfoA.Enabled:=true;
   //   kvfob.enabled:=true;

    end;
   StatusBar1.Panels[2].Text := Combobox1.text ;
end;

procedure TfrmCommandAndControl.Exit2Click(Sender: TObject);
begin
  Comport1.autoopen:=false;//close down the com port
  Close;
end;

procedure TfrmCommandAndControl.KVFOAStringPacket(Sender: TObject;
  Data: String);
var
dummy:string;
dummyf:real;
begin   //VFO A
   label4.caption:= data;
   if length(data)=14 then
     begin
      dummy:=copy(data,3,length(data)-3);
      dummyf:= strtofloat(dummY)*0.000001;
      dummy:= floattostr(dummyf);
      label4.caption:=floattostrf(dummyf,ffFixed,8,5);
      edit2.text:= label4.caption; //to engage automatic freq announcement

      ComPort1.PutString('IF;');
    end;
end;


procedure TfrmCommandAndControl.KVFOBStringPacket(Sender: TObject;
  Data: String);
var
dummy:string;
dummyf:real;
begin   //VFO B      //not used in this version
   label4.caption:= data;
   if length(data)=14 then
     begin
      dummy:=copy(data,3,length(data)-3);
      dummyf:= strtofloat(dummY)*0.000001;
      dummy:= floattostr(dummyf);
      label4.caption:=floattostrf(dummyf,ffFixed,8,5);

      ComPort1.PutString('IF;');
    end;
end;

procedure TfrmCommandAndControl.Edit1Change(Sender: TObject);
var
frequency :string;
first,second,third,fourth,fifth:string;
begin
  frequency:= copy(edit1.text,pos('FE FE',edit1.text)+12,21);
  trim(frequency);

  if (pos('FD',frequency)=19) then
    begin
      first:=copy(frequency,16,2);
      second:=copy(frequency,13,2);
      third:=copy(frequency,10,2);
      fourth:=copy(frequency,7,2);
      fifth:=copy(frequency,4,2);
      label5.caption:= first+second+third+fourth+fifth;
    end;
end;

procedure TfrmCommandAndControl.ModeChange(Sender: TObject);
begin
  if combobox1.itemindex > 1 then  //only for icoms
    BEGIN
      if Mode.Text = 'LSB' then
        begin
          ts := ICOMS.SetMode(trmLSB);
          label1.caption:='LSB';
        end
      else if Mode.Text = 'USB' then
        begin
          ts := ICOMS.SetMode(trmUSB);
          label1.caption:='USB';
        end
     else if Mode.Text = 'AM' then
       begin
         ts := ICOMS.SetMode(trmAM);
         label1.caption:='AM';
       end
    else if Mode.Text = 'AM Narrow' then   //not implemented
      begin
        ts := ICOMS.SetMode(trmAMNarrow);
        label1.caption:='AM Narrow';
      end
    else if Mode.Text = 'AM Wide' then   //not implemented
      begin
        ts := ICOMS.SetMode(trmAMWide);
        label1.caption:='AM Wide';
      end
    else if Mode.Text = 'CW' then
      begin
        ts := ICOMS.SetMode(trmCW);
        label1.caption:='CW';
      end
    else if Mode.Text = 'CW Narrow' then    //not implemented
      begin
        ts := ICOMS.SetMode(trmCWNarrow);
        label1.caption:='CW Narrow';
      end
    else if Mode.Text = 'FM' then
      begin
        ts := ICOMS.SetMode(trmFM);
        label1.caption:='FM';
      end
   else if Mode.Text = 'FM Narrow' then    //not implemented
      begin
        ts := ICOMS.SetMode(trmFMNarrow);
        label1.caption:='LSB';
      end
   else if Mode.Text = 'WFM' then      //not implemented
     begin
       ts := ICOMS.SetMode(trmWFM);
       label1.caption:='WFM';
    end
   else if Mode.Text = 'RTTY' then
     begin
       ts := ICOMS.setMode(trmRTTY);
       label1.caption:='RTTY'
     end;
   HexLog(addr(ts[1]),length(ts),'Set mode') ;
   Comport1.PutString(ts) ;
   Comport1.PutString(ts) ;
 END
 ELSE
   If Combobox1.Itemindex =1 then
     begin
       case mode.itemindex of
         0: begin
              Comport1.PutString('MD1;');//lsb
              Label1.caption := 'LSB';
            end;
         1: begin
              Comport1.PutString('MD2;');//usb
              Label1.caption := 'USB';
            end;
         2: begin
              Comport1.PutString('MD3;');//cw
              Label1.caption := 'CW';
            end;
         3: begin
              Comport1.PutString('MD4;');//fm
              Label1.caption := 'FM';
            end;
         4: begin
              Comport1.PutString('MD5;');//am
              Label1.caption := 'AM';
            end;
         5: begin
              Comport1.PutString('MD6;');//rtty
              Label1.caption := 'RTTY';
            end;
       end;
     END;
end;


procedure TfrmCommandAndControl.DataPacket1Packet(Sender: TObject;
  Data: Pointer; Size: Integer);
var
  tmp: string ;
begin
  tmp := MemCopy(data,Size) ;
  HexLog(data,size,'Receive data') ;
end;


procedure TfrmCommandAndControl.ComboBox2Change(Sender: TObject);
var
  freqfloat:real;
begin
  case Combobox2.ItemIndex of     //this is easier then polling all the band
    0: label5.caption:= '1800000';  // positions on start and saving to memory
    1: label5.caption:= '3850000';  // but not as elegant.
    2: label5.caption:= '7225000';
    3: label5.caption:= '10100000';
    4: label5.caption:= '14225000';
    5: label5.caption:= '18110000';
    6: label5.caption:= '21300000';
    7: label5.caption:= '24930000';
    8: label5.caption:= '28500000';
    9: label5.caption:= '50125000';
    10: label5.caption:= '144200000';
    11: label5.caption:= '432100000';    ///1000000 is 10.0 mhz
  end; //case
  ts:=Icoms.SetFreq(strtoint(label5.caption)) ;
  Comport1.PutString(ts);
  Comport1.PutString(ts);
  label5.caption:=  inttostr(strtoint(label5.caption));
  freqfloat:=(strtoint(label5.caption)*0.000001 );
  label7.caption:= Format('%3.6f', [(freqfloat)]);
end;

procedure TfrmCommandAndControl.RadioGroup1Click(Sender: TObject);
begin
  case Radiogroup1.ItemIndex of
    0: rShift:= 1000;      //amount of small shift for ICOM (Kenwood is adjusted elsewhere
    1: rShift:= 10000;
    2: rShift:= 100000;    //amount of large shift for ICOM
  end;//end case
end;

procedure TfrmCommandAndControl.Button6Click(Sender: TObject);
var
  freqSet:string;
  freqSetr:real;
begin
  freqSetr:=strtofloat(label4.caption);
  //rshift is 1000 so this needs to be 0.001 in small shift
  //case so the value is rShift * 0.000001
  //freqSetr:=freqsetr+ 0.01;   ///add the offset
   freqSetr:=freqsetr+ (rShift * 0.000001);     //for kenwood

  freqSet:=trim(label4.caption);
  freqSet:=trim(floattostr(freqsetr));

   if (freqsetr>=441) and (freqsetr<1300) then
     begin
       delete(freqset,5,1);
       freqset:='0'+freqset;
       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;


   if (freqsetr>=100) and (freqsetr<441) then
     begin
       delete(freqset,4,1);
       freqset:='00'+freqset;
       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;
  if (freqsetr>=10) and (freqsetr<100) then
     begin
       delete(freqset,3,1);
       freqset:='000'+freqset;

       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;

   if (freqsetr>=1) and (freqsetr<10) then
     begin
       delete(freqset,2,1);
       freqset:='0000'+freqset;

       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;

    if (freqsetr>=0) and (freqsetr<1) then
     begin
       delete(freqset,1,1);
       freqset:='00000'+freqset;

       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;

  freqset:=trim(freqset);
   // if RadioGroup8.ItemIndex=0 then freqset:='FA'+freqset else
   // if RadioGroup8.ItemIndex=1 then freqset:='FB'+freqset;

  freqset:='FA'+freqset;  // only VFO A used with Kenwood
  ComPort1.PutString(freqset+';');
 // ('FA00014195000;');

end;

procedure TfrmCommandAndControl.Button7Click(Sender: TObject);
var
  freqSet:string;
  freqSetr:real;
begin                //decrease freq
  freqSetr:=strtofloat(label4.caption);
  //rshift is 1000 so this needs to be 0.001 in small shift
  //case so the value is rShift * 0.000001
  //freqSetr:=freqsetr+ 0.01;   ///add the offset
   freqSetr:= freqsetr - (rShift * 0.000001);

  freqSet:=trim(label4.caption);
  freqSet:=trim(floattostr(freqsetr));

  if (freqsetr>=441) and (freqsetr<1300) then
     begin
       delete(freqset,5,1);
       freqset:='0'+freqset;
       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;



   if (freqsetr>=100) and (freqsetr<441) then
     begin
       delete(freqset,4,1);
       freqset:='00'+freqset;
       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;
  if (freqsetr>=10) and (freqsetr<100) then
     begin
       delete(freqset,3,1);
       freqset:='000'+freqset;

       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;

   if (freqsetr>=1) and (freqsetr<10) then
     begin
       delete(freqset,2,1);
       freqset:='0000'+freqset;

       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;

    if (freqsetr>=0) and (freqsetr<1) then
     begin
       delete(freqset,1,1);
       freqset:='00000'+freqset;

       while length(freqset)<11 do
         begin
           freqset:= freqset+'0';
         end;
     end;

  freqset:=trim(freqset);
 // if RadioGroup8.ItemIndex=0 then freqset:='FA'+freqset else
 // if RadioGroup8.ItemIndex=1 then freqset:='FB'+freqset;

   freqset:='FA'+freqset;  //only VFO A used for Kenwoods in this version

 ComPort1.PutString(freqset+';');
 // ('FA00014195000;');

end;

procedure TfrmCommandAndControl.Button8Click(Sender: TObject);
begin  //kenwood band up
  comport1.PutString('BU;');
end;

procedure TfrmCommandAndControl.Button9Click(Sender: TObject);
begin  //kenwood band down
  comport1.PutString('BD;');

end;

procedure TfrmCommandAndControl.ShowEngineLog1Click(Sender: TObject);
begin
  If ShowengineLog1.Checked=false then
    begin
      Label3.visible:=true;
      lstProgress.visible:=true; //engine log
      ShowengineLog1.checked:=true;
    end
  else
    begin
      Label3.visible:=false;
      lstProgress.visible:=false; //engine log
      ShowengineLog1.checked:=false;
    end;
end;

procedure TfrmCommandAndControl.HideTraining1Click(Sender: TObject);
begin
    If HideTraining1.Checked=false then
      begin
        btnLexicon.visible:=true;
        btntraingeneral.visible:=true;
        btntrainmic.visible:=true;
        HideTraining1.checked:=true;
      end
   else
     begin
       btnLexicon.visible:=false;
       btntraingeneral.visible:=false;
       btntrainmic.visible:=false;
       HideTraining1.checked:=false;
     end;

end;

procedure TfrmCommandAndControl.CheckBox2Click(Sender: TObject);
begin
  if label8.visible= true then
    begin
      Label8.visible:=false;
      Label9.visible:=false;
      Label10.visible:=false;
      Label11.visible:=false;
      If ASpeech=true then
        SpVoice1.Speak('Command and Control ready' ,SVSFLAGsAsync);
    end;
end;

procedure TfrmCommandAndControl.Button2Click(Sender: TObject);
begin   // routine used with MyexitWindows function to close down Windows
  MyExitWindows(EWX_POWEROFF or EWX_FORCE);
end;

procedure TfrmCommandAndControl.ArtificialSpeechAnnouncements1Click(
  Sender: TObject);
begin
  If ArtificialSpeechAnnouncements1.Checked=true then
    begin
      ASpeech:=false;    //global variable speech off
      ArtificialSpeechAnnouncements1.Checked:=false ;
    end
  else
    begin
      ASpeech:=true;
      ArtificialSpeechAnnouncements1.Checked:=true;
    end;
end;


procedure TfrmCommandAndControl.Timer1Timer(Sender: TObject);
var
timeOver:word;
begin   // make automatic frequency announcements
  if ComboBox1.ItemIndex = 1 then   //Kenwood
    SpVoice1.Speak(label4.caption+' mega hertz. ' ,SVSFLAGsAsync)  //speak time now
  else if   ComboBox1.ItemIndex > 1 then   //ICOM
     SpVoice1.Speak(label7.caption+' mega hertz. ' ,SVSFLAGsAsync);
  { disable the timer, as we are done }
  Timer1.Enabled := False;
end;

procedure TfrmCommandAndControl.Edit2Change(Sender: TObject);
begin   //the OnChange event handler is used to trigger a speech announcement
  if ASpeech=true then
    Timer1.enabled:=true; //to start automatic frequency announcement
end;

procedure TfrmCommandAndControl.Help2Click(Sender: TObject);
var //HTML help file
TempString:array[0..79]of char;
begin
  StrPCopy(TempString,'command.chm');
  //requires ShellAPI in uses clause
  ShellExecute(0,Nil,TempString,Nil,Nil,SW_NORMAL);
end;

end.
