www.pudn.com > TAPIOfControl.rar > AdSapiPh.pas


(***** BEGIN LICENSE BLOCK ***** 
 * Version: MPL 1.1 
 * 
 * The contents of this file are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at 
 * http://www.mozilla.org/MPL/ 
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the 
 * License. 
 * 
 * The Original Code is TurboPower Async Professional 
 * 
 * The Initial Developer of the Original Code is 
 * TurboPower Software 
 * 
 * Portions created by the Initial Developer are Copyright (C) 1991-2002 
 * the Initial Developer. All Rights Reserved. 
 * 
 * Contributor(s): 
 * 
 * ***** END LICENSE BLOCK ***** *) 
 
{*********************************************************} 
{*                   ADSAPIPH.PAS 4.06                   *} 
{*********************************************************} 
{* TApdSAPIPhone component                               *} 
{*********************************************************} 
 
{ 
  The TApdSapiPhone component descends directly from the TApdTapiDevice, 
  so it has similar characteristics. The differences are in the phone prompts, 
  and the redirection of the audio streams to/from a TApdSapiEngine. 
} 
 
{Global defines potentially affecting this unit} 
{$I AWDEFINE.INC} 
 
unit AdSapiPh; 
 
interface 
 
uses 
  Windows, 
  Messages, 
  Classes, 
  SysUtils, 
  MMSystem, 
  INIFiles, 
  OoMisc, 
  AdSapiEn, 
  AdTUtil, 
  AdTapi, 
  AdSapiGr, 
  AdISapi, 
  {$IFDEF Delphi6} 
  DateUtils, 
  Variants, 
  {$ENDIF} 
  ActiveX, 
  ComObj, 
  Dialogs; 
 
const 
  ApdSapiAskOperator = -3; 
  ApdSapiAskHangUp = -4; 
  ApdSapiAskBack = -5; 
  ApdSapiAskWhere = -10; 
  ApdSapiAskHelp = -11; 
  ApdSapiAskRepeat = 12; 
  ApdSapiAskSpeakFaster = 13; 
  ApdSapiAskSpeakSlower = -14; 
  ApdSapiAbort = -98; 
  ApdSapiTimeout = -99; 
 
  ApdSapiSpeedChange = 25; 
 
  ApdSapiConnect    = 1; 
  ApdSapiDisConnect = 2; 
 
  { Replies from the Ask... controls } 
 
  { Replies that are normally not handled by the control } 
  ApdTCR_ABORT          = -1; 
  ApdTCR_NORESPONSE     = -2; 
  ApdTCR_ASKOPERATOR    = -3; 
  ApdTCR_ASKHANGUP      = -4; 
  ApdTCR_ASKBACK        = -5; 
  { Replies that are normally handled by the control } 
  ApdTCR_ASKWHERE       = -10; 
  ApdTCR_ASKHELP        = -11; 
  ApdTCR_ASKREPEAT      = -12; 
  ApdTCR_ASKSPEAKFASTER = -13; 
  ApdTCR_ASKSPEAKSLOWER = -14; 
 
