Skip to content

Commit

Permalink
add text-to-speech command "speak"
Browse files Browse the repository at this point in the history
  • Loading branch information
cheat-engine committed Aug 12, 2016
1 parent 7671aad commit f647125
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 31 deletions.
35 changes: 34 additions & 1 deletion Cheat Engine/LuaHandler.pas
Expand Up @@ -95,7 +95,7 @@ implementation
LuaStructureFrm, LuaInternet, SymbolListHandler, processhandlerunit, processlist,
DebuggerInterface, WindowsDebugger, VEHDebugger, KernelDebuggerInterface,
DebuggerInterfaceAPIWrapper, Globals, math, speedhack2, CETranslator, binutils,
xinput;
xinput, winsapi;

resourcestring
rsLUA_DoScriptWasNotCalledRomTheMainThread = 'LUA_DoScript was not called '
Expand Down Expand Up @@ -7078,6 +7078,37 @@ function lua_loadFontFromStream(L: PLua_state): integer; cdecl;

end;

function lua_speak(L: Plua_State): integer; cdecl;
var
pc: integer;
s: widestring;
begin
result:=0;
pc:=lua_gettop(L);

if pc>=1 then
s:=Lua_ToString(L, 1);

if pc>=2 then
begin
if lua_isboolean(l,2) then
begin
lua_pushinteger(L, speak(s, lua_toboolean(L,2)));
exit(1);
end
else
begin
lua_pushinteger(L, speak(s, lua_tointeger(L,2)));
exit(1);
end;
end
else
begin
lua_pushinteger(L, speak(s));
exit(1);
end;

end;

procedure InitializeLua;
var
Expand Down Expand Up @@ -7552,6 +7583,8 @@ procedure InitializeLua;
lua_register(LuaVM, 'loadFontFromStream', lua_loadFontFromStream);
lua_register(LuaVM, 'unloadLoadedFont', lua_unloadLoadedFont);

lua_register(LuaVM, 'speak', lua_speak);


initializeLuaCustomControl;

Expand Down
13 changes: 13 additions & 0 deletions Cheat Engine/bin/defines.lua
Expand Up @@ -365,3 +365,16 @@ MOUSEEVENTF_XUP =0x0100
MOUSEEVENTF_WHEEL =0x0800
MOUSEEVENTF_HWHEEL =0x1000
MOUSEEVENTF_ABSOLUTE =0x8000

