GoogleTTS - alternative with caching

For those interested in keeping use of google tts engine, which I personally prefer to all others (better voice and I like support for multiple languages):
I wrote a different version of googletts action 2 years ago, never bothered to publish it till now.
It differs from the one in the official repository, mainly in caching function and ability to invoke it each time with language given as parameter (if desired).
I added the caching to files for two reasons:

  1. Reduce load on google API - for most people tts text is not dynamic and can be saved to mp3 and repeated from cache. The less abuse we put on the API - less reason for Google to keep restricting free use of it
  2. Playing previously recorded files from cache obviously works faster than sending it to API each time

It would be probably good to merge the functionality with the one already in the repository, but I won’t get to it soon.

Some details:

  • Action bundle can be obtained here - drop it into addons folder as always
  • Configuration in openhab.cfg
    googletts:cacheEnabled=true
    googletts:defaultLang=en
    Both settings may be optional. Defaults will be used as above if not configured.
    With cache enabled, mp3 files will be saved in etc\googletts folder for re-use.
    When cache is set to false, API will be called each time.
  • Use in rules (note the difference - since I implemented it as a separate action back then, it does not use “say”, but “voice” instead):
    voice(“Front door opened”)
    voice(“Front door opened”,“en”)
    voice(“Входная дверь открыта”,“ru”)
    voice(“Realmente funciona”,“es”)

Might contain bugs, even though I was running it for 2 years without any issues (did not even notice Google changed restrictions to API again until recently, since most of my voice notifications were playing from cache already anyway).

4 Likes

I like it!

Nice, but doesn’t work for me

I see

2016-04-16 12:19:22.278 [DEBUG] [a.googletts.internal.GoogleTts] - GoogleTTS action called with text:War nur Spass
2016-04-16 12:19:22.286 [DEBUG] [a.googletts.internal.GoogleTts] - GoogleTTS encoding hash for: War nur Spass
2016-04-16 12:19:22.287 [DEBUG] [a.googletts.internal.GoogleTts] - GoogleTTS hash filename: d47fe27b2fbe79c895ee1e8b7e450336

but no audio. Meanwhile, old Google TTS binding keeps working.

Any hint where to look for ? Which ‘etc’ directory do you mean, the same one where configuration, server and webapps dirs are located, i.e. /usr/share/openhab/ for Raspian package ? . Do I need to create the googletts subdir first?

@mstormi yes, that would be the right location.
Binding would create the subdir if it does not exist, there should be a message on it in the log.
What is there in the log after those 3 lines? Please post the full log if you can.

No more logging, that was all I got. I had used

<logger name="org.openhab.io.multimedia.tts.googletts" level="TRACE" />
<logger name="org.openhab.action.googletts" level="TRACE" />

Weird, whatever logical block is executed next, first thing there would be another debug message

For me the solution whas this:
Create the folder googletts
chown openhab googletts

Cool it’s work - Thanks for publishing.

this is what I get on OH2.2 snapshot.

does it need to be recompiled?

10:49:35.180 [ERROR] [b.action.googletts.internal.GoogleTts] - Could not play the file. See exception trace for details
javazoom.jl.decoder.JavaLayerException: Cannot create AudioDevice
        at javazoom.jl.player.FactoryRegistry.createAudioDevice(FactoryRegistry.java:97) ~[?:?]
        at javazoom.jl.player.Player.<init>(Player.java:97) ~[?:?]
        at javazoom.jl.player.Player.<init>(Player.java:82) ~[?:?]
        at org.openhab.action.googletts.internal.GoogleTts.playTextAudioFromCache(GoogleTts.java:117) ~[?:?]
        at org.openhab.action.googletts.internal.GoogleTts.downloadAndPlay(GoogleTts.java:146) ~[?:?]
        at org.openhab.action.googletts.internal.GoogleTts.voice(GoogleTts.java:85) ~[?:?]
        at org.openhab.action.googletts.internal.GoogleTts.voice(GoogleTts.java:99) ~[?:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1085) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1060) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1046) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:102) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:864) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:223) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:173) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:446) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:227) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:173) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:189) ~[?:?]
        at org.eclipse.smarthome.model.script.runtime.internal.engine.ScriptImpl.execute(ScriptImpl.java:77) ~[?:?]
        at org.eclipse.smarthome.model.rule.runtime.internal.engine.RuleEngineImpl.lambda$1(RuleEngineImpl.java:309) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:?]
        at java.lang.Thread.run(Thread.java:748) [?:?]
10:49:35.387 [ERROR] [b.action.googletts.internal.GoogleTts] - Could not play the file. See exception trace for details
javazoom.jl.decoder.JavaLayerException: Cannot create AudioDevice
        at javazoom.jl.player.FactoryRegistry.createAudioDevice(FactoryRegistry.java:97) ~[?:?]
        at javazoom.jl.player.Player.<init>(Player.java:97) ~[?:?]
        at javazoom.jl.player.Player.<init>(Player.java:82) ~[?:?]
        at org.openhab.action.googletts.internal.GoogleTts.playTextAudioFromCache(GoogleTts.java:117) ~[?:?]
        at org.openhab.action.googletts.internal.GoogleTts.downloadAndPlay(GoogleTts.java:146) ~[?:?]
        at org.openhab.action.googletts.internal.GoogleTts.voice(GoogleTts.java:85) ~[?:?]
        at org.openhab.action.googletts.internal.GoogleTts.voice(GoogleTts.java:99) ~[?:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1085) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1060) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1046) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:102) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:864) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:223) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:173) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:446) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:227) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:173) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:189) ~[?:?]
        at org.eclipse.smarthome.model.script.runtime.internal.engine.ScriptImpl.execute(ScriptImpl.java:77) ~[?:?]
        at org.eclipse.smarthome.model.rule.runtime.internal.engine.RuleEngineImpl.lambda$1(RuleEngineImpl.java:309) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:?]
        at java.lang.Thread.run(Thread.java:748) [?:?]

default audio device is a Google Home BTW

haven’t tried it myself, did it ever work?