type 
 
 
  TApdSapiPhoneSettings = set of (psVerify, psCanGoBack, 
                                  psDisableSpeedChange, psEnableOperator, 
                                  psEnableAskHangup); 
  TApdSapiPhoneReply = (prOk, prAbort, prNoResponse, prOperator, prHangUp, 
                        prBack, prWhere, prHelp, prRepeat, prSpeakFaster, 
                        prSpeakSlower, prCheck, prError, prUnknown); 
  TApdPhraseType = (ptHelp, ptBack, ptOperator, ptHangup, ptRepeat, 
                    ptWhere, ptSpeakFaster, ptSpeakSlower, ptUnknown, 
                    ptNone, ptCustom, ptAbort, ptTimeout); 
 
  TGrammarStringHandler = (gshIgnore, gshInsert, gshAutoReplace); 
 
  TApdSapiPhoneEvent = procedure (Sender : TObject) of object; 
  TApdOnAskForStringFinish = procedure (Sender : TObject; 
                                        Reply : TApdSapiPhoneReply; 
                                        Data : string; 
                                        SpokenData : string) of object; 
  TApdOnAskForDateTimeFinish = procedure (Sender : TObject; 
                                          Reply : TApdSapiPhoneReply; 
                                          Data : TDateTime; 
                                          SpokenData : string) of object; 
  TApdOnAskForIntegerFinish = procedure (Sender : TObject; 
                                         Reply : TApdSapiPhoneReply; 
                                         Data : Integer; 
                                         SpokenData : string) of object; 
  TApdOnAskForBooleanFinish = procedure (Sender : TObject; 
                                         Reply : TApdSapiPhoneReply; 
                                         Data : Boolean; 
                                         SpokenData : string) of object; 
 
  { Internal event to parse custom responses spoken by the user } 
  TApdCustomDataHandler = procedure (LastReply : TApdPhraseType; 
                                     LastRule : Integer; 
                                     LastPhrase : string) of object; 
  { Internal event to trigger the OnAskFor...Finish event } 
  TApdAskForEventTrigger = procedure (Reply : TApdSapiPhoneReply; 
                                      Data : Pointer; 
                                      SpokenData : string) of object; 
 
  {$IFNDEF Delphi6} 
  PDateTime = ^TDateTime; 
  PBoolean = ^Boolean; 
  {$ENDIF} 
 
  ESapiPhoneError = class (EApdSapiEngineException); 
 
  TApdSapiGrammarList = class (TStringList) 
    private 
    protected 
    public 
      procedure ReadSectionValues (Section : string; List : TStringList); 
      function SectionExists (Section : string) : Boolean; 
    published 
  end; 
 
  TApdSapiPhonePrompts = class (TPersistent) 
    private 
      FAskAreaCode    : string; 
      FAskLastFour    : string; 
      FAskNextThree   : string; 
      FCannotGoBack   : string; 
      FCannotHangUp   : string; 
      FGoingBack      : string; 
      FHangingUp      : string; 
      FHelp           : string; 
      FHelp2          : string; 
      FHelpVerify     : string; 
      FMain           : string; 
      FMain2          : string; 
      FMaxSpeed       : string; 
      FMinSpeed       : string; 
      FOperator       : string; 
      FNoOperator     : string; 
      FNoSpeedChange  : string; 
      FSpeakingFaster : string; 
      FSpeakingSlower : string; 
      FTooFewDigits   : string; 
      FTooManyDigits  : string; 
      FUnrecognized   : string; 
      FVerifyPost     : string; 
      FVerifyPre      : string; 
      FWhere          : string; 
      FWhere2         : string; 
 
    protected 
      procedure SetAskAreaCode (v : string); 
      procedure SetAskLastFour (v : string); 
      procedure SetAskNextThree (v : string); 
      procedure SetCannotGoBack (v : string); 
      procedure SetCannotHangUp (v : string); 
      procedure SetGoingBack (v : string); 
      procedure SetHangingUp (v : string); 
      procedure SetHelp (v : string); 
      procedure SetHelp2 (v : string); 
      procedure SetHelpVerify (v : string); 
      procedure SetMain (v : string); 
      procedure SetMain2 (v : string); 
      procedure SetMaxSpeed (v : string); 
      procedure SetMinSpeed (v : string); 
      procedure SetOperator (v : string); 
      procedure SetNoOperator (v : string); 
      procedure SetNoSpeedChange (v : string); 
      procedure SetSpeakingFaster (v : string); 
      procedure SetSpeakingSlower (v : string); 
      procedure SetTooFewDigits (v : string); 
      procedure SetTooManyDigits (v : string); 
      procedure SetUnrecognized (v : string); 
      procedure SetVerifyPost (v : string); 
      procedure SetVerifyPre (v : string); 
      procedure SetWhere (v : string); 
      procedure SetWhere2 (v : string); 
 
    public 
      constructor Create;  
 
      function GenerateGrammar (NewPrompt1 : string; NewPrompt2 : string; 
                                NewHelp1 : string; NewHelp2 : string; 
                                NewWhere1 : string; 
                                NewWhere2 : string) : string; 
      function GenerateExtensionGrammar (NewTooFewDigits : string; 
                                         NewTooManyDigits : string) : string; 
      function GeneratePhoneNumberGrammar (NewAskAreaCode : string; 
                                           NewAskNextThree : string; 
                                           NewAskLastFour : string) : string; 
 
    published 
      property AskAreaCode : string read FAskAreaCode write SetAskAreaCode; 
      property AskLastFour : string read FAskLastFour write SetAskLastFour; 
      property AskNextThree : string read FAskNextThree write SetAskNextThree; 
      property CannotGoBack : string read FCannotGoBack write SetCannotGoBack; 
      property CannotHangUp : string read FCannotHangUp write SetCannotHangUp; 
      property HangingUp : string read FHangingUp write SetHangingUp; 
      property Help : string read FHelp write SetHelp; 
      property Help2 : string read FHelp2 write SetHelp2; 
      property HelpVerify : string read FHelPVerify write SetHelpVerify; 
      property GoingBack : string read FGoingBack write SetGoingBack; 
      property Main : string read FMain write SetMain; 
      property Main2 : string read FMain2 write SetMain2; 
      property MaxSpeed : string read FMaxSpeed write SetMaxSpeed; 
      property MinSpeed : string read FMinSpeed write SetMinSpeed; 
      property Operator : string read FOperator write SetOperator; 
      property NoOperator : string read FNoOperator write SetNoOperator; 
      property NoSpeedChange : string 
               read FNoSpeedChange write SetNoSpeedChange; 
      property SpeakingFaster : string 
               read FSpeakingFaster write SetSpeakingFaster; 
      property SpeakingSlower : string 
               read FSpeakingSlower write SetSpeakingSlower; 
      property TooFewDigits : string read FTooFewDigits write SetTooFewDigits; 
      property TooManyDigits : string 
               read FTooManyDigits write SetTooManyDigits; 
      property Unrecognized : string read FUnrecognized write SetUnRecognized; 
      property VerifyPost : string read FVerifyPost write SetVerifyPost; 
      property VerifyPre : string read FVerifyPre write SetVerifyPre; 
      property Where : string read FWhere write SetWhere; 
      property Where2 : string read FWhere2 write SetWhere2; 
  end; 
 
  { TApdSapiAskForInfo handles the actual work of asking the user for a 
    response.  This handles getting help, repeating prompts and related 
    items } 
 
  TApdSapiAskForInfo = class (TObject) 
    private 
      FReplyHandle   : THandle; 
      FPrompts       : TApdSapiPhonePrompts; 
      FSapiEngine    : TApdCustomSapiEngine; 
      FAskForGrammar : TStringList; 
      FMainGrammar   : TStringList; 
      FOptions       : TApdSapiPhoneSettings; 
      FStringHandler : TGrammarStringHandler; 
 
    protected 
      function DeterminePhraseTypeEx (Phrase : string; 
                                      var Rule : string) : TApdPhraseType; 
      function IsAnglePhrase (Phrase : string) : Boolean; 
      function IsParenPhrase (Phrase : string) : Boolean; 
      function IsQuoted (Phrase : string) : Boolean; 
      function KillQuotes (Phrase : string) : string; 
      function GetValue (Value : string) : string; 
      function GetKey (Value : string) : string; 
      function AnalyzeRule (Tokens : TStringList; CurrentRule : string; 
                            INIFile : TApdSapiGrammarList; 
                            CurrentSection : TStringList; 
                            var CurrentWord : Integer; 
                            var MatchingKey : string 
                            ) : Boolean; 
      function RecurseRules (Tokens : TStringList; 
                             INIFile : TApdSapiGrammarList; 
                             CurrentSection : string; 
                             var CurrentWord : Integer; 
                             var MatchingKey : string 
                             ) : Boolean; 
      function LocateRule (Tokens : TStringList) : string; 
      procedure InitializeMainGrammar; 
      procedure SapiPhraseFinishHook (Sender : TObject; Phrase : string; 
                                      Results : Integer); virtual; 
      procedure SetAskForGrammar (v : TStringList); 
      procedure SetMainGrammar (v : TStringList); 
      procedure SetOptions (v : TApdSapiPhoneSettings); 
      procedure SetReplyHandle (v : THandle); 
 
    public 
      constructor Create; 
      destructor Destroy; override; 
 
      procedure AskFor; 
      function DeterminePhraseType (Phrase : string) : TApdPhraseType; 
      function FindGrammarRule (var Phrase : string) : string; 
 
    published 
      property AskForGrammar : TStringList 
               read FAskForGrammar write SetAskForGrammar; 
      property MainGrammar : TStringList 
               read FMainGrammar write SetMainGrammar; 
      property Options : TApdSapiPhoneSettings read FOptions write SetOptions; 
      property Prompts : TApdSapiPhonePrompts read FPrompts write FPrompts; 
      property ReplyHandle : THandle read FReplyHandle write SetReplyHandle; 
      property SapiEngine : TApdCustomSapiEngine 
               read FSapiEngine write FSapiEngine; 
  end; 
 
  TApdCustomSapiPhone = class (TApdCustomTapiDevice) 
    private 
      FSapiEngine                : TApdCustomSapiEngine; 
      IAMM                       : IAudioMultiMediaDevice; 
      IAT                        : IAudioTel; 
 
      FNumDigits                 : Integer; 
      FNoAnswerMax               : Integer; 
      FNoAnswerTime              : Integer; 
      FOptions                   : TApdSapiPhoneSettings; 
      FInAskFor                  : Boolean; 
      FSpellingEchoBack          : Boolean; 
 
      FPrompts                   : TApdSapiPhonePrompts; 
      FInfo                      : TApdSapiAskForInfo; 
 
      FExtension                 : string; 
      FDigitCount                : Integer; 
      FSpelledWord               : string; 
      FList                      : TStringList; 
 
      FCustomDataHandler         : TApdCustomDataHandler; 
      FEventTrigger              : TApdAskForEventTrigger; 
 
      FOnAskForDateFinish        : TApdOnAskForDateTimeFinish; 
      FOnAskForExtensionFinish   : TApdOnAskForStringFinish; 
      FOnAskForListFinish        : TApdOnAskForIntegerFinish; 
      FOnAskForPhoneNumberFinish : TApdOnAskForStringFinish; 
      FOnAskForSpellingFinish    : TApdOnAskForStringFinish; 
      FOnAskForTimeFinish        : TApdOnAskForDateTimeFinish; 
      FOnAskForYesNoFinish       : TApdOnAskForBooleanFinish; 
      FOnTapiDisconnect          : TNotifyEvent; 
 
    protected 
      procedure AskForDateDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
      procedure AskForExtensionDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
      procedure AskForListDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
      procedure AskForPhoneNumberDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
      procedure AskForSpellingDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
      procedure AskForTimeDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
      procedure AskForYesNoDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
 
      procedure AskForDateTrigger (Reply : TApdSapiPhoneReply; 
                                   Data : Pointer; 
                                   SpokenData : string); 
      procedure AskForExtensionTrigger (Reply : TApdSapiPhoneReply; 
                                        Data : Pointer; 
                                        SpokenData : string); 
      procedure AskForListTrigger (Reply : TApdSapiPhoneReply; 
                                   Data : Pointer; 
                                   SpokenData : string); 
      procedure AskForPhoneNumberTrigger (Reply : TApdSapiPhoneReply; 
                                          Data : Pointer; 
                                          SpokenData : string); 
      procedure AskForSpellingTrigger (Reply : TApdSapiPhoneReply; 
                                       Data : Pointer; 
                                       SpokenData : string); 
      procedure AskForTimeTrigger (Reply : TApdSapiPhoneReply; 
                                   Data : Pointer; 
                                   SpokenData : string); 
      procedure AskForYesNoTrigger (Reply : TApdSapiPhoneReply; 
                                    Data : Pointer; 
                                    SpokenData : string); 
 
      function ConvertResponse (RCode : Integer ) : TApdSapiPhoneReply; 
      procedure DoLineCallState (Device, P1, P2, P3 : LongInt); override; 
      procedure ExitAskFor (Reply : TApdSapiPhoneReply; 
                            Data : Pointer; 
                            SpokenData : string); 
      function GetPhraseData (LParam : Longint) : string; 
      function FixNumerics (Phrase : string) : string; 
      function InterpretDate (Phrase : string; 
                              var Trusted : Boolean) : TDateTime; virtual; 
      function InterpretPhoneNumber (Phrase : string) : string; 
      function InterpretTime (Phrase : string) : TDateTime; virtual; 
      procedure Notification(AComponent : TComponent; Operation: TOperation); 
                override; 
      procedure PhraseHandler (var Msg : TMessage); message apw_SapiInfoPhrase; 
      procedure SapiPhoneCallback (var Msg : TMessage); 
                message apw_SapiPhoneCallBack; 
      procedure SetNoAnswerMax (v : Integer); 
      procedure SetNoAnswerTime (v : Integer); 
      procedure SetNumDigits (v : Integer); 
      procedure SetOptions (v : TApdSapiPhoneSettings); 
      procedure SetSpellingEchoBack (v : Boolean); 
      procedure UpdateStateMachine (LastReply : TApdPhraseType; 
                                    LastRule : Integer; 
                                    LastPhrase : string); 
 
    public 
      constructor Create (AOwner : TComponent); override; 
      destructor Destroy; override; 
 
      procedure AbortAskFor; 
      procedure AskForDate (NewPrompt1 : string); 
      procedure AskForDateEx (NewPrompt1 : string; NewPrompt2 : string; 
                              NewHelp1 : string; NewHelp2 : string; 
                              NewWhere1 : string; 
                              NewWhere2 : string); 
      procedure AskForExtension (NewPrompt1 : string); 
      procedure AskForExtensionEx (NewPrompt1 : string; NewPrompt2 : string; 
                                   NewTooManyDigits : string; 
                                   NewTooFewDigits : string; 
                                   NewNumDigits : Integer; 
                                   NewHelp1 : string; 
                                   NewHelp2 : string; 
                                   NewWhere1 : string; 
                                   NewWhere2 : string); 
      procedure AskForList (List : TStringList; 
                           NewPrompt1 : string); 
      procedure AskForListEx (List : TStringList; 
                              NewPrompt1 : string; NewPrompt2 : string; 
                              NewHelp1 : string; NewHelp2 : string; 
                              NewWhere1 : string; 
                              NewWhere2 : string); 
      procedure AskForPhoneNumber (NewPrompt1 : string); 
      procedure AskForPhoneNumberEx (NewPrompt1 : string; 
                                     NewPrompt2 : string; 
                                     NewAskAreaCode : string; 
                                     NewAskNextThree : string; 
                                     NewAskLastFour : string; 
                                     NewHelp1 : string; 
                                     NewHelp2 : string; 
                                     NewWhere1 : string; 
                                     NewWhere2 : string); 
      procedure AskForSpelling (NewPrompt1 : string); 
      procedure AskForSpellingEx (NewPrompt1 : string; 
                                  NewPrompt2 : string; 
                                  NewHelp1 : string; 
                                  NewHelp2 : string; 
                                  NewWhere1 : string; 
                                  NewWhere2 : string); 
      procedure AskForTime (NewPrompt1 : string); 
      procedure AskForTimeEx (NewPrompt1 : string; NewPrompt2 : string; 
                              NewHelp1 : string; NewHelp2 : string; 
                              NewWhere1 : string; 
                              NewWhere2 : string); 
      procedure AskForYesNo (NewPrompt1 : string); 
      procedure AskForYesNoEx (NewPrompt1 : string; 
                               NewPrompt2 : string; 
                               NewHelp1 : string; NewHelp2 : string; 
                               NewWhere1 : string; 
                               NewWhere2 : string); 
      procedure SetDefaultPrompts (NewPrompt1 : string; NewPrompt2 : string; 
                                   NewHelp1 : string; NewHelp2 : string; 
                                   NewWhere1 : string; NewWhere2 : string); 
      procedure Speak (const Text : string); 
 
      property Prompts : TApdSapiPhonePrompts read FPrompts write FPrompts; 
 
    published 
      property NoAnswerMax : Integer read FNoAnswerMax write SetNoAnswerMax; 
      property NoAnswerTime : Integer read FNoAnswerTime write SetNoAnswerTime; 
      property NumDigits : Integer read FNumDigits write SetNumDigits; 
      property Options : TApdSapiPhoneSettings read FOptions write SetOptions; 
      property SapiEngine : TApdCustomSapiEngine 
               read FSapiEngine write FSapiEngine; 
      property SpellingEchoBack : Boolean 
               read FSpellingEchoBack write SetSpellingEchoBack default True; 
 
      property OnAskForDateFinish : TApdOnAskForDateTimeFinish 
               read FOnAskForDateFinish write FOnAskForDateFinish; 
      property OnAskForExtensionFinish : TApdOnAskForStringFinish 
               read FOnAskForExtensionFinish write FOnAskForExtensionFinish; 
      property OnAskForListFinish : TApdOnAskForIntegerFinish 
               read FOnAskForListFinish write FOnAskForListFinish; 
      property OnAskForPhoneNumberFinish : TApdOnAskForStringFinish 
               read FOnAskForPhoneNumberFinish 
               write FOnAskForPhoneNumberFinish; 
      property OnAskForSpellingFinish : TApdOnAskForStringFinish 
               read FOnAskForSpellingFinish write FOnAskFOrSpellingFinish; 
      property OnAskForTimeFinish : TApdOnAskForDateTimeFinish 
               read FOnAskForTimeFinish write FOnAskForTimeFinish; 
      property OnAskForYesNoFinish : TApdOnAskForBooleanFinish 
               read FOnAskForYesNoFinish write FOnAskForYesNoFinish; 
      property OnTapiDisconnect : TNotifyEvent 
               read FOnTapiDisconnect write FOnTapiDisconnect; 
      {properties} 
      property SelectedDevice; 
      property ComPort; 
      property StatusDisplay; 
      property TapiLog; 
      property AnswerOnRing; 
      property RetryWait; 
      property MaxAttempts; 
      property ShowTapiDevices; 
      property ShowPorts; 
      property EnableVoice; 
      property FilterUnsupportedDevices; 
 
      {events} 
      property OnTapiStatus; 
      property OnTapiLog; 
      property OnTapiPortOpen; 
      property OnTapiPortClose; 
      property OnTapiConnect; 
      property OnTapiFail; 
      property OnTapiDTMF; 
      property OnTapiCallerID; 
      property OnTapiWaveNotify; 
      property OnTapiWaveSilence; 
 
  end; 
 
  TApdSapiPhone = class (TApdCustomSapiPhone) 
    private 
    protected 
    public 
    published 
      property NoAnswerMax; 
      property NoAnswerTime; 
      property NumDigits; 
      property Options; 
      property SapiEngine; 
 
      {properties} 
      property SelectedDevice; 
      property ComPort; 
      property StatusDisplay; 
      property TapiLog; 
      property AnswerOnRing; 
      property RetryWait; 
      property MaxAttempts; 
      property ShowTapiDevices; 
      property ShowPorts; 
      property EnableVoice; 
      property FilterUnsupportedDevices;                                
 
      {events} 
      property OnTapiStatus; 
      property OnTapiLog; 
      property OnTapiPortOpen; 
      property OnTapiPortClose; 
      property OnTapiConnect; 
      property OnTapiFail; 
      property OnTapiDTMF; 
      property OnTapiCallerID; 
      property OnTapiWaveNotify; 
      property OnTapiWaveSilence; 
  end; 
 
implementation 
 
procedure TokenizePhrase (Phrase : string; 
                                             Tokens : TStringList); 
{ Tokenizes a series of words into a TStringList } 
 
  procedure AddWord (Tokens : TStringList; var NewWord : ShortString); 
  begin 
    if NewWord <> '' then 
      Tokens.Add (NewWord); 
    NewWord := ''; 
  end; 
   
type 
  TParseState = (psColChars, psColSpace, psInQuote); 
   
var 
  State      : TParseState; 
  i          : Integer; 
  PhraseLen  : Integer; 
  WorkString : ShortString; 
 
begin 
  Tokens.Clear; 
  i := 1; 
  WorkString := ''; 
  State := psColChars; 
  PhraseLen := Length (Phrase); 
  while i <= PhraseLen do begin 
    case State of 
      psColChars : 
        if Phrase[i] = ' ' then begin 
          State := psColSpace; 
          AddWord (Tokens, WorkString); 
        end else if Phrase[i] = '"' then begin 
          State := psInQuote; 
          AddWord (Tokens, WorkString); 
          WorkString := WorkString + Phrase[i]; 
        end else 
          WorkString := WorkString + Phrase[i]; 
      psColSpace : 
        if Phrase[i] = '"' then begin 
          State := psInQuote; 
          WorkString := WorkString + Phrase[i]; 
        end else if Phrase[i] <> ' ' then begin 
          WorkString := WorkString + Phrase[i]; 
          State := psColChars; 
        end; 
      psInQuote : 
        if Phrase[i] = '"' then begin 
          WorkString := WorkString + Phrase[i]; 
          AddWord (Tokens, WorkString); 
          State := psColChars; 
        end else 
          WorkString := WorkString + Phrase[i]; 
    end; 
    Inc (i); 
  end; 
  if PhraseLen > 1 then begin 
    if (WorkString[1] = '"') and (WorkString[PhraseLen] <> '"') then 
      WorkString := WorkString + '"'; 
  end; 
  AddWord (Tokens, WorkString); 
