-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4643428
commit 9cfaf50
Showing
2 changed files
with
391 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
unit AndroidTTS; | ||
|
||
interface | ||
|
||
uses | ||
System.SysUtils, System.Classes, | ||
{$IFDEF ANDROID} | ||
AndroidAPI.JNIBridge, androidapi.JNI.TTS, | ||
Androidapi.JNI.JavaTypes, | ||
{$ENDIF} | ||
FMX.Forms; | ||
|
||
type | ||
TAndroidTTS = class(TComponent) | ||
private | ||
{$IFDEF ANDROID} | ||
ftts: JTextToSpeech; | ||
type | ||
TttsOnInitListener = class(TJavaLocal, JTextToSpeech_OnInitListener, | ||
JTextToSpeech_OnUtteranceCompletedListener) | ||
private | ||
[weak] FParent : TAndroidTTS; | ||
public | ||
constructor Create(AParent : TAndroidTTS); | ||
// JTextToSpeech_OnInitListener | ||
procedure onInit(status: Integer); cdecl; | ||
// JTextToSpeech_OnUtteranceCompletedListener | ||
procedure onUtteranceCompleted(utteranceID: JString); cdecl; | ||
end; | ||
private | ||
fttsListener : TttsOnInitListener; | ||
FDone: TNotifyEvent; | ||
procedure Init; | ||
// Never fires | ||
// property OnDone: TNotifyEvent read FDone write FDone; | ||
{$ENDIF} | ||
public | ||
constructor Create(AOwner: TComponent); override; | ||
procedure Speak(say: String); | ||
{$IFDEF ANDROID} | ||
procedure setSpeechRate(speechRate: Single); | ||
procedure setPitch(pitch: Single); | ||
{$ENDIF} | ||
end; | ||
|
||
procedure Register; | ||
|
||
implementation | ||
|
||
{$IFDEF ANDROID} | ||
|
||
uses | ||
FMX.Helpers.Android | ||
, FMX.Platform.Android | ||
, Androidapi.JNI.Os | ||
, Androidapi.JNI.GraphicsContentViewText | ||
, Androidapi.Helpers | ||
, Androidapi.JNI.App | ||
; | ||
|
||
{$ENDIF} | ||
|
||
procedure Register; | ||
begin | ||
RegisterComponents('Android', [TAndroidTTS]); | ||
end; | ||
|
||
{ TAndroidTTS } | ||
|
||
constructor TAndroidTTS.Create(AOwner: TComponent); | ||
begin | ||
inherited; | ||
{$IFDEF ANDROID} | ||
Init; | ||
{$ENDIF} | ||
end; | ||
|
||
procedure TAndroidTTS.Speak(say: String); | ||
{$IFDEF ANDROID} | ||
var | ||
params: JHashMap; | ||
begin | ||
params := nil; | ||
// This needs to be a <String,String> hashmap for the OnDone to work. | ||
{ params := TJHashMap.JavaClass.init(); | ||
params.put(TJTextToSpeech_Engine.JavaClass.KEY_PARAM_UTTERANCE_ID, | ||
StringToJString('id')); } | ||
|
||
ftts.speak(StringToJString(say), TJTextToSpeech.JavaClass.QUEUE_FLUSH, params); | ||
end; | ||
{$ELSE} | ||
begin | ||
|
||
end; | ||
{$ENDIF} | ||
|
||
{$IFDEF ANDROID} | ||
procedure TAndroidTTS.setSpeechRate(speechRate: Single); | ||
begin | ||
ftts.setSpeechRate(speechRate); | ||
end; | ||
|
||
procedure TAndroidTTS.setPitch(pitch: Single); | ||
begin | ||
ftts.setPitch(pitch); | ||
end; | ||
{$ENDIF} | ||
|
||
{$IFDEF ANDROID} | ||
procedure TAndroidTTS.Init; | ||
begin | ||
Ftts := TJTextToSpeech.JavaClass.init(SharedActivityContext, fttsListener); | ||
end; | ||
|
||
{ TAndroidTTS.TttsOnInitListener } | ||
|
||
constructor TAndroidTTS.TttsOnInitListener.Create(AParent: TAndroidTTS); | ||
begin | ||
Inherited Create; | ||
FParent := AParent; | ||
end; | ||
|
||
procedure TAndroidTTS.TttsOnInitListener.onInit(status: Integer); | ||
var | ||
Result : Integer; | ||
begin | ||
if (status = TJTextToSpeech.JavaClass.SUCCESS) then | ||
begin | ||
result := FParent.ftts.setLanguage(TJLocale.JavaClass.ENGLISH); | ||
FParent.ftts.setOnUtteranceCompletedListener(self); | ||
if (result = TJTextToSpeech.JavaClass.LANG_MISSING_DATA) or | ||
(result = TJTextToSpeech.JavaClass.LANG_NOT_SUPPORTED) then | ||
raise Exception.Create('This Language is not supported') | ||
else | ||
begin | ||
// Processing after Init | ||
end; | ||
end | ||
else | ||
raise Exception.Create('Initilization Failed!'); | ||
end; | ||
|
||
procedure TAndroidTTS.TttsOnInitListener.onUtteranceCompleted( | ||
utteranceID: JString); | ||
begin | ||
FParent.ftts.setOnUtteranceCompletedListener(self); | ||
|
||
// Currently not firing. | ||
TThread.Synchronize(nil, procedure begin | ||
if Assigned(FParent.FDone) then | ||
begin | ||
FParent.FDone(FParent); | ||
end; | ||
end); | ||
end; | ||
{$ENDIF} | ||
|
||
end. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
unit SpeechRecognition; | ||
|
||
interface | ||
|
||
uses | ||
System.SysUtils, System.Classes, System.Messaging; | ||
|
||
type | ||
TGuesses = Array of String; | ||
TRecognitionEventEx = procedure(Sender: TObject; Guesses: TGuesses) of object; | ||
TRecognitionEvent = procedure(Sender: TObject; Guess: String) of object; | ||
|
||
TSpeechRecognition = class(TComponent) | ||
private | ||
const | ||
RecognizerRequestCode = 165308207; //arbitrary value | ||
DefaultPrompt = 'Speak now'; | ||
DefaultLanguage = 'en-US'; | ||
private | ||
FListening: Boolean; | ||
FLanguage: String; | ||
FOnRecognition: TRecognitionEvent; | ||
FOnRecognitionEx: TRecognitionEventEx; | ||
FPrompt: String; | ||
FTempCommands: TStrings; | ||
FOnCommand: TRecognitionEvent; | ||
FAlwaysGuesses: Boolean; | ||
procedure IntentCallback(const Sender: TObject; const M: TMessage); | ||
procedure DoListen; | ||
procedure ProcessesGuesses(AGuesses: TGuesses); | ||
function StorePrompt: Boolean; | ||
function StoreLanguage: Boolean; | ||
public | ||
procedure Listen; overload; | ||
procedure ListenFor(const ACommands: TStrings); overload; | ||
procedure ListenFor(const ACommands: TGuesses); overload; | ||
constructor Create(AOwner: TComponent); override; | ||
destructor Destroy; override; | ||
published | ||
property Prompt: String read FPrompt write FPrompt stored StorePrompt; | ||
property AlwaysGuesses: Boolean read FAlwaysGuesses write FAlwaysGuesses default True; | ||
property Language: String read FLanguage write FLanguage stored StoreLanguage; | ||
property OnRecognition: TRecognitionEvent read FOnRecognition write FOnRecognition; | ||
property OnRecognitionEx: TRecognitionEventEx read FOnRecognitionEx write FOnRecognitionEx; | ||
property OnCommand: TRecognitionEvent read FOnCommand write FOnCommand; | ||
end; | ||
|
||
procedure Register; | ||
|
||
implementation | ||
|
||
{$IFDEF ANDROID} | ||
uses | ||
android.speech.SpeechRecognizer | ||
, FMX.Helpers.Android | ||
, FMX.Platform.Android | ||
, AndroidAPI.JNIBridge | ||
, Androidapi.JNI.Os | ||
, Androidapi.JNI.GraphicsContentViewText | ||
, Androidapi.JNI.JavaTypes | ||
, Androidapi.Helpers | ||
, Androidapi.JNI.App; | ||
{$ENDIF} | ||
|
||
procedure Register; | ||
begin | ||
RegisterComponents('Android', [TSpeechRecognition]); | ||
end; | ||
|
||
{ TSpeechRecognition } | ||
|
||
constructor TSpeechRecognition.Create(AOwner: TComponent); | ||
begin | ||
inherited; | ||
FLanguage := DefaultLanguage; | ||
FPrompt := DefaultPrompt; | ||
FTempCommands := TStringList.Create; | ||
FListening := False; | ||
FAlwaysGuesses := True; | ||
{$IFDEF ANDROID} | ||
TMessageManager.DefaultManager.SubscribeToMessage(TMessageResultNotification, IntentCallback); | ||
{$ENDIF} | ||
end; | ||
|
||
destructor TSpeechRecognition.Destroy; | ||
begin | ||
FTempCommands.Free; | ||
{$IFDEF ANDROID} | ||
TMessageManager.DefaultManager.Unsubscribe(TMessageResultNotification, IntentCallback); | ||
{$ENDIF} | ||
inherited; | ||
end; | ||
|
||
procedure TSpeechRecognition.DoListen; | ||
{$IFDEF ANDROID} | ||
var | ||
Recognizer: JIntent; | ||
{$ENDIF} | ||
begin | ||
FListening := True; | ||
{$IFDEF ANDROID} | ||
Recognizer := TJIntent.JavaClass.init(TJRecognizerIntent.JavaClass.ACTION_RECOGNIZE_SPEECH); | ||
Recognizer.putExtra(TJRecognizerIntent.JavaClass.EXTRA_LANGUAGE_MODEL, | ||
TJRecognizerIntent.JavaClass.LANGUAGE_MODEL_FREE_FORM); | ||
Recognizer.putExtra(TJRecognizerIntent.JavaClass.EXTRA_PROMPT, | ||
StringToJString(FPrompt)); | ||
Recognizer.putExtra(TJRecognizerIntent.JavaClass.EXTRA_MAX_RESULTS, 5); // default 5 | ||
Recognizer.putExtra(TJRecognizerIntent.JavaClass.EXTRA_LANGUAGE, | ||
StringToJString(FLanguage)); | ||
|
||
MainActivity.startActivityForResult(Recognizer, RecognizerRequestCode); | ||
{$ENDIF} | ||
end; | ||
|
||
function TSpeechRecognition.StoreLanguage: Boolean; | ||
begin | ||
result := FLanguage <> DefaultLanguage; | ||
end; | ||
|
||
function TSpeechRecognition.StorePrompt: Boolean; | ||
begin | ||
Result := FPrompt <> DefaultPrompt; | ||
end; | ||
|
||
{$IFDEF ANDROID} | ||
function GetTextFromRecognizer(Intent: JIntent): TGuesses; | ||
var | ||
guesses: JArrayList; | ||
guess: JObject; | ||
x: Integer; | ||
begin | ||
guesses := intent.getStringArrayListExtra(TJRecognizerIntent.JavaClass.EXTRA_RESULTS); | ||
setlength(result, guesses.size); | ||
for x := 0 to guesses.Size - 1 do | ||
begin | ||
guess := guesses.get(x); | ||
result[x] := JStringToString(guess.toString) | ||
end; | ||
end; | ||
{$ENDIF} | ||
|
||
procedure TSpeechRecognition.IntentCallback(const Sender: TObject; const M: TMessage); | ||
{$IFDEF ANDROID} | ||
var | ||
Notification: TMessageResultNotification; | ||
Guesses: TGuesses; | ||
{$ENDIF} | ||
begin | ||
FListening := False; | ||
{$IFDEF ANDROID} | ||
// onActivityResult message received, process it | ||
Notification := TMessageResultNotification(M); | ||
//Label1.Text := Format('Req: %d Rel: %d', [Notification.RequestCode, Notification.ResultCode]); | ||
if Notification.ResultCode = TJActivity.JavaClass.RESULT_OK then | ||
if Notification.RequestCode = RecognizerRequestCode then | ||
begin | ||
Guesses := GetTextFromRecognizer(Notification.Value); | ||
ProcessesGuesses(Guesses); | ||
end; | ||
{$ENDIF} | ||
end; | ||
|
||
function MatchGuessesToCommands(AGuesses: TGuesses; ACommands: TStrings): string; | ||
var | ||
c, g: Integer; | ||
begin | ||
for c := 0 to ACommands.Count - 1 do | ||
for g := 0 to Length(AGuesses) - 1 do | ||
if SameText(AGuesses[g], ACommands[c]) then | ||
exit(ACommands[c]); | ||
Result := ''; | ||
end; | ||
|
||
procedure TSpeechRecognition.ProcessesGuesses(AGuesses: TGuesses); | ||
var | ||
Guess: String; | ||
Command: String; | ||
LookingForCommand: Boolean; | ||
begin | ||
if (FTempCommands.Count > 0) and Assigned(FOnCommand) then | ||
begin | ||
LookingForCommand := True; | ||
if FTempCommands.Count > 0 then | ||
begin | ||
Command := MatchGuessesToCommands(AGuesses, FTempCommands); | ||
FOnCommand(self, Command); | ||
end; | ||
end else | ||
LookingForCommand := False; | ||
if not LookingForCommand or FAlwaysGuesses then | ||
begin | ||
if Assigned(FOnRecognition) then | ||
begin | ||
if Length(AGuesses) > 0 then | ||
Guess := AGuesses[0] | ||
else | ||
Guess := ''; | ||
FOnRecognition(Self, Guess); | ||
end; | ||
if Assigned(FOnRecognitionEx) then FOnRecognitionEx(Self, AGuesses); | ||
end; | ||
end; | ||
|
||
procedure TSpeechRecognition.Listen; | ||
begin | ||
if FListening then Exit; | ||
FTempCommands.Clear; | ||
DoListen; | ||
end; | ||
|
||
procedure TSpeechRecognition.ListenFor(const ACommands: TStrings); | ||
begin | ||
if FListening then Exit; | ||
if Assigned(ACommands) then | ||
FTempCommands.Assign(ACommands) | ||
else | ||
FTempCommands.Clear; | ||
DoListen; | ||
end; | ||
|
||
procedure TSpeechRecognition.ListenFor(const ACommands: TGuesses); | ||
var | ||
i: Integer; | ||
begin | ||
if FListening then Exit; | ||
FTempCommands.Clear; | ||
for i := 0 to Length(ACommands) - 1 do | ||
FTempCommands.Add(ACommands[i]); | ||
DoListen; | ||
end; | ||
|
||
end. |