--text to speech "Speak" params
SPF_DEFAULT = 0
SPF_ASYNC = ( 1 << 0 )
SPF_PURGEBEFORESPEAK = ( 1 << 1 )
SPF_IS_FILENAME = ( 1 << 2 )
SPF_IS_XML = ( 1 << 3 )
SPF_IS_NOT_XML = ( 1 << 4 )
SPF_PERSIST_XML = ( 1 << 5 )
SPF_NLP_SPEAK_PUNC = ( 1 << 6 )
SPF_PARSE_SAPI = ( 1 << 7 )
SPF_PARSE_SSML = ( 1 << 8 )
SPF_PARSE_AUTODETECT = 0
3 changes: 3 additions & 0 deletions Cheat Engine/bin/main.lua
Expand Up @@ -351,6 +351,9 @@ playSound(stream, waittilldone OPTIONAL): Plays the given memorystream containin
playSound(tablefile, waittilldone OPTIONAL) : Takes the memorystream from the tablefile and plays it.
There are two tablefiles predeclared inside cheat engine "Activate" and "Deactivate" . You are free to use or override them
speak(text, waittilldone OPTIONAL): Speaks a given text and waits till done. If waitTillDone is true the thread it's in will be frozen till it is done
speak(test, flags): Speaks a given text using the given flags. https://msdn.microsoft.com/en-us/library/speechplatform_speakflags.aspx
getUserRegistryEnvironmentVariable(name): string - Returns the environment variable stored in the user registry environment
setUserRegistryEnvironmentVariable(name, string) - Sets the environment variable stored in the user registry environment
broadcastEnvironmentUpdate() : Call this when you've changed the environment variables in the registry. This will cause at least the shell to update so you don't have to reboot. (It's always recommended to reboot though)
Expand Down
78 changes: 48 additions & 30 deletions Cheat Engine/winsapi.pas
Expand Up @@ -2,6 +2,30 @@

{
partial sapi.h conversion for the syntesized voice functions
https://www.w3.org/TR/speech-synthesis/
works:
<?xml version="1.0"?>
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/10/synthesis
http://www.w3.org/TR/speech-synthesis/synthesis.xsd"
xml:lang="en-US">
hello
</speak>
this wil freeze it(for a too long time)
<?xml version="1.0"?>
<!DOCTYPE speak PUBLIC "-//W3C//DTD SYNTHESIS 1.0//EN"
"http://www.w3.org/TR/speech-synthesis/synthesis.dtd">
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xml:lang="en-US">
hello
</speak>
}

{$mode objfpc}{$H+}
Expand All @@ -11,7 +35,8 @@ interface
uses
windows, Classes, SysUtils, ole2, variants, ActiveX, comobj;

procedure bla(s: widestring);
function speak(s: widestring; waittilldone: boolean=false): HRESULT; overload;
function speak(s: widestring; flags: dword): HRESULT; overload;

const
CLSID_SpVoice: TGUID = (D1:$96749377;D2:$3391;D3:$11D2;D4:($9e,$e3,$00,$c0,$4f,$79,$73,$96));
Expand Down Expand Up @@ -311,47 +336,40 @@ SPVOICESTATUS = record

implementation

var z: ISpVoice;

procedure bla(s: widestring);
var
voice: ISpVoice;
novoice: boolean=false;

function speak(s: widestring; flags: dword): HRESULT;
var
sn: ulong;
hr: HRESULT;
begin
// CLSID_SpVoice
{
for c users reading this code, and wondering why I don't call _release.
FPC will call _Release automatically when the reference count is nil
}
if not assigned(z) then
z:=ISpVoice(CreateComObject(CLSID_SpVoice));

if assigned(z) then
hr:=z.Speak(pwchar(s),SPF_PURGEBEFORESPEAK or SPF_ASYNC or SPF_PARSE_SSML, nil);
if not assigned(voice) then
begin
try
voice:=ISpVoice(CreateComObject(CLSID_SpVoice));
except
exit(-1);
end;
end;

{
works:
<?xml version="1.0"?>
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/10/synthesis
http://www.w3.org/TR/speech-synthesis/synthesis.xsd"
xml:lang="en-US">
if assigned(voice) then
result:=voice.Speak(pwchar(s), flags, nil);
end;

hello
</speak>

this wil freeze it
<?xml version="1.0"?>
<!DOCTYPE speak PUBLIC "-//W3C//DTD SYNTHESIS 1.0//EN"
"http://www.w3.org/TR/speech-synthesis/synthesis.dtd">
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xml:lang="en-US">
function speak(s: widestring; waitTillDone: boolean=false): HRESULT; overload;
var flags: dword;
begin
flags:=SPF_PURGEBEFORESPEAK;
if not waittilldone then
flags:=flags or SPF_ASYNC;

weee
</speak>
}
result:=speak(s, flags);
end;

end.
Expand Down

4 comments on commit f647125

@mgrinzPlayer
Copy link
Contributor

@mgrinzPlayer mgrinzPlayer commented on f647125 Aug 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. I made some tests. And I have small suggestion:

Index: bin/main.lua
===================================================================
--- bin/main.lua    (old)
+++ bin/main.lua    (new)
@@ -352,8 +352,10 @@
   There are two tablefiles predeclared inside cheat engine "Activate" and "Deactivate" . You are free to use or override them

 speak(text, waittilldone OPTIONAL): Speaks a given text and waits till done.  If waitTillDone is true the thread it's in will be frozen till it is done
-speak(test, flags): Speaks a given text using the given flags. https://msdn.microsoft.com/en-us/library/speechplatform_speakflags.aspx
+speak(text, flags): Speaks a given text using the given flags. https://msdn.microsoft.com/en-us/library/speechplatform_speakflags.aspx

+speakEnglish(text, waittilldone OPTIONAL) and speakEnglish(text, flags) - will try English voice, fallback to default voice on failure. Do not use SPF_IS_NOT_XML flag.
+
 getUserRegistryEnvironmentVariable(name): string - Returns the environment variable stored in the user registry environment
 setUserRegistryEnvironmentVariable(name, string) - Sets the environment variable stored in the user registry environment
 broadcastEnvironmentUpdate() : Call this when you've changed the environment variables in the registry. This will cause at least the shell to update so you don't have to reboot. (It's always recommended to reboot though)
Index: LuaHandler.pas
===================================================================
--- LuaHandler.pas  (old)
+++ LuaHandler.pas  (new)
@@ -7078,7 +7078,7 @@

 end;

-function lua_speak(L: Plua_State): integer; cdecl;
+function lua_speakEx(engLang: boolean; L: Plua_State): integer; cdecl;
 var
   pc: integer;
   s: widestring;
@@ -7089,6 +7089,8 @@
   if pc>=1 then
     s:=Lua_ToString(L, 1);

+  if engLang then s:='<speak version="1.0" xml:lang="en">'+s+'</speak>'; // should use default English voice
+
   if pc>=2 then
   begin
     if lua_isboolean(l,2) then
@@ -7110,6 +7112,16 @@

 end;

+function lua_speak(L: Plua_State): integer; cdecl;
+begin
+  lua_speakEx(false,L);
+end;
+
+function lua_speakEnglish(L: Plua_State): integer; cdecl;
+begin
+  lua_speakEx(true,L);
+end;
+
 procedure InitializeLua;
 var
   s: tstringlist;
@@ -7584,6 +7596,7 @@
     lua_register(LuaVM, 'unloadLoadedFont', lua_unloadLoadedFont);

     lua_register(LuaVM, 'speak', lua_speak);
+    lua_register(LuaVM, 'speakEnglish', lua_speakEnglish);


     initializeLuaCustomControl;

@cheat-engine
Copy link
Owner Author

@cheat-engine cheat-engine commented on f647125 Aug 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually a bit more complicated as the xml added in front will break SSML parsing. But for those that know what they do, the speak command will work
(I've added speakEnglish as it's good enough for basic stuff. Might mess up characters like > and < but I doubt anyone uses those)

@mgrinzPlayer
Copy link
Contributor

@mgrinzPlayer mgrinzPlayer commented on f647125 Aug 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could escape &, < and > when engLang is true:

  if engLang then // should use default English voice
  begin
    s:=StringReplace(s,'<','&lt;',[rfReplaceAll]);
    s:=StringReplace(s,'>','&gt;',[rfReplaceAll]);
    s:=StringReplace(s,'&','&amp;',[rfReplaceAll]);
    s:='<speak version="1.0" xml:lang="en">'+s+'</speak>';
  end;

@mgrinzPlayer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, & must be replaced first.

  if engLang then // should use default English voice
  begin
    s:=StringReplace(s,'&','&amp;',[rfReplaceAll]);
    s:=StringReplace(s,'<','&lt;',[rfReplaceAll]);
    s:=StringReplace(s,'>','&gt;',[rfReplaceAll]);
    s:='<speak version="1.0" xml:lang="en">'+s+'</speak>';
  end;

Please sign in to comment.