end; 
 
{$IFNDEF Delphi6} 
 
{ Add some general convenience routines } 
 
function DayOfTheWeek (TheDate : TDateTime) : Integer; 
begin 
  Result := (DateTimeToTimeStamp (TheDate).Date - 1) mod 7 + 1; 
end; 
 
function MonthOfTheYear (TheDate : TDateTime) : Word; 
var 
  Year, Day: Word; 
begin 
  DecodeDate (TheDate, Year, Result, Day); 
end; 
 
procedure IncAMonth (var Year, Month, Day : Word; NumMonths : Integer); 
type 
  PMonthDayTable = ^TMonthDayTable; 
  TMonthDayTable = array[1..12] of Word; 
 
const 
  MonthDays: array [Boolean] of TMonthDayTable = 
    ((31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), 
     (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)); 
var 
  DayTable: PDayTable; 
  Sign: Integer; 
   
begin 
  if NumMonths >= 0 then 
    Sign := 1 
  else 
    Sign := -1; 
  Year := Year + (NumMonths div 12); 
  NumMonths := NumMonths mod 12; 
  Inc (Month, NumMonths); 
  if Word (Month-1) > 11 then 
  begin 
    Inc (Year, Sign); 
    Inc (Month, -12 * Sign); 
  end; 
  DayTable := @MonthDays[IsLeapYear (Year)]; 
  if Day > DayTable^[Month] then 
    Day := DayTable^[Month]; 
end; 
 
function IncMonth(const TheDate : TDateTime; NumberOfMonths : Integer) : TDateTime; 
var 
  Year, Month, Day : Word; 
begin 
  DecodeDate (TheDate, Year, Month, Day); 
  IncAMonth (Year, Month, Day, NumberOfMonths); 
  Result := EncodeDate (Year, Month, Day); 
end; 
 
function IncYear (TheDate : TDateTime; NumYears : Integer) : TDateTime; 
begin 
  Result := IncMonth (TheDate, NumYears * 12); 
end; 
{$ENDIF} 
 
{ TApdSapiGrammarList } 
procedure TApdSapiGrammarList.ReadSectionValues (Section : string; 
                                                 List : TStringList); 
var 
  i : Integer; 
  FirstChar : Char; 
begin 
  i := IndexOf ('[' + Section + ']'); 
  List.Clear; 
  if i < 0 then 
    Exit; 
  Inc (i); 
  while (i < Count) do begin 
    if Length (Strings[i]) > 0 then begin 
     FirstChar := Trim (Strings[i])[1]; 
     if (FirstChar <> ';') and (FirstChar <> '[') then 
       List.Add (Strings[i]) 
     else if FirstChar = '[' then 
       Exit; 
    end; 
    Inc (i); 
  end; 
end;                                                  
 
function TApdSapiGrammarList.SectionExists (Section : string) : Boolean; 
begin 
  if IndexOf ('[' + Section + ']') >= 0 then 
    Result := True 
  else 
    Result := False; 
end; 
 
{ TApdSapiPhonePrompts } 
 
constructor TApdSapiPhonePrompts.Create; 
begin 
  inherited Create; 
 
  AskAreaCode    := ApdAskAreaCode; 
  AskLastFour    := ApdAskLastFour; 
  AskNextThree   := ApdAskNextThree; 
  CannotGoBack   := ApdCannotGoBack; 
  CannotHangUp   := ApdCannotHangUp; 
  HangingUp      := ApdHangingUp; 
  Help           := ApdHelp; 
  Help2          := ApdHelp2; 
  HelpVerify     := ApdHelpVerify; 
  GoingBack      := ApdGoingBack; 
  Main           := ApdMain; 
  Main2          := ApdMain2; 
  FMaxSpeed      := ApdMaxSpeed; 
  FMinSpeed      := ApdMinSpeed; 
  Operator       := ApdOperator; 
  NoOperator     := ApdNoOperator; 
  NoSpeedChange  := ApdNoSpeedChange; 
  SpeakingFaster := ApdSpeakingFaster; 
  SpeakingSlower := ApdSpeakingSlower; 
  TooFewDigits   := ApdTooFewDigits; 
  TooManyDigits  := ApdTooManyDigits; 
  Unrecognized   := ApdUnrecognized; 
  VerifyPost     := ApdVerifyPost; 
  VerifyPre      := ApdVerifyPre; 
  Where          := ApdWhere; 
  Where2         := ApdWhere2; 
end; 
 
function TApdSapiPhonePrompts.GenerateExtensionGrammar ( 
                                         NewTooFewDigits : string; 
                                         NewTooManyDigits : string) : string; 
begin 
  Result := ''; 
  if NewTooFewDigits <> '' then 
    Result := Result + 'TooFewDigits=' + NewTooFewDigits + ^M^J 
  else if TooFewDigits <> '' then 
    Result := Result + 'TooFewDigits=' + TooFewDigits + ^M^J; 
 
  if NewTooManyDigits <> '' then 
    Result := Result + 'TooManyDigits=' + NewTooManyDigits + ^M^J 
  else if TooManyDigits <> '' then 
    Result := Result + 'TooManyDigits =' + TooManyDigits + ^M^J; 
end; 
 
function TApdSapiPhonePrompts.GenerateGrammar (NewPrompt1 : string; 
                                               NewPrompt2 : string; 
                                               NewHelp1 : string; 
                                               NewHelp2 : string; 
                                               NewWhere1 : string; 
                                               NewWhere2 : string) : string; 
begin 
  Result := '[Prompts]'^M^J; 
  if NewPrompt1 <> '' then 
    Result := Result + 'Main=' + NewPrompt1 + ^M^J 
  else if Main <> '' then 
    Result := Result + 'Main=' + Main + ^M^J; 
 
  if NewPrompt2 <> '' then 
    Result := Result + 'Main.2=' + NewPrompt2 + ^M^J 
  else if Main2 <> '' then 
    Result := Result + 'Main=' + Main2 + ^M^J; 
 
  if NewWhere1 <> '' then 
    Result := Result + 'Where=' + NewWhere1 + ^M^J 
  else if Where <> '' then 
    Result := Result + 'Where=' + Where + ^M^J; 
 
  if NewWhere2 <> '' then 
    Result := Result + 'Where.2=' + NewWhere2 + ^M^J 
  else if Where2 <> '' then 
    Result := Result + 'Where.2=' + Where2 + ^M^J; 
 
  if NewHelp1 <> '' then 
    Result := Result + 'Help=' + NewHelp1 + ^M^J 
  else if Help <> '' then 
    Result := Result + 'Help=' + Help + ^M^J; 
 
  if NewHelp2 <> '' then 
    Result := Result + 'Help.2=' + NewHelp2+ ^M^J 
  else if Help2 <> '' then 
    Result := Result + 'Help.2=' + Help2 + ^M^J; 
end; 
 
function TApdSapiPhonePrompts.GeneratePhoneNumberGrammar ( 
                                           NewAskAreaCode : string; 
                                           NewAskNextThree : string; 
                                           NewAskLastFour : string) : string; 
begin 
  Result := ''; 
  if NewAskAreaCode <> '' then 
    Result := Result + 'AskAreaCode =' + NewAskAreaCode + ^M^J 
  else if AskAreaCode <> '' then 
    Result := Result + 'AskAreaCode =' + AskAreaCode + ^M^J; 
 
  if NewAskNextThree <> '' then 
    Result := Result + 'AskNextThree =' + NewAskNextThree + ^M^J 
  else if AskNextThree <> '' then 
    Result := Result + 'AskNextThree =' + AskNextThree + ^M^J; 
 
  if NewAskLastFour <> '' then 
    Result := Result + 'AskLastFour =' + NewAskLastFour + ^M^J 
  else if AskLastFour <> '' then 
    Result := Result + 'AskLastFour =' + AskLastFour + ^M^J; 
end; 
 
procedure TApdSapiPhonePrompts.SetAskAreaCode (v : string); 
begin 
  if v <> FAskAreaCode then 
    FAskAreaCode := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetAskLastFour (v : string); 
begin 
  if v <> FAskLastFour then 
    FAskLastFour := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetAskNextThree (v : string); 
begin 
  if v <> FAskNextThree then 
    FAskNextThree := v; 
 
end; 
 
procedure TApdSapiPhonePrompts.SetCannotGoBack (v : string); 
begin 
  if v <> FCannotGoBack then 
    FCannotGoBack := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetCannotHangUp (v : string); 
begin 
  if v <> FCannotHangUp then 
    FCannotHangUp := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetGoingBack (v : string); 
begin 
  if v <> FGoingBack then 
    FGoingBack := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetHangingUp (v : string); 
begin 
  if v <> FHangingUp then 
    FHangingUp := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetHelp (v : string); 
begin 
  if v <> FHelp then 
    FHelp := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetHelp2 (v : string); 
begin 
  if v <> FHelp2 then 
    FHelp2 := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetHelpVerify (v : string); 
begin 
  if v <> FHelpVerify then 
    FHelpVerify := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetMain (v : string); 
begin 
  if v <> FMain then 
    FMain := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetMain2 (v : string); 
begin 
  if v <> FMain2 then 
    FMain2 := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetMaxSpeed (v : string); 
begin 
  if v <> FMaxSpeed then 
    FMaxSpeed := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetMinSpeed (v : string); 
begin 
  if v <> FMinSpeed then 
    FMinSpeed := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetOperator (v : string); 
begin 
  if v <> FOperator then 
    FOperator := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetNoOperator (v : string); 
begin 
  if v <> FNoOperator then 
    FNoOperator := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetNoSpeedChange (v : string); 
begin 
  if v <> FNoSpeedChange then 
    FNoSpeedChange := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetSpeakingFaster (v : string); 
begin 
  if v <> FSpeakingFaster then 
    FSpeakingFaster := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetSpeakingSlower (v : string); 
begin 
  if v <> FSpeakingSlower then 
    FSpeakingSlower := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetTooFewDigits (v : string); 
begin 
  if v <> FTooFewDigits then 
    FTooFewDigits := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetTooManyDigits (v : string); 
begin 
  if v <> FTooManyDigits then 
    FTooManyDigits := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetUnrecognized (v : string); 
begin 
  if v <> FUnrecognized then 
    FUnrecognized := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetVerifyPost (v : string); 
begin 
  if v <> FVerifyPost then 
    FVerifyPost := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetVerifyPre (v : string); 
begin 
  if v <> FVerifyPre then 
    FVerifyPre := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetWhere (v : string); 
begin 
  if v <> FWhere then 
    FWhere := v; 
end; 
 
procedure TApdSapiPhonePrompts.SetWhere2 (v : string); 
begin 
  if v <> FWhere2 then 
    FWhere2 := v; 
end; 
 
{ TApdSapiAskForInfo } 
 
constructor TApdSapiAskForInfo.Create; 
begin 
  inherited Create; 
 
  FAskForGrammar := TStringList.Create; 
  FMainGrammar := TStringList.Create; 
  FPrompts := TApdSapiPhonePrompts.Create; 
  FStringHandler := gshIgnore;  
 
  InitializeMainGrammar; 
end; 
 
destructor TApdSapiAskForInfo.Destroy; 
begin 
  FAskForGrammar.Free; 
  FMainGrammar.Free; 
  FPrompts.Free; 
     
  inherited Destroy; 
end; 
 
procedure TApdSapiAskForInfo.AskFor; 
begin 
  if not Assigned (FSapiEngine) then 
    raise ESapiPhoneError.Create (-1, ecApdNoSapiEngine); 
 
  if not Assigned (FPrompts) then 
    raise ESapiPhoneError.Create (-1, ecApdNoPrompts); 
 
  FSapiEngine.RegisterPhraseFinishHook (SapiPhraseFinishHook); 
 
  FSapiEngine.Speak (Prompts.Main); 
  FSapiEngine.DirectSR.GrammarFromString (FMainGrammar.Text + ^M^J + 
                                          FAskForGrammar.Text); 
  FSapiEngine.Listening := True; 
end; 
 
function TApdSapiAskForInfo.DeterminePhraseType (Phrase : string) : TApdPhraseType; 
var 
  PhraseType : string; 
 
begin 
  Result := DeterminePhraseTypeEx (Phrase, PhraseType); 
end; 
 
