2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-02-07 23:09:26 +00:00

98_Text2Speech.pm:

- Quality and Speed as new Parameter
              - Bugfixing with Google Download
              - added VoiceRSS as new Ressouce             
              - TTS_FileTemplateDir: Beginning with absolute directory now possible
              - TTS_Timeout (optional) added 


git-svn-id: https://svn.fhem.de/fhem/trunk@9162 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
tobiasfaust 2015-08-30 05:00:32 +00:00
parent 2910939e3e
commit 146567e7be
2 changed files with 225 additions and 53 deletions

View File

@ -1,5 +1,11 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- feature: 98_Text2Speech.pm:
- Quality and Speed as new Parameter
- Bugfixing with Google Download
- added VoiceRSS as new Ressouce
- TTS_FileTemplateDir: Beginning with absolute directory now possible
- TTS_Timeout (optional) added
- feature: 74_Unifi: - ReadingNames now by default (in this order): - feature: 74_Unifi: - ReadingNames now by default (in this order):
Attr 'devAlias' > controllerAlias > hostname > user_id Attr 'devAlias' > controllerAlias > hostname > user_id
- Attr 'devAlias' now can rename: - Attr 'devAlias' now can rename:

View File

@ -15,6 +15,8 @@
# ALL ALL = NOPASSWD: /usr/bin/mplayer # ALL ALL = NOPASSWD: /usr/bin/mplayer
############################################## ##############################################
# VoiceRSS: http://www.voicerss.org/api/documentation.aspx
package main; package main;
use strict; use strict;
use warnings; use warnings;
@ -43,19 +45,56 @@ my $mplayerOpts = '-nolirc -noconsolecontrols';
my $mplayerNoDebug = '-really-quiet'; my $mplayerNoDebug = '-really-quiet';
my $mplayerAudioOpts = '-ao alsa:device='; my $mplayerAudioOpts = '-ao alsa:device=';
#my $ttsAddr = 'http://translate.google.com/translate_tts?tl=de&q='; #my $ttsAddr = 'http://translate.google.com/translate_tts?tl=de&q=';
my $ttsHost = 'translate.google.com'; my %ttsHost = ("Google" => "translate.google.com",
my $ttsLang = 'tl='; "VoiceRSS" => "api.voicerss.org"
my $ttsQuery = 'q='; );
my $ttsPath = '/translate_tts?'; my %ttsLang = ("Google" => "tl=",
my %language = ("Deutsch" => "de", "VoiceRSS" => "hl="
"English-US" => "en-us", );
"Schwedisch" => "sv", my %ttsQuery = ("Google" => "q=",
"Indian-Hindi" => "hi", "VoiceRSS" => "src="
"Arabic" => "ar", );
"France" => "fr", my %ttsPath = ("Google" => "/translate_tts?",
"Spain" => "es", "VoiceRSS" => "/?"
"Italian" => "it", );
"Chinese" => "cn" my %ttsAddon = ("Google" => "client=t&prev=input",
"VoiceRSS" => ""
);
my %ttsAPIKey = ("Google" => "", # kein APIKey nötig
"VoiceRSS" => "key="
);
my %ttsUser = ("Google" => "", # kein Username nötig
"VoiceRSS" => "" # kein Username nötig
);
my %ttsSpeed = ("Google" => "",
"VoiceRSS" => "r="
);
my %ttsQuality = ("Google" => "",
"VoiceRSS" => "f="
);
my %ttsMaxChar = ("Google" => 100,
"VoiceRSS" => 300
);
my %language = ("Google" => {"Deutsch" => "de",
"English-US" => "en-us",
"Schwedisch" => "sv",
"Indian-Hindi" => "hi",
"Arabic" => "ar",
"France" => "fr",
"Spain" => "es",
"Italian" => "it",
"Chinese" => "cn"
},
"VoiceRSS" => {"Deutsch" => "de-de",
"English-US" => "en-us",
"Schwedisch" => "sv-se",
"Indian-Hindi" => "en-in", # gibts nicht
"Arabic" => "en-us", # gibts nicht
"France" => "fr-fr",
"Spain" => "es-es",
"Italian" => "it-it",
"Chinese" => "zh-cn"
}
); );
########################## ##########################
@ -70,7 +109,40 @@ sub Text2Speech_Initialize($)
$hash->{AttrFn} = "Text2Speech_Attr"; $hash->{AttrFn} = "Text2Speech_Attr";
$hash->{AttrList} = "disable:0,1". $hash->{AttrList} = "disable:0,1".
" TTS_Delemiter". " TTS_Delemiter".
" TTS_Ressource:Google,ESpeak". " TTS_Ressource:ESpeak,". join(",", sort keys %ttsHost).
" TTS_APIKey".
" TTS_User".
" TTS_Quality:".
"48khz_16bit_stereo,".
"48khz_16bit_mono,".
"48khz_8bit_stereo,".
"48khz_8bit_mono".
"44khz_16bit_stereo,".
"44khz_16bit_mono,".
"44khz_8bit_stereo,".
"44khz_8bit_mono".
"32khz_16bit_stereo,".
"32khz_16bit_mono,".
"32khz_8bit_stereo,".
"32khz_8bit_mono".
"24khz_16bit_stereo,".
"24khz_16bit_mono,".
"24khz_8bit_stereo,".
"24khz_8bit_mono".
"22khz_16bit_stereo,".
"22khz_16bit_mono,".
"22khz_8bit_stereo,".
"22khz_8bit_mono".
"16khz_16bit_stereo,".
"16khz_16bit_mono,".
"16khz_8bit_stereo,".
"16khz_8bit_mono".
"8khz_16bit_stereo,".
"8khz_16bit_mono,".
"8khz_8bit_stereo,".
"8khz_8bit_mono".
" TTS_Speed:-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10".
" TTS_TimeOut".
" TTS_CacheFileDir". " TTS_CacheFileDir".
" TTS_UseMP3Wrap:0,1". " TTS_UseMP3Wrap:0,1".
" TTS_MplayerCall". " TTS_MplayerCall".
@ -78,7 +150,7 @@ sub Text2Speech_Initialize($)
" TTS_FileMapping". " TTS_FileMapping".
" TTS_FileTemplateDir". " TTS_FileTemplateDir".
" TTS_VolumeAdjust". " TTS_VolumeAdjust".
" TTS_Language:".join(",", sort keys %language). " TTS_Language:".join(",", sort keys %{$language{"Google"}}).
" ".$readingFnAttributes; " ".$readingFnAttributes;
} }
@ -191,19 +263,27 @@ sub Text2Speech_Attr(@) {
return "File <".$file."> does not exists in CacheFileDir" if(! -e $file); return "File <".$file."> does not exists in CacheFileDir" if(! -e $file);
} elsif ($a[2] eq "TTS_FileTemplateDir") { } elsif ($a[2] eq "TTS_FileTemplateDir") {
unless(-e ($TTS_CacheFileDir ."/". $value) or mkdir ($TTS_CacheFileDir ."/". $value)) { # Verzeichnis beginnt mit /, dann absoluter Pfad, sonst Unterpfad von $TTS_CacheFileDir
my $newDir;
if($value =~ m/^\/.*/) { $newDir = $value; } else { $newDir = $TTS_CacheFileDir ."/". $value;}
unless(-e ($newDir) or mkdir ($newDir)) {
#Verzeichnis anlegen gescheitert #Verzeichnis anlegen gescheitert
return "Could not create directory: <$value>"; return "Could not create directory: <$value>";
} }
} elsif ($a[2] eq "TTS_TimeOut") {
return "Only Numbers allowed" if ($value !~ m/[0-9]+/);
} elsif ($a[2] eq "TTS_FileMapping") { } elsif ($a[2] eq "TTS_FileMapping") {
#Bsp: silence:silence.mp3 pling:mypling,mp3 #Bsp: silence:silence.mp3 pling:mypling,mp3
#ueberpruefen, ob mp3 Template existiert #ueberpruefen, ob mp3 Template existiert
my @FileTpl = split(" ", $TTS_FileMapping); my @FileTpl = split(" ", $TTS_FileMapping);
my $newDir;
for(my $j=0; $j<(@FileTpl); $j++) { for(my $j=0; $j<(@FileTpl); $j++) {
my @FileTplPc = split(/:/, $FileTpl[$j]); my @FileTplPc = split(/:/, $FileTpl[$j]);
return "file does not exist: <".$TTS_CacheFileDir ."/". $TTS_FileTemplateDir ."/". $FileTplPc[1] .">" if($TTS_FileTemplateDir =~ m/^\/.*/) { $newDir = $TTS_FileTemplateDir; } else { $newDir = $TTS_CacheFileDir ."/". $TTS_FileTemplateDir;}
unless (-e $TTS_CacheFileDir ."/". $TTS_FileTemplateDir ."/". $FileTplPc[1]); return "file does not exist: <".$newDir ."/". $FileTplPc[1] .">"
unless (-e $newDir ."/". $FileTplPc[1]);
} }
} }
@ -305,9 +385,17 @@ sub Text2Speech_Set($@)
{ {
my ($hash, @a) = @_; my ($hash, @a) = @_;
my $me = $hash->{NAME}; my $me = $hash->{NAME};
my $TTS_APIKey = AttrVal($hash->{NAME}, "TTS_APIKey", undef);
my $TTS_User = AttrVal($hash->{NAME}, "TTS_User", undef);
my $TTS_Ressource = AttrVal($hash->{NAME}, "TTS_Ressource", "Google");
my $TTS_TimeOut = AttrVal($hash->{NAME}, "TTS_TimeOut", 60);
return "no set argument specified" if(int(@a) < 2); return "no set argument specified" if(int(@a) < 2);
return "No APIKey specified" if (length($ttsAPIKey{$TTS_Ressource})>0 && !defined($TTS_APIKey));
return "No Username for TTS Access specified" if (length($ttsUser{$TTS_Ressource})>0 && !defined($TTS_User));
my $cmd = shift(@a); # Dummy my $cmd = shift(@a); # Dummy
$cmd = shift(@a); # DevName $cmd = shift(@a); # DevName
@ -326,7 +414,7 @@ sub Text2Speech_Set($@)
if($cmd eq "tts") { if($cmd eq "tts") {
if($hash->{MODE} eq "DIRECT") { if($hash->{MODE} eq "DIRECT") {
Text2Speech_PrepareSpeech($hash, join(" ", @a)); Text2Speech_PrepareSpeech($hash, join(" ", @a));
$hash->{helper}{RUNNING_PID} = BlockingCall("Text2Speech_DoIt", $hash, "Text2Speech_Done", 60, "Text2Speech_AbortFn", $hash) unless(exists($hash->{helper}{RUNNING_PID})); $hash->{helper}{RUNNING_PID} = BlockingCall("Text2Speech_DoIt", $hash, "Text2Speech_Done", $TTS_TimeOut, "Text2Speech_AbortFn", $hash) unless(exists($hash->{helper}{RUNNING_PID}));
} elsif ($hash->{MODE} eq "REMOTE") { } elsif ($hash->{MODE} eq "REMOTE") {
Text2Speech_Write($hash, "tts " . join(" ", @a)); Text2Speech_Write($hash, "tts " . join(" ", @a));
} else {return undef;} } else {return undef;}
@ -382,17 +470,19 @@ sub Text2Speech_PrepareSpeech($$) {
$TTS_AddDelemiter = ""; $TTS_AddDelemiter = "";
} }
if($TTS_Ressource eq "Google") { if($TTS_Ressource ne "ESpeak") {
my @text; my @text;
# ersetze Sonderzeichen die Google nicht auflösen kann # ersetze Sonderzeichen die Google nicht auflösen kann
$t =~ s/ä/ae/g; if($TTS_Ressource eq "Google") {
$t =~ s/ö/oe/g; $t =~ s/ä/ae/g;
$t =~ s/ü/ue/g; $t =~ s/ö/oe/g;
$t =~ s/Ä/Ae/g; $t =~ s/ü/ue/g;
$t =~ s/Ö/Oe/g; $t =~ s/Ä/Ae/g;
$t =~ s/Ü/Ue/g; $t =~ s/Ö/Oe/g;
$t =~ s/ß/ss/g; $t =~ s/Ü/Ue/g;
$t =~ s/ß/ss/g;
}
@text = $hash->{helper}{Text2Speech} if($hash->{helper}{Text2Speech}[0]); #vorhandene Queue, neuen Sprachbaustein hinten anfuegen @text = $hash->{helper}{Text2Speech} if($hash->{helper}{Text2Speech}[0]); #vorhandene Queue, neuen Sprachbaustein hinten anfuegen
push(@text, $t); push(@text, $t);
@ -424,11 +514,11 @@ sub Text2Speech_PrepareSpeech($$) {
@text = Text2Speech_SplitString(\@text, 0, $cutter, 1, ""); @text = Text2Speech_SplitString(\@text, 0, $cutter, 1, "");
} }
@text = Text2Speech_SplitString(\@text, 100, $TTS_Delemiter, $TTS_ForceSplit, $TTS_AddDelemiter); @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, $TTS_Delemiter, $TTS_ForceSplit, $TTS_AddDelemiter);
@text = Text2Speech_SplitString(\@text, 100, "(?<=[\\.!?])\\s*", 0, ""); @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, "(?<=[\\.!?])\\s*", 0, "");
@text = Text2Speech_SplitString(\@text, 100, ",", 0, "al"); @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, ",", 0, "al");
@text = Text2Speech_SplitString(\@text, 100, ";", 0, "al"); @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, ";", 0, "al");
@text = Text2Speech_SplitString(\@text, 100, "und", 0, "af"); @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, "und", 0, "af");
Log3 $hash, 4, "$me: Auflistung der Textbausteine nach Aufbereitung:"; Log3 $hash, 4, "$me: Auflistung der Textbausteine nach Aufbereitung:";
for(my $i=0; $i<(@text); $i++) { for(my $i=0; $i<(@text); $i++) {
@ -471,7 +561,7 @@ sub Text2Speech_SplitString(@$$$$){
my @newText; my @newText;
for(my $i=0; $i<(@text); $i++) { for(my $i=0; $i<(@text); $i++) {
if((length($text[$i]) <= 100) && (!$ForceSplit)) { #Google kann nur 100zeichen if((length($text[$i]) <= $MaxChar) && (!$ForceSplit)) { #Google kann nur 100zeichen
push(@newText, $text[$i]); push(@newText, $text[$i]);
next; next;
} }
@ -525,7 +615,7 @@ sub Text2Speech_BuildMplayerCmdString($$) {
sub Text2Speech_readingsSingleUpdateByName($$$) { sub Text2Speech_readingsSingleUpdateByName($$$) {
my ($devName, $readingName, $readingVal) = @_; my ($devName, $readingName, $readingVal) = @_;
my $hash = $defs{$devName}; my $hash = $defs{$devName};
Log3 $hash, 4, "Text2Speech_readingsSingleUpdateByName: Dev:$devName Reading:$readingName Val:$readingVal"; #Log3 $hash, 4, "Text2Speech_readingsSingleUpdateByName: Dev:$devName Reading:$readingName Val:$readingVal";
readingsSingleUpdate($defs{$devName}, $readingName, $readingVal, 1); readingsSingleUpdate($defs{$devName}, $readingName, $readingVal, 1);
} }
@ -541,7 +631,7 @@ sub Text2Speech_CalcMP3Duration($$) {
eval { eval {
use MP3::Info; use MP3::Info;
my $tag = get_mp3info($file); my $tag = get_mp3info($file);
if ($tag) { if ($tag && defined($tag->{SECS})) {
$time = int($tag->{SECS}+0.5); $time = int($tag->{SECS}+0.5);
Log3 $hash, 4, "Text2Speech_CalcMP3Duration: $file hat eine Länge von $time Sekunden."; Log3 $hash, 4, "Text2Speech_CalcMP3Duration: $file hat eine Länge von $time Sekunden.";
} }
@ -565,12 +655,46 @@ sub Text2Speech_CalcMP3Duration($$) {
sub Text2Speech_Download($$$) { sub Text2Speech_Download($$$) {
my ($hash, $file, $text) = @_; my ($hash, $file, $text) = @_;
my $HttpResponse; my $TTS_Ressource = AttrVal($hash->{NAME}, "TTS_Ressource", "Google");
my $fh; my $TTS_User = AttrVal($hash->{NAME}, "TTS_User", "");
my $TTS_Language = AttrVal($hash->{NAME}, "TTS_Language", "Deutsch"); my $TTS_APIKey = AttrVal($hash->{NAME}, "TTS_APIKey", "");
my $TTS_Language = AttrVal($hash->{NAME}, "TTS_Language", "Deutsch");
my $TTS_Quality = AttrVal($hash->{NAME}, "TTS_Quality", "");
my $TTS_Speed = AttrVal($hash->{NAME}, "TTS_Speed", "");
Log3 $hash->{NAME}, 4, "Text2Speech: Hole URL: ". "http://" . $ttsHost . $ttsPath . $ttsLang . $language{$TTS_Language} . "&client=t&prev=input&" . $ttsQuery . uri_escape($text);
$HttpResponse = GetHttpFile($ttsHost, $ttsPath . $ttsLang . $language{$TTS_Language} . "&client=t&prev=input&" . $ttsQuery . uri_escape($text)); my $HttpResponse;
my $HttpResponseErr;
my $fh;
my $url = "http://" . $ttsHost{$TTS_Ressource} . $ttsPath{$TTS_Ressource};
$url .= $ttsLang{$TTS_Ressource};
$url .= $language{$TTS_Ressource}{$TTS_Language};
$url .= "&" . $ttsAddon{$TTS_Ressource} if(length($ttsAddon{$TTS_Ressource})>0);
$url .= "&" . $ttsUser{$TTS_Ressource} . $TTS_User if(length($ttsUser{$TTS_Ressource})>0);
$url .= "&" . $ttsAPIKey{$TTS_Ressource} . $TTS_APIKey if(length($ttsAPIKey{$TTS_Ressource})>0);
$url .= "&" . $ttsQuality{$TTS_Ressource} . $TTS_Quality if(length($ttsQuality{$TTS_Ressource})>0);
$url .= "&" . $ttsSpeed{$TTS_Ressource} . $TTS_Speed if(length($ttsSpeed{$TTS_Ressource})>0);
$url .= "&" . $ttsQuery{$TTS_Ressource} . uri_escape($text);
Log3 $hash->{NAME}, 4, "Text2Speech: Verwende ".$TTS_Ressource." OnlineResource zum Download";
Log3 $hash->{NAME}, 4, "Text2Speech: Hole URL: ". $url;
#$HttpResponse = GetHttpFile($ttsHost, $ttsPath . $ttsLang . $language{$TTS_Ressource}{$TTS_Language} . "&" . $ttsQuery . uri_escape($text));
my $param = {
url => $url,
timeout => 5,
hash => $hash, # Muss gesetzt werden, damit die Callback funktion wieder $hash hat
method => "GET" # Lesen von Inhalten
#httpversion => "1.1",
#header => "User-Agent:Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22m" # Den Header gemäss abzufragender Daten ändern
#header => "agent: Mozilla/1.22\r\nUser-Agent: Mozilla/1.22"
};
($HttpResponseErr, $HttpResponse) = HttpUtils_BlockingGet($param);
if(length($HttpResponseErr) > 0) {
Log3 $hash->{NAME}, 3, "Text2Speech: Fehler beim abrufen der Daten von " .$TTS_Ressource. " Translator";
Log3 $hash->{NAME}, 3, "Text2Speech: " . $HttpResponseErr;
}
$fh = new IO::File ">$file"; $fh = new IO::File ">$file";
if(!defined($fh)) { if(!defined($fh)) {
@ -590,12 +714,18 @@ sub Text2Speech_DoIt($) {
my $TTS_CacheFileDir = AttrVal($hash->{NAME}, "TTS_CacheFileDir", "cache"); my $TTS_CacheFileDir = AttrVal($hash->{NAME}, "TTS_CacheFileDir", "cache");
my $TTS_Ressource = AttrVal($hash->{NAME}, "TTS_Ressource", "Google"); my $TTS_Ressource = AttrVal($hash->{NAME}, "TTS_Ressource", "Google");
my $TTS_Language = AttrVal($hash->{NAME}, "TTS_Language", "Deutsch"); my $TTS_Language = AttrVal($hash->{NAME}, "TTS_Language", "Deutsch");
my $TTS_SentenceAppendix = AttrVal($hash->{NAME}, "TTS_SentenceAppendix", undef); #muss eine mp3-Datei sein, ohne Pfadangabe
my $TTS_FileTemplateDir = AttrVal($hash->{NAME}, "TTS_FileTemplateDir", "templates");
my $myFileTemplateDir;
if($TTS_FileTemplateDir =~ m/^\/.*/) { $myFileTemplateDir = $TTS_FileTemplateDir; } else { $myFileTemplateDir = $TTS_CacheFileDir ."/". $TTS_FileTemplateDir;}
my $verbose = AttrVal($hash->{NAME}, "verbose", 3); my $verbose = AttrVal($hash->{NAME}, "verbose", 3);
my $cmd; my $cmd;
Log3 $hash->{NAME}, 4, "Verwende TTS Spracheinstellung: ".$TTS_Language; Log3 $hash->{NAME}, 4, "Verwende TTS Spracheinstellung: ".$TTS_Language;
if($TTS_Ressource eq "Google") { if($TTS_Ressource =~ m/(Google|VoiceRSS)/) {
my $filename; my $filename;
my $file; my $file;
@ -612,15 +742,13 @@ sub Text2Speech_DoIt($) {
# zusammenzuführen. Ziel: sauberer Sprachfluss # zusammenzuführen. Ziel: sauberer Sprachfluss
my @Mp3WrapFiles; my @Mp3WrapFiles;
my @Mp3WrapText; my @Mp3WrapText;
my $TTS_SentenceAppendix = AttrVal($hash->{NAME}, "TTS_SentenceAppendix", undef); #muss eine mp3-Datei sein, ohne Pfadangabe
my $TTS_FileTemplateDir = AttrVal($hash->{NAME}, "TTS_FileTemplateDir", "templates");
$TTS_SentenceAppendix = $TTS_CacheFileDir ."/". $TTS_FileTemplateDir ."/". $TTS_SentenceAppendix if($TTS_SentenceAppendix); $TTS_SentenceAppendix = $myFileTemplateDir ."/". $TTS_SentenceAppendix if($TTS_SentenceAppendix);
undef($TTS_SentenceAppendix) if($TTS_SentenceAppendix && (! -e $TTS_SentenceAppendix)); undef($TTS_SentenceAppendix) if($TTS_SentenceAppendix && (! -e $TTS_SentenceAppendix));
#Abspielliste erstellen #Abspielliste erstellen
foreach my $t (@{$hash->{helper}{Text2Speech}}) { foreach my $t (@{$hash->{helper}{Text2Speech}}) {
if(-e $TTS_CacheFileDir."/".$t) { $filename = $t;} else {$filename = md5_hex($language{$TTS_Language} ."|". $t) . ".mp3";} # falls eine bestimmte mp3-Datei gespielt werden soll if(-e $TTS_CacheFileDir."/".$t) { $filename = $t;} else {$filename = md5_hex($language{$TTS_Ressource}{$TTS_Language} ."|". $t) . ".mp3";} # falls eine bestimmte mp3-Datei gespielt werden soll
$file = $TTS_CacheFileDir."/".$filename; $file = $TTS_CacheFileDir."/".$filename;
if(-e $file) { if(-e $file) {
push(@Mp3WrapFiles, $file); push(@Mp3WrapFiles, $file);
@ -661,16 +789,21 @@ sub Text2Speech_DoIt($) {
Log3 $hash->{NAME}, 4, "Text2Speech: Bearbeite jetzt den Text: ". $hash->{helper}{Text2Speech}[0]; Log3 $hash->{NAME}, 4, "Text2Speech: Bearbeite jetzt den Text: ". $hash->{helper}{Text2Speech}[0];
if(-e $TTS_CacheFileDir."/".$hash->{helper}{Text2Speech}[0]) { if(-e $hash->{helper}{Text2Speech}[0]) {
# falls eine bestimmte mp3-Datei gespielt werden soll # falls eine bestimmte mp3-Datei mit absolutem Pfad gespielt werden soll
$filename = $hash->{helper}{Text2Speech}[0]; $filename = $hash->{helper}{Text2Speech}[0];
$file = $filename;
Log3 $hash->{NAME}, 4, "Text2Speech: $filename als direkte MP3 Datei erkannt!";
} elsif(-e $TTS_CacheFileDir."/".$hash->{helper}{Text2Speech}[0]) {
# falls eine bestimmte mp3-Datei mit relativem Pfad gespielt werden soll
$filename = $hash->{helper}{Text2Speech}[0];
$file = $TTS_CacheFileDir."/".$filename;
Log3 $hash->{NAME}, 4, "Text2Speech: $filename als direkte MP3 Datei erkannt!"; Log3 $hash->{NAME}, 4, "Text2Speech: $filename als direkte MP3 Datei erkannt!";
} else { } else {
$filename = md5_hex($language{$TTS_Language} ."|". $hash->{helper}{Text2Speech}[0]) . ".mp3"; $filename = md5_hex($language{$TTS_Ressource}{$TTS_Language} ."|". $hash->{helper}{Text2Speech}[0]) . ".mp3";
$file = $TTS_CacheFileDir."/".$filename;
Log3 $hash->{NAME}, 4, "Text2Speech: Textbaustein ist keine direkte MP3 Datei, ermittle MD5 CacheNamen: $filename"; Log3 $hash->{NAME}, 4, "Text2Speech: Textbaustein ist keine direkte MP3 Datei, ermittle MD5 CacheNamen: $filename";
} }
$file = $TTS_CacheFileDir."/".$filename;
if(! -e $file) { # Datei existiert noch nicht im Cache if(! -e $file) { # Datei existiert noch nicht im Cache
Text2Speech_Download($hash, $file, $hash->{helper}{Text2Speech}[0]); Text2Speech_Download($hash, $file, $hash->{helper}{Text2Speech}[0]);
@ -714,6 +847,8 @@ sub Text2Speech_Done($) {
my $tts_done = shift(@a); my $tts_done = shift(@a);
my $filename = shift(@a); my $filename = shift(@a);
my $TTS_TimeOut = AttrVal($hash->{NAME}, "TTS_TimeOut", 60);
if($filename) { if($filename) {
my @text; my @text;
for(my $i=0; $i<$tts_done; $i++) { for(my $i=0; $i<$tts_done; $i++) {
@ -727,7 +862,7 @@ sub Text2Speech_Done($) {
# erneutes aufrufen da ev. weiterer Text in der Warteschlange steht # erneutes aufrufen da ev. weiterer Text in der Warteschlange steht
if(@{$hash->{helper}{Text2Speech}} > 0) { if(@{$hash->{helper}{Text2Speech}} > 0) {
$hash->{helper}{RUNNING_PID} = BlockingCall("Text2Speech_DoIt", $hash, "Text2Speech_Done", 60, "Text2Speech_AbortFn", $hash); $hash->{helper}{RUNNING_PID} = BlockingCall("Text2Speech_DoIt", $hash, "Text2Speech_Done", $TTS_TimeOut, "Text2Speech_AbortFn", $hash);
} }
} }
@ -871,6 +1006,11 @@ sub Text2Speech_WriteStats($$$$){
Using the Google Engine. It´s nessessary to have internet access. This engine is the recommend engine Using the Google Engine. It´s nessessary to have internet access. This engine is the recommend engine
because the quality is fantastic. This engine is using by default. because the quality is fantastic. This engine is using by default.
</li> </li>
<li>VoiceRSS<br>
Using the VoiceRSS Engine. Its a free engine till 350 requests per day. If you need more, you have to pay.
It´s nessessary to have internet access. This engine is the 2nd recommend engine
because the quality is also fantastic. To use this engine you need an APIKey (see TTS_APIKey)
</li>
<li>ESpeak<br> <li>ESpeak<br>
Using the ESpeak Engine. Installation of the espeak sourcen is required.<br> Using the ESpeak Engine. Installation of the espeak sourcen is required.<br>
<code>apt-get install espeak</code> <code>apt-get install espeak</code>
@ -878,6 +1018,16 @@ sub Text2Speech_WriteStats($$$$){
</ul> </ul>
</li> </li>
<li>TTS_APIKey<br>
An APIKey its needed if you want to use VoiceRSS. You have to register at the following page:<br>
http://www.voicerss.org/registration.aspx <br>
After this, you will get your personal APIKey.
</li>
<li>TTS_User<br>
Actual without any usage. Needed in case if a TTS Engine need an username and an apikey for each request.
</li>
<li>TTS_CacheFileDir<br> <li>TTS_CacheFileDir<br>
optional: The downloaded Goole audio bricks are saved in this folder for reusing. optional: The downloaded Goole audio bricks are saved in this folder for reusing.
No automatically implemented deleting are available.<br> No automatically implemented deleting are available.<br>
@ -1038,9 +1188,15 @@ sub Text2Speech_WriteStats($$$$){
Achtung: Nur bei einem lokal definierter Text2Speech Instanz m&ouml;glich! Achtung: Nur bei einem lokal definierter Text2Speech Instanz m&ouml;glich!
<ul> <ul>
<li>Google<br> <li>Google<br>
Nutzung der GoogleSprachengine. Ein Internetzugriff ist notwendig! Aufgrund der Qualit&auml;t ist der Nutzung der Google Sprachengine. Ein Internetzugriff ist notwendig! Aufgrund der Qualit&auml;t ist der
Einsatz diese Engine zu empfehlen und der Standard. Einsatz diese Engine zu empfehlen und der Standard.
</li> </li>
<li>VoiceRSS<br>
Nutzung der VoiceRSS Sprachengine. Die Nutzung ist frei bis zu 350 Anfragen pro Tag.
Wenn mehr benötigt werden ist ein Bezahlmodell wählbar. Ein Internetzugriff ist notwendig!
Aufgrund der Qualit&auml;t ist der Einsatz diese Engine ebenfalls zu empfehlen.
Wenn diese Engine benutzt wird, ist ein APIKey notwendig (siehe TTXS_APIKey)
</li>
<li>ESpeak<br> <li>ESpeak<br>
Nutzung der ESpeak Offline Sprachengine. Die Qualit&auml; ist schlechter als die Google Engine. Nutzung der ESpeak Offline Sprachengine. Die Qualit&auml; ist schlechter als die Google Engine.
ESpeak ist vor der Nutzung zu installieren.<br> ESpeak ist vor der Nutzung zu installieren.<br>
@ -1048,6 +1204,16 @@ sub Text2Speech_WriteStats($$$$){
</li> </li>
</ul> </ul>
</li> </li>
<li>TTS_APIKey<br>
Wenn VoiceRSS genutzt wird, ist ein APIKey notwendig. Um diesen zu erhalten ist eine vorherige
Registrierung notwendig. Anschließend erhält man den APIKey <br>
http://www.voicerss.org/registration.aspx <br>
</li>
<li>TTS_User<br>
Bisher ohne Benutzung. Falls eine Sprachengine zusätzlich zum APIKey einen Usernamen im Request verlangt.
</li>
<li>TTS_CacheFileDir<br> <li>TTS_CacheFileDir<br>
Optional: Die per Google geladenen Sprachbausteine werden in diesem Verzeichnis zur Wiedeverwendung abgelegt. Optional: Die per Google geladenen Sprachbausteine werden in diesem Verzeichnis zur Wiedeverwendung abgelegt.