function TApdSapiAskForInfo.DeterminePhraseTypeEx (Phrase : string; 
                                                   var Rule : string) : TApdPhraseType; 
begin 
  Result := ptUnknown; 
  Rule := FindGrammarRule (Phrase); 
  if Rule = '' then begin 
    Result := ptNone; 
    Exit; 
  end; 
  try 
    case StrToInt (Rule) of 
      -3  : Result := ptOperator; 
      -4  : Result := ptHangup; 
      -5  : Result := ptBack; 
      -10 : Result := ptWhere; 
      -11 : Result := ptHelp; 
      -12 : Result := ptRepeat; 
      -13 : Result := ptSpeakFaster; 
      -14 : Result := ptSpeakSlower; 
    end; 
 
  except 
    on EConvertError do 
      begin 
      end; 
  end; 
end; 
 
function TApdSapiAskForInfo.GetKey (Value : string) : string; 
{ Returns the Key portion of = } 
var 
  Sep : Integer; 
 
begin 
  Result := ''; 
  Sep := Pos ('=', Value); 
  if Sep > 1 then 
    Result := Copy (Value, 1, Sep - 1); 
end; 
 
function TApdSapiAskForInfo.GetValue (Value : string) : string; 
{ Returns the Value portion of = } 
var 
  Sep : Integer; 
 
begin 
  Result := ''; 
  Sep := Pos ('=', Value); 
  if Sep > 0 then 
    Result := Copy (Value, Sep + 1, Length (Value) - Sep) 
end; 
 
function TApdSapiAskForInfo.IsAnglePhrase (Phrase : string) : Boolean; 
begin 
  Result := False; 
  if Phrase = '' then 
    Exit; 
  if (Phrase[1] = '<') and (Phrase[Length (Phrase)] = '>') then 
    Result := True; 
end; 
 
function TApdSapiAskForInfo.IsParenPhrase (Phrase : string) : Boolean; 
begin 
  Result := False; 
  if Phrase = '' then 
    Exit; 
  if (Phrase[1] = '(') and (Phrase[Length (Phrase)] = ')') then 
    Result := True; 
end; 
 
function TApdSapiAskForInfo.IsQuoted (Phrase : string) : Boolean; 
begin 
  Result := False; 
  if Phrase = '' then 
    Exit; 
  if (Phrase[1] = '"') and (Phrase[Length (Phrase)] = '"') then 
    Result := True; 
end; 
 
function TApdSapiAskForInfo.KillQuotes (Phrase : string) : string; 
begin 
  Result := Phrase; 
  if IsQuoted (Phrase) then 
    Result := Copy (Phrase, 2, Length (Phrase) - 2); 
end; 
 
function TApdSapiAskForInfo.AnalyzeRule (Tokens : TStringList; 
                                         CurrentRule : string; 
                                         INIFile : TApdSapiGrammarList; 
                                         CurrentSection : TStringList; 
                                         var CurrentWord : Integer; 
                                         var MatchingKey : string) : Boolean; 
var 
  List : TStringList; 
  i : Integer; 
  j : Integer; 
  Optional : Boolean; 
  RCode : Boolean; 
  OldCurrentWord : Integer; 
 
begin 
  OldCurrentWord := CurrentWord; 
  Result := True; 
  Optional := False; 
  { Tokenize the rule that is being analyzed } 
  List := TStringList.Create; 
  try 
    TokenizePhrase (CurrentRule, List); 
    i := 0; 
    while i < List.Count do begin 
      { If the token is of the form  or (stuff), first check to see 
        if there is a section of that name.  If there is then use RecurseRules 
        to analyze it.  If there isn't a section with that name, then see 
        if there is one or more rules with that name in the current section. 
        If so, then analyze those rules. } 
      if (IsAnglePhrase (List[i])) or (IsParenPhrase (List[i])) then begin 
        if INIFile.SectionExists (List[i]) then begin 
          RCode := RecurseRules (Tokens, INIFile, List[i], CurrentWord, 
                                 MatchingKey); 
          if (not RCode) and (not Optional) then begin 
            Result := False; 
            CurrentWord := OldCurrentWord; 
            Exit; 
          end; 
        end else begin 
          RCode := False; 
          for j := 0 to CurrentSection.Count - 1 do 
            if GetKey (CurrentSection[j]) = List[i] then begin 
              if AnalyzeRule (Tokens, GetValue (CurrentSection[j]), 
                              INIFile, CurrentSection, CurrentWord, 
                              MatchingKey) then 
                RCode := True; 
            end; 
          if (not RCode) and (not Optional) then begin 
            Result := False; 
            CurrentWord := OldCurrentWord; 
            Exit; 
          end; 
        end; 
        Optional := False; 
      end else if List[i] = '[opt]' then begin 
        Optional := True; 
      end else if List[i] = '[1+]' then begin 
      end else if List[i] = '[0+]' then begin 
        Optional := True; 
      end else if IsQuoted (List[i]) then begin 
        case FStringHandler of 
          gshIgnore : 
            begin 
              { do nothing } 
            end; 
          gshInsert : 
            begin 
              Tokens.Insert(CurrentWord, KillQuotes (List[i])); 
              Inc (CurrentWord); 
            end; 
          gshAutoReplace : 
            begin 
              if (i = 0) then begin 
                Tokens.Insert(0, KillQuotes (List[i])); 
                Inc (CurrentWord); 
              end else begin 
                if (IsAnglePhrase (List[i - 1])) or 
                   (IsParenPhrase (List[i - 1])) or 
                   (CurrentWord = 0) then begin 
                  Tokens.Insert(0, KillQuotes (List[i])); 
                  Inc (CurrentWord); 
                end else begin 
                  if (i > 1) and (List[i - 2] = '[opt]') then begin 
                    Tokens.Insert(0, KillQuotes (List[i])); 
                    Inc (CurrentWord); 
                  end else 
                    Tokens[CurrentWord - 1] := KillQuotes (List[i]); 
                end; 
              end; 
            end; 
        end; 
      end else begin 
        if CurrentWord < Tokens.Count then begin 
          if List[i] = Tokens[CurrentWord] then begin 
            Inc (CurrentWord); 
          end else if not Optional then 
            Result := False; 
        end else if CurrentWord >= Tokens.Count then begin 
          Exit; 
        end; 
        Optional := False; 
      end; 
      Inc (i); 
    end; 
  finally 
    List.Free; 
  end; 
end; 
 
function TApdSapiAskForInfo.RecurseRules (Tokens : TStringList; 
                                          INIFile : TApdSapiGrammarList; 
                                          CurrentSection : string; 
                                          var CurrentWord : Integer; 
                                          var MatchingKey : string) : Boolean; 
{ Recurse Rules loads a new section and then analyzes it } 
var 
  i : Integer; 
  Value : string; 
  List : TStringList; 
 
begin 
  Result := False; 
  List := TStringList.Create; 
  try 
    { Does the section ask for exists as a section in the grammar } 
    if INIFile.SectionExists (CurrentSection) then begin 
      INIFile.ReadSectionValues (CurrentSection, List); 
      for i := 0 to List.Count - 1 do begin 
        Value := GetKey (List[i]); 
        if (Value = '') or (Value = CurrentSection) or 
           ((not IsAnglePhrase (Value)) and 
            (not IsParenPhrase (Value))) then begin 
          Value := GetValue (List[i]); 
          { Analyze the rule } 
          if AnalyzeRule (Tokens, Value, INIFile, List, CurrentWord, 
                          MatchingKey) then begin 
            { The rule matched.  Exit successfully } 
            if (Value <> '') and 
               ((MatchingKey = '') or (IsAnglePhrase (MatchingKey)) or 
                (IsParenPhrase (MatchingKey))) then 
              MatchingKey := GetKey (List[i]); 
            Result := True; 
            Exit; 
          end; 
        end; 
      end; 
    end; 
  finally 
    List.Free; 
  end; 
end; 
 
function TApdSapiAskForInfo.LocateRule (Tokens : TStringList) : string; 
 
  procedure PreparseGrammar (Grammar : TStringList); 
  var 
    i : Integer; 
  begin 
    for i := Grammar.Count - 1 downto 0 do begin 
      Grammar[i] := Trim (Grammar[i]); 
      if Grammar[i] = '' then 
        Grammar.Delete(i) 
      else if (Copy (Grammar[i], 1, 2) = '//') or 
              (Copy (Grammar[i], 1, 1) = ';') then 
        Grammar.Delete(i); 
    end; 
  end; 
 
var 
  INIFile : TApdSapiGrammarList; 
  MatchingKey : string; 
  FWorkingGrammar : TStringList; 
  CurrentWord : Integer; 
 
begin 
  Result := ''; 
  CurrentWord := 0; 
  MatchingKey := ''; 
  { Create an INI file that will be used to work with the grammar } 
  INIFile := TApdSapiGrammarList.Create; 
  try 
    { Create a work grammar and merge all the grammars into it } 
    FWorkingGrammar := TStringList.Create; 
    try 
      FWorkingGrammar.Text := FMainGrammar.Text + ^M^J + FAskForGrammar.Text; 
      PreparseGrammar (FWorkingGrammar); 
      INIFile.Assign (FWorkingGrammar);  
    finally 
      FWorkingGrammar.Free; 
    end; 
    { Analyze the grammar - start with the  rule } 
    RecurseRules (Tokens, INIFile, '', CurrentWord, MatchingKey); 
    Result := MatchingKey; 
  finally 
    INIFile.Free; 
  end; 
end; 
 
function TApdSapiAskForInfo.FindGrammarRule (var Phrase : string) : string; 
 
var 
  Tokens : TStringList; 
  i      : Integer; 
   
begin 
  Result := ''; 
  if Phrase = '' then 
    Exit; 
  Tokens := TStringList.Create; 
  try 
    { Break the phrase down into its tokens } 
    TokenizePhrase (Phrase, Tokens); 
    { Find the first rule that matches the phrase } 
    Result := LocateRule (Tokens); 
    Phrase := ''; 
    for i := 0 to Tokens.Count - 1 do  
      if i < Tokens.Count - 1 then 
        Phrase := Phrase + Tokens[i] + ' ' 
      else 
        Phrase := Phrase + Tokens[i]; 
  finally 
    Tokens.Free; 
  end; 
end; 
 
procedure TApdSapiAskForInfo.InitializeMainGrammar; 
begin 
  with FMainGrammar do begin 
    Clear; 
    Text := ApdDefaultPhoneGrammar; 
  end; 
end; 
 
procedure TApdSapiAskForInfo.SapiPhraseFinishHook (Sender : TObject; 
                                                   Phrase : string; 
                                                   Results : Integer); 
var 
  PhraseType  : TApdPhraseType; 
  PhraseRule  : string; 
  StringData  : PChar; 
 
begin 
  PhraseType := DeterminePhraseTypeEx (Phrase, PhraseRule); 
  case PhraseType of 
    ptHelp        : 
      FSapiEngine.Speak (Prompts.Help); 
    ptBack        : 
      begin 
        if psCanGoBack in Options then begin 
          FSapiEngine.DeRegisterPhraseFinishHook (SapiPhraseFinishHook); 
          FSapiEngine.Speak (Prompts.GoingBack); 
          if ReplyHandle <> 0 then 
            PostMessage (ReplyHandle, apw_SapiInfoPhrase, ApdSapiAskBack, 0); 
        end else  
          FSapiEngine.Speak (Prompts.CannotGoBack); 
      end; 
    ptOperator    : 
      begin 
        if psEnableOperator in Options then begin 
          FSapiEngine.DeRegisterPhraseFinishHook (SapiPhraseFinishHook); 
          FSapiEngine.Speak (Prompts.Operator); 
          if ReplyHandle <> 0 then 
            PostMessage (ReplyHandle, apw_SapiInfoPhrase, ApdSapiAskOperator, 0); 
        end else  
          FSapiEngine.Speak (Prompts.NoOperator); 
      end; 
    ptHangup      : 
      begin 
        if psEnableAskHangup in Options then begin 
          FSapiEngine.DeRegisterPhraseFinishHook (SapiPhraseFinishHook); 
          FSapiEngine.Speak (Prompts.HangingUp); 
          if ReplyHandle <> 0 then 
            PostMessage (ReplyHandle, apw_SapiInfoPhrase, ApdSapiAskHangup, 0); 
        end else  
          FSapiEngine.Speak (Prompts.CannotHangUp); 
      end; 
    ptWhere       : 
      begin 
        FSapiEngine.Speak (Prompts.Where); 
        if ReplyHandle <> 0 then 
          PostMessage (ReplyHandle, apw_SapiInfoPhrase, ApdSapiAskWhere, 0); 
      end; 
    ptRepeat      : 
      begin 
        FSapiEngine.Speak (Prompts.Main2); 
        if ReplyHandle <> 0 then 
          PostMessage (ReplyHandle, apw_SapiInfoPhrase, ApdSapiAskRepeat, 0); 
      end; 
    ptSpeakFaster : 
      begin 
        if psDisableSpeedChange in Options then begin 
          FSapiEngine.Speak (Prompts.NoSpeedChange); 
        end else begin 
          if FSapiEngine.DirectSS.Speed < FSapiEngine.DirectSS.MaxSpeed - 
                                          ApdSapiSpeedChange then begin 
            FSapiEngine.DirectSS.Speed := FSapiEngine.DirectSS.Speed + 
                                          ApdSapiSpeedChange; 
            FSapiEngine.Speak (Prompts.SpeakingFaster); 
            if ReplyHandle <> 0 then 
              PostMessage (ReplyHandle, apw_SapiInfoPhrase, 
                           ApdSapiAskSpeakFaster, 0); 
          end else  
            FSapiEngine.Speak (Prompts.MaxSpeed); 
        end; 
      end; 
    ptSpeakSlower : 
      begin 
        if psDisableSpeedChange in Options then begin 
          FSapiEngine.Speak (Prompts.NoSpeedChange); 
        end else begin 
          if FSapiEngine.DirectSS.Speed > FSapiEngine.DirectSS.MinSpeed + 
                                          ApdSapiSpeedChange then begin 
            FSapiEngine.DirectSS.Speed := FSapiEngine.DirectSS.Speed - 
                                          ApdSapiSpeedChange; 
            FSapiEngine.Speak (Prompts.SpeakingSlower); 
            if ReplyHandle <> 0 then 
              PostMessage (ReplyHandle, apw_SapiInfoPhrase, 
                           ApdSapiAskSpeakSlower, 0); 
          end else  
            FSapiEngine.Speak (Prompts.MinSpeed); 
        end; 
      end; 
    ptNone        : 
      begin 
      end; 
    ptUnknown     : 
      begin 
        { Parse unrecognized grammar rules here } 
        try 
          if ReplyHandle <> 0 then begin 
            StringData := StrAlloc (Length (Phrase) + 1); 
            StrPCopy (StringData, Phrase); 
            PostMessage (ReplyHandle, apw_SapiInfoPhrase, 
                         StrToInt (PhraseRule), LongInt (StringData)); 
          end; 
        except 
          on EConvertError do 
            FSapiEngine.Speak (Prompts.Unrecognized); 
        end; 
      end; 
  end; 
end; 
 
procedure TApdSapiAskForInfo.SetAskForGrammar (v : TStringList); 
begin 
  FAskForGrammar.Assign (v); 
end; 
 
procedure TApdSapiAskForInfo.SetMainGrammar (v : TStringList); 
begin 
  FMainGrammar.Assign (v); 
end; 
 
procedure TApdSapiAskForInfo.SetOptions (v : TApdSapiPhoneSettings); 
begin 
  if v <> FOptions then 
    FOptions := v; 
end; 
 
procedure TApdSapiAskForInfo.SetReplyHandle (v : THandle); 
begin 
  if v <> FReplyHandle then 
    FReplyHandle := v; 
end; 
 
{ TApdCustomSapiPhone } 
 
constructor TApdCustomSapiPhone.Create (AOwner : TComponent); 
begin 
  inherited Create (AOwner); 
  FSpellingEchoBack := True; 
 
  FInfo := TApdSapiAskForInfo.Create; 
  FPrompts := TApdSapiPhonePrompts.Create; 
  FList := TStringList.Create; 
 
  FSapiEngine := SearchSapiEngine (Owner); 
end; 
 
destructor TApdCustomSapiPhone.Destroy; 
begin 
  AbortAskFor; 
  CancelCall; 
 
  FInfo.Free; 
  FPrompts.Free; 
  FList.Free; 
 
  inherited Destroy; 
end; 
 
procedure TApdCustomSapiPhone.AbortAskFor; 
begin 
  ExitAskFor (prAbort, nil, ''); 
end; 
 
procedure TApdCustomSapiPhone.AskForDateDataHandler (LastReply : TApdPhraseType; 
                                                     LastRule : Integer; 
                                                     LastPhrase : string); 
var 
  OutDate : PDateTime; 
  Trusted : Boolean; 
 
begin 
  if LastRule = -500 then begin 
    GetMem (OutDate, SizeOf (TDateTime)); 
    OutDate^ := InterpretDate (LastPhrase, Trusted); 
    if Trusted then 
      ExitAskFor (prOk, OutDate, LastPhrase) 
    else 
      ExitAskFor (prCheck, OutDate, LastPhrase) 
  end else  
    FSapiEngine.Speak (Prompts.Unrecognized); 
end; 
 
procedure TApdCustomSapiPhone.AskForExtensionDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
var 
  OutExtension : PChar; 
 
begin 
  if LastRule = -400 then begin 
    FExtension := FExtension + LastPhrase; 
    FDigitCount := FDigitCount + 1; 
    if FDigitCount >= NumDigits then begin 
      OutExtension := nil; 
      GetMem (OutExtension, Length (OutExtension) + 1); 
      StrPCopy (OutExtension, FExtension); 
      ExitAskFor (prOk, OutExtension, LastPhrase); 
    end; 
  end else  
    FSapiEngine.Speak (Prompts.Unrecognized); 
end; 
 
procedure TApdCustomSapiPhone.AskForListDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
var 
  OutIndex : PInteger; 
  i : Integer; 
                                          
begin 
  GetMem (OutIndex, sizeof (Integer)); 
  if LastRule = -300 then begin 
    i := FList.IndexOf (LastPhrase); 
    if i > -1 then begin 
      OutIndex^ := i; 
      ExitAskFor (prOk, OutIndex, LastPhrase); 
    end else  
      FSapiEngine.Speak (Prompts.Unrecognized); 
  end else 
    FSapiEngine.Speak (Prompts.Unrecognized); 
end; 
 
procedure TApdCustomSapiPhone.AskForPhoneNumberDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
var 
  PhoneNumber : PChar; 
   
begin 
  if LastRule = -600 then begin 
    LastPhrase := InterpretPhoneNumber (LastPhrase); 
    GetMem (PhoneNumber, Length (LastPhrase) + 1); 
    StrPCopy (PhoneNumber, LastPhrase); 
    ExitAskFor (prOk, PhoneNumber, LastPhrase); 
  end else  
    FSapiEngine.Speak (Prompts.Unrecognized); 
end; 
 
procedure TApdCustomSapiPhone.AskForSpellingDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
var 
  OutSpelledWord : PChar; 
                                          
begin 
  if LastRule = -200 then begin 
    if FSpellingEchoBack then  
      FSapiEngine.Speak (LastPhrase); 
    FSpelledWord := FSpelledWord + LastPhrase; 
  end else if LastRule = -201 then begin 
    GetMem (OutSpelledWord, Length (FSpelledWord) + 1); 
    StrPCopy (OutSpelledWord, FSpelledWord); 
    ExitAskFor (prOk, OutSpelledWord, LastPhrase); 
  end else if LastRule = -202 then begin 
    FSpelledWord := ''; 
  end else if LastRule = -203 then begin 
     FSpelledWord := Copy (FSpelledWord, 1, Length (FSpelledWord) - 1); 
  end else if LastRule = -203 then 
    FSapiEngine.Speak (ApdYouHaveSpelled + FSpelledWord) 
  else 
    FSapiEngine.Speak (Prompts.Unrecognized); 
end; 
 
procedure TApdCustomSapiPhone.AskForTimeDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
var 
  OutTime : PDateTime;                                        
begin 
  if (LastRule <= -700) and (LastRule >= -705) then begin 
    GetMem (OutTime, SizeOf (TDateTime)); 
    OutTime^ := InterpretTime (LastPhrase); 
    ExitAskFor (prOk, OutTime, LastPhrase);  
  end else  
    FSapiEngine.Speak (Prompts.Unrecognized); 
end; 
 
procedure TApdCustomSapiPhone.AskForYesNoDataHandler (LastReply : TApdPhraseType; 
                                       LastRule : Integer; 
                                       LastPhrase : string); 
var 
  Reply : PBoolean;                                        
begin 
  if LastRule = -100 then begin 
    GetMem (Reply, SizeOf (Boolean)); 
    Reply^ := True; 
    ExitAskFor (prOk, Reply, LastPhrase); 
  end else if LastRule = -101 then begin 
    GetMem (Reply, SizeOf (Boolean)); 
    Reply^ := False; 
    ExitAskFor (prOk, Reply, LastPhrase); 
  end else  
    FSapiEngine.Speak (Prompts.Unrecognized); 
end; 
 
procedure TApdCustomSapiPhone.AskForDateTrigger (Reply : TApdSapiPhoneReply; 
                                                 Data : Pointer; 
                                                 SpokenData : string); 
begin 
  { Events may still be firing after components are destroyed - or while they 
    are being destroyed.  Check for this in all of the event triggers } 
  if csDestroying in ComponentState then 
    Exit; 
  if Assigned (FOnAskForDateFinish) then begin 
    if Assigned (Data) then begin 
      FOnAskForDateFinish (Self, Reply, TDateTime (Data^), SpokenData); 
      FreeMem (Data); 
    end else 
      FOnAskForDateFinish (Self, Reply, 0, SpokenData); 
  end; 
end; 
 
procedure TApdCustomSapiPhone.AskForExtensionTrigger (Reply : TApdSapiPhoneReply; 
                                                      Data : Pointer; 
                                                      SpokenData : string); 
begin 
  { Events may still be firing after components are destroyed - or while they 
    are being destroyed.  Check for this in all of the event triggers } 
  if csDestroying in ComponentState then 
    Exit; 
  if Assigned (FOnAskForExtensionFinish) then begin 
    if Assigned (Data) then begin 
      FOnAskForExtensionFinish (Self, Reply, string (PChar (Data)), 
                                SpokenData); 
      FreeMem (Data); 
    end else 
      FOnAskForExtensionFinish (Self, Reply, '', SpokenData); 
  end; 
end; 
 
procedure TApdCustomSapiPhone.AskForListTrigger (Reply : TApdSapiPhoneReply; 
                                                 Data : Pointer; 
                                                 SpokenData : string); 
var 
  OutIdx : PInteger; 
 
begin 
  { Events may still be firing after components are destroyed - or while they 
    are being destroyed.  Check for this in all of the event triggers } 
  if csDestroying in ComponentState then 
    Exit; 
  if Assigned (FOnAskForListFinish) then begin 
    if Assigned (Data) then begin 
      OutIdx := Data; 
      FOnAskForListFinish (Self, Reply, OutIdx^, SpokenData); 
      FreeMem (Data); 
    end else 
      FOnAskForListFinish (Self, Reply, -1, SpokenData); 
  end; 
end; 
 
procedure TApdCustomSapiPhone.AskForPhoneNumberTrigger ( 
                                   Reply : TApdSapiPhoneReply; 
                                   Data : Pointer; 
                                   SpokenData : string); 
begin 
  { Events may still be firing after components are destroyed - or while they 
    are being destroyed.  Check for this in all of the event triggers } 
  if csDestroying in ComponentState then 
    Exit; 
  if Assigned (FOnAskForPhoneNumberFinish) then begin 
    if Assigned (Data) then begin 
      FOnAskForPhoneNumberFinish (Self, Reply, string (PChar (Data)), 
                                  SpokenData); 
      FreeMem (Data); 
    end else 
      FOnAskForPhoneNumberFinish (Self, Reply, '', SpokenData); 
  end; 
end; 
 
procedure TApdCustomSapiPhone.AskForSpellingTrigger ( 
                                   Reply : TApdSapiPhoneReply; 
                                   Data : Pointer; 
                                   SpokenData : string); 
begin 
  { Events may still be firing after components are destroyed - or while they 
    are being destroyed.  Check for this in all of the event triggers } 
  if csDestroying in ComponentState then 
    Exit; 
  if Assigned (FOnAskForSpellingFinish) then begin 
    if Assigned (Data) then begin 
      FOnAskForSpellingFinish (Self, Reply, string (PChar (Data)), 
                               SpokenData); 
      FreeMem (Data); 
    end else 
      FOnAskForSpellingFinish (Self, Reply, '', SpokenData); 
  end; 
end; 
 
procedure TApdCustomSapiPhone.AskForTimeTrigger (Reply : TApdSapiPhoneReply; 
                                                 Data : Pointer; 
                                                 SpokenData : string); 
var 
  OutTime : PDateTime; 
 
begin 
  { Events may still be firing after components are destroyed - or while they 
    are being destroyed.  Check for this in all of the event triggers } 
  if csDestroying in ComponentState then 
    Exit; 
  if Assigned (FOnAskForTimeFinish) then begin 
    if Assigned (Data) then begin 
      OutTime := Data; 
      FOnAskForTimeFinish (Self, Reply, TDateTime (OutTime^), SpokenData); 
      FreeMem (Data); 
    end else 
      FOnAskForTimeFinish (Self, Reply, 0, SpokenData); 
  end; 
end; 
 
procedure TApdCustomSapiPhone.AskForYesNoTrigger (Reply : TApdSapiPhoneReply; 
                                   Data : Pointer; 
                                   SpokenData : string); 
var 
  Response : PBoolean; 
 
begin 
  { Events may still be firing after components are destroyed - or while they 
    are being destroyed.  Check for this in all of the event triggers } 
  if csDestroying in ComponentState then 
    Exit; 
  if Assigned (FOnAskForYesNoFinish) then begin 
    if Assigned (Data) then begin 
      Response := Data; 
      FOnAskForYesNoFinish (Self, Reply, Boolean (Response^), SpokenData); 
      FreeMem (Data); 
    end else 
      FOnAskForYesNoFinish (Self, Reply, False, SpokenData); 
  end; 
end; 
 
procedure TApdCustomSapiPhone.AskForDate (NewPrompt1 : string); 
begin 
  AskForDateEx (NewPrompt1, '', '', '', '', ''); 
end; 
 
procedure TApdCustomSapiPhone.AskForDateEx (NewPrompt1 : string; 
                                            NewPrompt2 : string; 
                                            NewHelp1 : string; 
                                            NewHelp2 : string; 
                                            NewWhere1 : string; 
                                            NewWhere2 : string); 
 
begin 
  AbortAskFor; 
  { Initialize the custom grammar } 
 
  with FInfo.AskForGrammar do begin 
    Clear; 
    Text :=ApdAskForDateGrammar; 
  end; 
 
  { Initialize the ask for engine } 
 
  FInfo.ReplyHandle := FHandle; 
  FInfo.SapiEngine := SapiEngine; 
  FInfo.Options := Options; 
 
  { Set up the prompts } 
 
  SetDefaultPrompts (NewPrompt1, NewPrompt2, NewHelp1, NewHelp2, NewWhere1, 
                     NewWhere2); 
 
  { Start listening for a response } 
 
  FCustomDataHandler := AskForDateDataHandler; 
  FEventTrigger := AskForDateTrigger; 
 
  FInfo.AskFor; 
end; 
 
procedure TApdCustomSapiPhone.AskForExtension (NewPrompt1 : string); 
begin 
  AskForExtensionEx (NewPrompt1, '', '', '', 0, '', '', '', ''); 
end; 
 
procedure TApdCustomSapiPhone.AskForExtensionEx (NewPrompt1 : string; 
                                                 NewPrompt2 : string; 
                                                 NewTooManyDigits : string; 
                                                 NewTooFewDigits : string; 
                                                 NewNumDigits : Integer; 
                                                 NewHelp1 : string; 
                                                 NewHelp2 : string; 
                                                 NewWhere1 : string; 
                                                 NewWhere2 : string); 
begin 
  AbortAskFor; 
  { Initialize the custom grammar } 
  with FInfo.AskForGrammar do begin 
    Clear; 
    Text :=ApdAskForExtensionGrammar; 
  end; 
 
  { Initialize the ask for engine } 
 
  FInfo.ReplyHandle := FHandle; 
  FInfo.SapiEngine := SapiEngine; 
  FInfo.Options := Options; 
  FExtension := ''; 
  FDigitCount := 0; 
 
  { Set up the prompts } 
 
  SetDefaultPrompts (NewPrompt1, NewPrompt2, NewHelp1, NewHelp2, NewWhere1, 
                     NewWhere2); 
 
  { Start listening for a response } 
 
  FCustomDataHandler := AskForExtensionDataHandler; 
  FEventTrigger := AskForExtensionTrigger; 
 
  FInfo.AskFor; 
end; 
 
procedure TApdCustomSapiPhone.AskForList (List : TStringList; 
                                          NewPrompt1 : string); 
begin 
  AskForListEx (List, NewPrompt1, '', '', '', '', ''); 
end; 
 
procedure TApdCustomSapiPhone.AskForListEx (List : TStringList; 
                                            NewPrompt1 : string; 
                                            NewPrompt2 : string; 
                                            NewHelp1 : string; 
                                            NewHelp2 : string; 
                                            NewWhere1 : string; 
                                            NewWhere2 : string); 
var 
  i : Integer; 
 
begin 
  AbortAskFor; 
  { Initialize the custom grammar } 
  { Always build the grammar manually } 
  with FInfo.AskForGrammar do begin 
    Clear; 
    Add ('[]'); 
    Add ('=(MyList)'); 
    Add (''); 
    Add ('[(MyList)]'); 
    Add ('-300=(MyListItems)'); 
    Add (''); 
    Add ('[(MyListItems)]'); 
    for i := 0 to List.Count - 1 do 
      Add ('=' + List[i]); 
  end; 
 
  { Initialize the ask for engine } 
 
  FInfo.ReplyHandle := FHandle; 
  FInfo.SapiEngine := SapiEngine; 
  FInfo.Options := Options; 
  FList.Assign (List); 
 
  { Set up the prompts } 
 
  SetDefaultPrompts (NewPrompt1, NewPrompt2, NewHelp1, NewHelp2, NewWhere1, 
                     NewWhere2); 
 
  { Start listening for a response } 
 
  FCustomDataHandler := AskForListDataHandler; 
  FEventTrigger := AskForListTrigger; 
 
  FInfo.AskFor; 
end; 
 
procedure TApdCustomSapiPhone.AskForPhoneNumber (NewPrompt1 : string); 
begin 
  AskForPhoneNumberEx (NewPrompt1, '', '', '', '', '', '', '', ''); 
end; 
 
procedure TApdCustomSapiPhone.AskForPhoneNumberEx (NewPrompt1 : string; 
                                                   NewPrompt2 : string; 
                                                   NewAskAreaCode : string; 
                                                   NewAskNextThree : string; 
                                                   NewAskLastFour : string; 
                                                   NewHelp1 : string; 
                                                   NewHelp2 : string; 
                                                   NewWhere1 : string; 
                                                   NewWhere2 : string); 
begin 
  AbortAskFor; 
  { Initialize the custom grammar } 
  with FInfo.AskForGrammar do begin 
    Clear; 
    Text := ApdAskForPhoneNumberGrammar; 
  end; 
 
  { Initialize the ask for engine } 
 
  FInfo.ReplyHandle := FHandle; 
  FInfo.SapiEngine := SapiEngine; 
  FInfo.Options := Options; 
 
  { Set up the prompts } 
 
  SetDefaultPrompts (NewPrompt1, NewPrompt2, NewHelp1, NewHelp2, NewWhere1, 
                     NewWhere2); 
 
  { Start listening for a response } 
 
  FCustomDataHandler := AskForPhoneNumberDataHandler; 
  FEventTrigger := AskForPhoneNumberTrigger; 
 
  FInfo.AskFor; 
end; 
 
procedure TApdCustomSapiPhone.AskForSpelling (NewPrompt1 : string); 
begin 
  AskForSpellingEx (NewPrompt1, '', '', '', '', ''); 
end; 
 
procedure TApdCustomSapiPhone.AskForSpellingEx (NewPrompt1 : string; 
                                                NewPrompt2 : string; 
                                                NewHelp1 : string; 
                                                NewHelp2 : string; 
                                                NewWhere1 : string; 
                                                NewWhere2 : string); 
 
begin 
  AbortAskFor; 
  { Initialize the custom grammar } 
 
  with FInfo.AskForGrammar do begin 
    Clear; 
    Text := ApdAskForSpellingGrammar; 
  end; 
 
  { Initialize the ask for engine } 
 
  FInfo.ReplyHandle := FHandle; 
  FInfo.SapiEngine := SapiEngine; 
  FInfo.Options := Options; 
  FSpelledWord := ''; 
 
  { Set up the prompts } 
 
  SetDefaultPrompts (NewPrompt1, NewPrompt2, NewHelp1, NewHelp2, NewWhere1, 
                     NewWhere2); 
 
  { Start listening for a response } 
 
  FCustomDataHandler := AskForSpellingDataHandler; 
  FEventTrigger := AskForSpellingTrigger; 
 
  FInfo.AskFor; 
end; 
 
procedure TApdCustomSapiPhone.AskForTime (NewPrompt1 : string); 
begin 
  AskForTimeEx (NewPrompt1, '', '', '', '', ''); 
end; 
 
procedure TApdCustomSapiPhone.AskForTimeEx (NewPrompt1 : string; 
                                            NewPrompt2 : string; 
                                            NewHelp1 : string; 
                                            NewHelp2 : string; 
                                            NewWhere1 : string; 
                                            NewWhere2 : string); 
begin 
  AbortAskFor; 
  { Initialize the custom grammar } 
  with FInfo.AskForGrammar do begin 
    Clear; 
    Text := ApdAskForTimeGrammar; 
  end; 
 
  { Initialize the ask for engine } 
   
  FInfo.ReplyHandle := FHandle; 
  FInfo.SapiEngine := SapiEngine; 
  FInfo.Options := Options; 
 
  { Set up the prompts } 
 
  SetDefaultPrompts (NewPrompt1, NewPrompt2, NewHelp1, NewHelp2, NewWhere1, 
                     NewWhere2); 
 
  { Start listening for a response } 
 
  FCustomDataHandler := AskForTimeDataHandler; 
  FEventTrigger := AskForTimeTrigger; 
 
  FInfo.AskFor; 
end; 
 
procedure TApdCustomSapiPhone.AskForYesNo (NewPrompt1 : string); 
begin 
  AskForYesNoEx (NewPrompt1, '', '', '', '', ''); 
end; 
 
procedure TApdCustomSapiPhone.AskForYesNoEx (NewPrompt1 : string; 
                                             NewPrompt2 : string; 
                                             NewHelp1 : string; 
                                             NewHelp2 : string; 
                                             NewWhere1 : string; 
                                             NewWhere2 : string); 
begin 
  AbortAskFor; 
  { Initialize the custom grammar } 
 
  with FInfo.AskForGrammar do begin 
    Clear; 
    Text := ApdAskForYesNoGrammar; 
  end; 
 
  { Initialize the ask for engine } 
   
  FInfo.ReplyHandle := FHandle; 
  FInfo.SapiEngine := SapiEngine; 
  FInfo.Options := Options; 
 
  { Set up the prompts } 
 
  SetDefaultPrompts (NewPrompt1, NewPrompt2, NewHelp1, NewHelp2, NewWhere1, 
                     NewWhere2); 
 
  { Start listening for a response } 
 
  FCustomDataHandler := AskForYesNoDataHandler; 
  FEventTrigger := AskForYesNoTrigger; 
 
  FInfo.AskFor; 
end; 
 
function TApdCustomSapiPhone.ConvertResponse (RCode : Integer ) : 
                                              TApdSapiPhoneReply; 
begin 
  case RCode of 
    $0                    : Result := prOk; 
    ApdTCR_ABORT          : Result := prAbort; 
    ApdTCR_NORESPONSE     : Result := prNoResponse; 
    ApdTCR_ASKOPERATOR    : Result := prOperator; 
    ApdTCR_ASKHANGUP      : Result := prHangUp; 
    ApdTCR_ASKBACK        : Result := prBack; 
    ApdTCR_ASKWHERE       : Result := prWhere; 
    ApdTCR_ASKHELP        : Result := prHelp; 
    ApdTCR_ASKREPEAT      : Result := prRepeat; 
    ApdTCR_ASKSPEAKFASTER : Result := prSpeakFaster; 
    ApdTCR_ASKSPEAKSLOWER : Result := prSpeakSlower; 
  else 
    Result := prUnknown; 
  end; 
end; 
 
procedure TApdCustomSapiPhone.DoLineCallState (Device, P1, P2, P3 : LongInt); 
 
  procedure AssignWaveInDevice; 
  var 
    Res             : Longint; 
    DeviceID        : DWORD; 
    WaveFormat      : TWaveFormatEx; 
    WaveId          : SDATA; 
 
  begin 
    DeviceID := GetWaveDeviceId (False); 
    { Set up the wave format. } 
    with WaveFormat do begin 
      wFormatTag      := WAVE_FORMAT_PCM; 
      nChannels       := Channels; 
      nSamplesPerSec  := SamplesPerSecond; 
      nAvgBytesPerSec := SamplesPerSecond * (BitsPerSample div 8); 
      nBlockAlign     := (Channels * BitsPerSample) div 8; 
      wBitsPerSample  := BitsPerSample; 
      cbSize          := 0; 
    end; 
    { Open the TAPI wave in device. } 
 
    IAMM := CreateComObject (CLSID_MMAudioSource) as IAudioMultiMediaDevice; 
    if Assigned (IAMM) then begin 
      Res := IAMM.DeviceNumSet (DeviceID); 
      if Res <> 0 then 
        raise Exception.Create (ecApdBadDeviceNum + 
                                IntToStr (Res)); 
      IAT := CreateComObject (CLSID_AudioSourceTel) as IAudioTel; 
      if Assigned (IAT) then begin 
        try 
          WaveID.pData := @WaveFormat; 
          WaveID.dwSize := SizeOf (TWaveFormatEx); 
          FSapiEngine.CheckError (IAT.WaveFormatSet (WaveID)); 
          FSapiEngine.CheckError (IAT.AudioObject (IAMM)); 
          FSapiEngine.DirectSR.InitAudioSourceObject (DWORD (IAT));  
        except 
          on E:EOleException do begin 
            FSapiEngine.CheckError (E.ErrorCode); 
          end; 
        end; 
      end else 
        raise Exception.Create (ecApdCannotCreateCOM); 
    end else 
      raise Exception.Create (ecApdCannotCreateCOM); 
  end; 
 
begin 
  inherited DoLineCallState (Device, P1, P2, P3); 
  case P1 of 
    LineCallState_Connected : 
      begin 
        if Assigned (FSapiEngine) then begin 
          if (not (tfPhoneOptimized in FSapiEngine.SSVoices.Features[ 
                  FSapiEngine.SSVoices.CurrentVoice])) or 
             (not (sfPhoneOptimized in FSapiEngine.SREngines.Features[ 
                  FSapiEngine.SREngines.CurrentEngine])) then 
            raise ESapiPhoneError.Create (-1, ecApdNotPhone);  
          AssignWaveInDevice; 
          { in Win9x, sending audio output via SAPI to the wave handle causes } 
          { the audio to go through the sound card instead of TAPI, we'll use } 
          { the WaveDeviceID instead of the WaveInHandle for 9x } 
          if IsWin2000 then                                              {!!.04} 
            FSapiEngine.DirectSS.InitAudioDestMM (WaveInHandle)          {!!.02} 
          else                                                           {!!.02} 
            FSapiEngine.DirectSS.InitAudioDestMM (GetWaveDeviceId(True));{!!.02} 
             
          try 
            FSapiEngine.DirectSR.Microphone := scApdTelMic; 
          except 
            on EOleError do 
              if Assigned (FSapiEngine.OnSRError) then 
                FSapiEngine.OnSRError (FSapiEngine, 0, '', 
                                       ApdStrE_CANNOTSETMIC); 
          end; 
        end; 
        PostMessage (FHandle, apw_SapiPhoneCallBack, ApdSapiConnect, 0); 
      end; 
    LineCallState_Disconnected : 
      begin 
        AbortAskFor; 
        if Assigned (FSapiEngine) then begin 
          FSapiEngine.Speaking := False; 
          FSapiEngine.Listening := False; 
          { Give the engine a little slack } 
          DelayTicks (4, True); 
          FSapiEngine.DirectSS.InitAudioDestMM (0); 
          FSapiEngine.DirectSR.InitAudioSourceObject (0);  
          try 
            FSapiEngine.DirectSR.Microphone := scApdTelMic; 
          except 
            on EOleError do begin 
              { Events may still be firing after components are destroyed - or while they 
                are being destroyed.  Check for this in all of the event triggers } 
              if csDestroying in ComponentState then 
                Exit; 
              if Assigned (FSapiEngine.OnSRError) then 
                FSapiEngine.OnSRError (FSapiEngine, 0, '', 
                                       ApdStrE_CANNOTSETMIC); 
            end; 
          end; 
        end; 
        PostMessage (FHandle, apw_SapiPhoneCallBack, ApdSapiDisConnect, 0); 
      end; 
  end; 
end; 
 
function TApdCustomSapiPhone.GetPhraseData (LParam : Longint) : string; 
var 
  TheString : PChar; 
begin 
  if LParam = 0 then 
    Exit; 
  TheString := PChar (LParam); 
  Result := TheString; 
  StrDispose (TheString); 
end; 
 
function TApdCustomSapiPhone.FixNumerics (Phrase : string) : string; 
var 
  i : Integer; 
  j : Integer; 
  Tokens : TStringList; 
begin 
  Tokens := TStringList.Create; 
  try 
    TokenizePhrase (Phrase, Tokens); 
    i := Tokens.Count - 1; 
    while i >= 0 do begin 
      for j := 1 to ApdNumericCvtEntries do 
        if Tokens[i] = ApdNumericCvt[j].InputPhrase then 
          case ApdNumericCvt[j].PhraseType of 
            ptConvert : 
              Tokens[i] := ApdNumericCvt[j].OutputPhrase; 
            ptDrop : 
              Tokens.Delete (i); 
            ptConvertJoin : 
              if i < Tokens.Count - 1 then begin  
                Tokens[i] := ApdNumericCvt[j].OutputPhrase + 
                             Tokens[i + 1]; 
                Tokens.Delete(i + 1); 
              end else 
                Tokens[i] := ApdNumericCvt[j].OutputPhrase; 
          end; 
      Dec (i); 
    end; 
    Result := ''; 
    for i := 0 to Tokens.Count - 1 do 
      if i < Tokens.Count - 1 then 
        Result := Result + Tokens[i] + ' ' 
      else 
        Result := Result + Tokens[i]; 
  finally 
    Tokens.Free; 
  end; 
end; 
 
function TApdCustomSapiPhone.InterpretDate (Phrase : string; 
                                            var Trusted : Boolean) : TDateTime; 
 
  function ConvertDateWord (DateWord : string; var Number : Integer) : TApdDateWordType; 
  var 
    i : Integer; 
  begin 
    Number := 0; 
    Result := dwtUnknown; 
    for i := 1 to ApdDateWordCount do 
      if ApdDateWordList[i].DateWord = DateWord then begin 
        Result := ApdDateWordList[i].WordType; 
        Number := ApdDateWordList[i].Number; 
        Exit; 
      end; 
  end; 
 
type 
  TDateState = (dsDateStart, dsInMonth, dsInYear, dsInYear2, dsInNextLast, 
                dsIdle); 
 
var 
  Tokens          : TStringList; 
  State           : TDateState; 
  i               : Integer; 
  Month           : Word; 
  Day             : Word; 
  Year            : Word; 
  Number          : Integer; 
  DayOffset       : Integer; 
  MonthOffset     : Integer; 
  YearOffset      : Integer; 
  OffsetDir       : Integer; 
  TodaysDayNum    : Integer; 
  TodaysMonthNum  : Integer; 
  WorkNum         : Integer; 
  CurYear         : Integer; 
 
begin 
  Result := 0; 
  Trusted := True; 
  Phrase := FixNumerics (Phrase); 
  if Phrase = '' then 
    Exit; 
 
  State := dsDateStart; 
  DecodeDate (Date, Year, Month, Day); 
  CurYear := Year; 
  TodaysDayNum := DayOfTheWeek (Day); 
  TodaysMonthNum := MonthOfTheYear (Day); 
 
  DayOffset := 0; 
  MonthOffset := 0; 
  YearOffset := 0; 
  OffsetDir := 1; 
 
  Tokens := TStringList.Create; 
  try 
    { Break the phrase down into its tokens } 
    TokenizePhrase (Phrase, Tokens); 
 
    for i := 0 to Tokens.Count - 1 do begin 
      case State of 
        dsDateStart : 
          begin 
            case ConvertDateWord (Tokens[i], Number) of 
              dwtMonth : 
                begin 
                  Month := Number; 
                  State := dsInMonth; 
                end; 
              dwtDay : 
                begin 
                  if Number >= TodaysDayNum then 
                    DayOffset := Number - TodaysDayNum + 1 
                  else 
                    DayOffset := 8 + Number - TodaysDayNum; 
                  State := dsIdle; 
                end; 
              dwtNext : 
                State := dsInNextLast; 
              dwtLast : 
                begin 
                  OffsetDir := -1; 
                  State := dsInNextLast; 
                end; 
              dwtToday : 
                State := dsIdle; 
              dwtTomorrow : 
                begin 
                  State := dsIdle; 
                  DayOffset := 1; 
                end; 
              dwtYesterday : 
                begin 
                  State := dsIdle; 
                  DayOffset := -1; 
                end; 
              dwtUnknown : 
                begin 
                  try 
                    Month := StrToInt (Tokens[i]); 
                    State := dsInMonth;  
                  except 
                    on EConvertError do 
                      begin 
                      end; 
                  end; 
                end; 
            end; 
          end; 
 
        dsInMonth : 
          begin 
            try 
              Day := StrToInt (Tokens[i]); 
              if Day > 31 then begin 
                Year := Day; 
                Day := 1; 
                State := dsIdle; 
                Trusted := False; 
              end else 
                State := dsInYear; 
            except 
              on EConvertError do 
                begin 
                end; 
            end; 
          end; 
 
        dsInYear : 
          begin 
            try 
              Year := StrToInt (Tokens[i]); 
              if Year < 10 then 
                Year := Year * 1000 
              else 
                Year := Year * 100; 
              State := dsInYear2; 
              if i = Tokens.Count - 1 then 
                Year := Year div  100 + (CurYear div 100) * 100; 
            except 
              on EConvertError do 
                begin 
                end; 
            end; 
          end; 
 
        dsInYear2 : 
          begin 
            try 
              WorkNum := StrToInt (Tokens[i]); 
              if WorkNum <> 0 then begin 
                Year := Year + StrToInt (Tokens[i]); 
                State := dsIdle; 
              end; 
            except 
              on EConvertError do 
                begin 
                end; 
            end; 
          end; 
 
        dsInNextLast : 
          begin 
            case ConvertDateWord (Tokens[i], Number) of 
              dwtWeekOff : 
                begin 
                  DayOffset := 7; 
                  State := dsIdle; 
                end; 
              dwtMonthOff : 
                begin 
                  MonthOffset := 1; 
                  State := dsIdle; 
                end; 
              dwtYearOff : 
                begin 
                  YearOffset := 1; 
                  State := dsIdle; 
                end; 
              dwtDay : 
                begin 
                  if OffsetDir > 0 then 
                    DayOffset := 8 + Number - TodaysDayNum 
                  else 
                    DayOffset := 6 + TodaysDayNum - Number; 
                  State := dsIdle; 
                end; 
              dwtMonth : 
                begin 
                  if OffsetDir > 0 then 
                    MonthOffset := 4 + Number - TodaysMonthNum 
                  else 
                    MonthOffset := 8 + TodaysMonthNum - Number; 
                  State := dsIdle; 
                end; 
            end; 
          end; 
 
        dsIdle : 
          begin 
          end; 
      end; 
    end; 
  finally 
    Tokens.Free; 
  end; 
 
  Result := EncodeDate (Year, Month, Day) + (DayOffset * OffsetDir); 
  Result := IncYear (Result, YearOffset * OffsetDir); 
  Result := IncMonth (Result, MonthOffset * OffsetDir); 
end; 
 
function TApdCustomSapiPhone.InterpretPhoneNumber (Phrase : string) : string; 
var 
  State : Integer; 
  i : Integer; 
  List : TStringList; 
  j : Integer; 
  GotOne : Boolean; 
 
begin 
  Result := ''; 
  State := 0; 
  List := TStringList.Create; 
  try 
    TokenizePhrase (Phrase, List); 
    for i := 0 to List.Count - 1 do begin 
      case State of 
        0 : 
          begin 
            GotOne := False; 
            if List[i] = 'hundred' then 
              State := 1 
            else begin 
              for j := 1 to ApdPhoneNumberEntries do 
               if ApdPhoneNumberConvert[j].InputPhrase = List[i] then begin 
                 Result := Result + ApdPhoneNumberConvert[j].OutputPhrase; 
                 GotOne := True; 
                 Break; 
               end; 
              if not GotOne then 
                Result := Result + List[i]; 
            end; 
          end; 
        1 : 
          begin 
            GotOne := False; 
            if List[i] <> 'and' then 
              Result := Result + '00'; 
            for j := 1 to ApdPhoneNumberEntries do 
             if ApdPhoneNumberConvert[j].InputPhrase = List[i] then begin 
               Result := Result + ApdPhoneNumberConvert[j].OutputPhrase; 
               GotOne := True; 
               Break; 
             end; 
            if not GotOne then 
              Result := Result + List[i]; 
            State := 0; 
          end; 
      end; 
    end; 
  finally 
    List.Free; 
  end; 
end; 
   
function TApdCustomSapiPhone.InterpretTime (Phrase : string) : TDateTime; 
 
  function ConvertTimeWord (TimeWord : string) : TApdTimeWordType; 
  var 
    i : Integer; 
  begin 
    Result := twtUnknown; 
    for i := 1 to ApdTimeWordCount do 
      if ApdTimeWordList[i].TimeWord = TimeWord then begin 
        Result := ApdTimeWordList[i].WordType; 
        Exit; 
      end; 
  end; 
   
type 
  TTimeState = (tsTimeStart, tsInQuarterHalf, tsInBeforeAfter, 
                tsInHours, tsInOClock, tsInMinutes, tsGetAMPM, 
                tsIdle); 
  TTimeOffset = (toNone, toQuarter, toHalf); 
  TTimeBeforeAfter = (tbaAfter, tbaBefore); 
  TTimeAMPM = (tapAM, tapPM); 
 
 
var 
  Tokens          : TStringList; 
  State           : TTimeState; 
  i               : Integer; 
  TimeOffset      : TTimeOffset; 
  TimeBeforeAfter : TTimeBeforeAfter; 
  TimeAMPM        : TTimeAMPM; 
  Hour            : Integer; 
  Minute          : Integer; 
 
begin 
  Result := 0; 
  Phrase := FixNumerics (Phrase); 
  if Phrase = '' then 
    Exit; 
 
  State := tsTimeStart; 
  TimeOffset := toNone; 
  TimeBeforeAfter := tbaAfter; 
  TimeAMPM := tapAM; 
  Hour := 0; 
  Minute := 0; 
 
  Tokens := TStringList.Create; 
  try 
    { Break the phrase down into its tokens } 
    TokenizePhrase (Phrase, Tokens); 
 
    for i := 0 to Tokens.Count - 1 do begin 
      case State of 
        tsTimeStart : 
          begin 
            case ConvertTimeWord (Tokens[i]) of 
              twtQuarter  : 
                begin 
                  TimeOffset := toQuarter; 
                  State := tsInQuarterHalf; 
                end; 
              twtHalf     : 
                begin 
                  TimeOffset := toHalf; 
                  State := tsInQuarterHalf; 
                end; 
              twtMidnight : 
                begin 
                  Hour := 12; 
                  State := tsIdle; 
                  TimeAMPM := tapAM; 
                end; 
              twtNoon     : 
                begin 
                  Hour := 12; 
                  TimeAMPM := tapPM; 
                  State := tsIdle; 
                end; 
              twtUnknown  : 
                begin 
                  try 
                    Hour := StrToInt (Tokens[i]); 
                  except 
                    on EConvertError do begin 
                    end; 
                  end; 
                  if Hour < 7 then 
                    TimeAMPM := tapPM; 
                  State := tsInHours; 
                end; 
              else begin 
              end; 
            end; 
          end; 
 
        tsInQuarterHalf : 
          begin 
            case ConvertTimeWord (Tokens[i]) of 
              twtAfter  : 
                State := tsInBeforeAfter; 
              twtBefore : 
                begin 
                  TimeBeforeAfter := tbaBefore; 
                  State := tsInBeforeAfter; 
                end; 
              else begin 
              end; 
            end; 
          end; 
 
        tsInBeforeAfter : 
          begin 
            case ConvertTimeWord (Tokens[i]) of 
              twtMidnight : 
                begin 
                  Hour := 12; 
                  TimeAMPM := tapAM; 
                  State := tsIdle; 
                end; 
              twtNoon : 
                begin 
                  Hour := 12; 
                  TimeAMPM := tapPM; 
                  State := tsIdle; 
                end; 
              twtUnknown : 
                begin 
                  try 
                    Hour := StrToInt (Tokens[i]); 
                  except 
                    on EConvertError do begin 
                    end; 
                  end; 
                  if Hour < 7 then 
                    TimeAMPM := tapPM; 
                  State := tsGetAMPM; 
                end; 
            end; 
          end; 
 
        tsGetAMPM : 
          begin 
            case ConvertTimeWord (Tokens[i]) of 
              twtAM : 
                State := tsIdle; 
              twtPM : 
                begin 
                  TimeAMPM := tapPM; 
                  State := tsIdle; 
                end; 
              else 
                begin 
                end; 
            end; 
          end; 
 
        tsInHours : 
          begin 
            case ConvertTimeWord (Tokens[i]) of 
              twtAfter : 
                begin 
                  Minute := Hour; 
                  Hour := 0; 
                  State := tsInBeforeAfter; 
                end; 
              twtBefore : 
                begin 
                  Minute := Hour; 
                  Hour := 0; 
                  State := tsInBeforeAfter; 
                  TimeBeforeAfter := tbaBefore; 
                end; 
              twtUnknown : 
                begin 
                  try 
                    Minute := StrToInt (Tokens[i]); 
                  except 
                    on EConvertError do begin 
                    end; 
                  end; 
                  State := tsGetAMPM; 
                end; 
            end; 
          end; 
 
        tsIdle : 
          begin 
          end; 
      end; 
    end; 
  finally 
    Tokens.Free; 
  end; 
 
  if (TimeAMPM = tapAM) and (Hour = 12) then 
    Hour := 0; 
  if (TimeAMPM = tapPM) and (Hour < 12) then 
    Hour := Hour + 12; 
  case TimeOffset of 
    toHalf : Minute := 30; 
    toQuarter : Minute := 15; 
  end; 
  if TimeBeforeAfter = tbaBefore then begin 
    Minute := 60 - Minute; 
    Dec (Hour); 
    if Hour < 0 then 
      Hour := 23;  
  end; 
 
  Result := EncodeTime (Hour, Minute, 0, 0); 
end; 
 
procedure TApdCustomSapiPhone.Notification(AComponent : TComponent; 
                                            Operation : TOperation); 
  {-Handle new/deleted components} 
begin 
  inherited Notification(AComponent, Operation); 
 
  if Operation = opRemove then begin 
    {Owned components going away} 
    if AComponent = FSapiEngine then 
      FSapiEngine := nil; 
  end else if Operation = opInsert then begin 
    {Check for new Sapi Engine} 
    if AComponent is TApdSapiEngine then begin 
      if not Assigned (FSapiEngine) then 
        FSapiEngine := TApdSapiEngine (AComponent); 
    end; 
  end; 
end; 
 
procedure TApdCustomSapiPhone.PhraseHandler (var Msg : TMessage); 
var 
  LastReply : TApdPhraseType; 
  LastRule : Integer; 
  LastPhrase : string; 
 
begin 
  LastRule := 0; 
  LastPhrase := ''; 
  case Msg.WParam of 
    ApdSapiAskOperator    : 
      LastReply := ptOperator; 
    ApdSapiAskHangUp      : 
      LastReply := ptHangup; 
    ApdSapiAskBack        : 
      LastReply := ptBack; 
    ApdSapiAskWhere       : 
      LastReply := ptWhere; 
    ApdSapiAskHelp        : 
      LastReply := ptHelp; 
    ApdSapiAskRepeat      : 
      LastReply := ptRepeat; 
    ApdSapiAskSpeakFaster : 
      LastReply := ptSpeakFaster; 
    ApdSapiAskSpeakSlower : 
      LastReply := ptSpeakSlower; 
    ApdSapiAbort         : 
      LastReply := ptAbort; 
    else 
      begin 
        LastReply := ptCustom; 
        LastRule := Msg.WParam; 
        LastPhrase := GetPhraseData (Msg.LParam); 
      end; 
  end; 
  UpdateStateMachine (LastReply, LastRule, LastPhrase); 
end; 
 
procedure TApdCustomSapiPhone.ExitAskFor (Reply : TApdSapiPhoneReply; 
                                          Data : Pointer; 
                                          SpokenData : string); 
var 
  FWorkTrigger : TApdAskForEventTrigger; 
   
begin 
  if not Assigned (FSapiEngine) then begin 
    FInAskFor := False; 
    FWorkTrigger := FEventTrigger; 
    FCustomDataHandler := nil; 
    FEventTrigger      := nil; 
    Exit; 
  end; 
  FSapiEngine.Listening := False; 
  FSapiEngine.Speaking := False; 
  FSapiEngine.DeRegisterPhraseFinishHook (FInfo.SapiPhraseFinishHook); 
  FInAskFor := False; 
  FWorkTrigger := FEventTrigger; 
  FCustomDataHandler := nil; 
  FEventTrigger      := nil; 
  if Assigned (FWorkTrigger) then 
    FWorkTrigger (Reply, Data, SpokenData); 
end; 
 
procedure TApdCustomSapiPhone.UpdateStateMachine (LastReply : TApdPhraseType; 
                                                  LastRule : Integer; 
                                                  LastPhrase : string); 
begin 
  case LastReply of 
    ptAbort : 
      ExitAskFor (prAbort, nil, LastPhrase); 
    ptOperator : 
      ExitAskFor (prOperator, nil, LastPhrase); 
    ptHangup   : 
      ExitAskFor (prHangup, nil, LastPhrase); 
    ptBack     : 
      ExitAskFor (prBack, nil, LastPhrase); 
    ptCustom   : 
      if Assigned (FCustomDataHandler) then 
        FCustomDataHandler (LastReply, LastRule, LastPhrase); 
  end; 
end; 
 
procedure TApdCustomSapiPhone.SapiPhoneCallback (var Msg : TMessage); 
begin 
  case Msg.WParam of 
    ApdSapiConnect : 
      begin 
      end; 
    ApdSapiDisconnect : 
      begin 
        { Events may still be firing after components are destroyed - or while they 
          are being destroyed.  Check for this in all of the event triggers } 
        if csDestroying in ComponentState then 
          Exit; 
        if Assigned (FOnTapiDisconnect) then 
          FOnTapiDisconnect (Self); 
      end; 
  end; 
end; 
 
procedure TApdCustomSapiPhone.SetNoAnswerMax (v : Integer); 
begin 
  if v <> FNoAnswerMax then 
    FNoAnswerMax := v; 
end; 
 
procedure TApdCustomSapiPhone.SetNoAnswerTime (v : Integer); 
begin 
  if v <> FNoAnswerTime then 
    FNoAnswerTime := v; 
end; 
 
procedure TApdCustomSapiPhone.SetDefaultPrompts (NewPrompt1 : string; 
                                                 NewPrompt2 : string; 
                                                 NewHelp1 : string; 
                                                 NewHelp2 : string; 
                                                 NewWhere1 : string; 
                                                 NewWhere2 : string); 
begin 
  if NewPrompt1 <> '' then 
    FInfo.FPrompts.Main := NewPrompt1 
  else 
    FInfo.FPrompts.Main := FPrompts.Main; 
 
  if NewPrompt2 <> '' then 
    FInfo.FPrompts.Main2 := NewPrompt2 
  else 
    FInfo.FPrompts.Main2 := FPrompts.Main2; 
 
  if NewHelp1 <> '' then 
    FInfo.FPrompts.Help := NewHelp1 
  else 
    FInfo.FPrompts.Help:= FPrompts.Help; 
 
  if NewHelp2 <> '' then 
    FInfo.FPrompts.Help2 := NewHelp2 
  else 
    FInfo.FPrompts.Help2:= FPrompts.Help2; 
 
  if NewWhere1 <> '' then 
    FInfo.FPrompts.Where := NewWhere1 
  else 
    FInfo.FPrompts.Where := FPrompts.Where2; 
 
  if NewWhere2 <> '' then 
    FInfo.FPrompts.Where2 := NewWhere2 
  else 
    FInfo.FPrompts.Where2 := FPrompts.Where2; 
end; 
 
procedure TApdCustomSapiPhone.SetNumDigits (v : Integer); 
begin 
  if v <> FNumDigits then 
    FNumDigits := v; 
end; 
 
procedure TApdCustomSapiPhone.SetOptions (v : TApdSapiPhoneSettings); 
begin 
  if v <> FOptions then 
    FOptions := v; 
end; 
 
procedure TApdCustomSapiPhone.SetSpellingEchoBack (v : Boolean); 
begin 
  if v <> FSpellingEchoBack then 
    FSpellingEchoBack := v; 
end; 
 
procedure TApdCustomSapiPhone.Speak (const Text : string); 
begin 
  if Assigned (FSapiEngine) then 
    FSapiEngine.Speak (Text); 
end; 
 
 
end.