From b070c83a17cf93a3a9c2fe4852d1a17934fdc2af Mon Sep 17 00:00:00 2001 From: "Tobias.Faust" <> Date: Tue, 7 Mar 2017 11:52:27 +0000 Subject: [PATCH] 98_Text2Speech: new server mode to serve audiofile for requestors git-svn-id: https://svn.fhem.de/fhem/trunk@13633 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/98_Text2Speech.pm | 174 ++++++++++++++++++++++++------------ 2 files changed, 118 insertions(+), 57 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index a7063f00c..438f6f935 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # 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. + - feature: 98_Text2Speech: new server mode to serve audiofile for requestors - feature: at+notify: extend the FHEMWEB Wizard with a simple command modifier - feature: 42_SYSMON: support ssh login with public key - bugfix: 10_EQ3BT: fix lastChangeBy reading diff --git a/fhem/FHEM/98_Text2Speech.pm b/fhem/FHEM/98_Text2Speech.pm index 2edcee850..c9012c0b1 100644 --- a/fhem/FHEM/98_Text2Speech.pm +++ b/fhem/FHEM/98_Text2Speech.pm @@ -5,7 +5,7 @@ # 98_Text2Speech.pm # # written by Tobias Faust 2013-10-23 -# e-mail: tobias dot faust at online dot de +# e-mail: tobias dot faust at gmx dot net # ############################################## @@ -163,6 +163,7 @@ sub Text2Speech_Initialize($) " TTS_VolumeAdjust". " TTS_noStatisticsLog:1,0". " TTS_Language:".join(",", sort keys %{$language{"Google"}}). + " TTS_SpeakAsFastAsPossible:1,0". " ".$readingFnAttributes; } @@ -204,6 +205,10 @@ sub Text2Speech_Define($$) $hash->{portpassword} = $a[3] if(@a == 4); $hash->{MODE} = "REMOTE"; + } elsif (lc($dev) eq "none") { + # Ein DummyDevice, Serverdevice. Nur Generierung der mp3 TTS Dateien + $hash->{MODE} = "SERVER"; + undef $hash->{ALSADEVICE}; } else { # Ein Alsadevice ist angegeben # pruefen, ob Alsa-Device in /etc/asound.conf definiert ist @@ -254,21 +259,24 @@ sub Text2Speech_Attr(@) { return "wrong delemiter syntax: [+-]a[lfn]. \n". " Example 1: +an~\n". " Example 2: +al." if($value !~ m/^([+-]a[lfn]){0,1}(.){1}$/i); - return "This Attribute is only available in direct mode" if($hash->{MODE} ne "DIRECT"); + return "This Attribute is only available in direct or server mode" if($hash->{MODE} !~ m/(DIRECT|SERVER)/ ); } elsif ($a[2] eq "TTS_Ressource") { - return "This Attribute is only available in direct mode" if($hash->{MODE} ne "DIRECT"); + return "This Attribute is only available in direct or server mode" if($hash->{MODE} !~ m/(DIRECT|SERVER)/ ); } elsif ($a[2] eq "TTS_CacheFileDir") { - return "This Attribute is only available in direct mode" if($hash->{MODE} ne "DIRECT"); + return "This Attribute is only available in direct or server mode" if($hash->{MODE} !~ m/(DIRECT|SERVER)/ ); + } elsif ($a[2] eq "TTS_SpeakAsFastAsPossible") { + return "This Attribute is only available in direct or server mode" if($hash->{MODE} !~ m/(DIRECT|SERVER)/ ); + } elsif ($a[2] eq "TTS_UseMP3Wrap") { - return "This Attribute is only available in direct mode" if($hash->{MODE} ne "DIRECT"); + return "This Attribute is only available in direct or server mode" if($hash->{MODE} !~ m/(DIRECT|SERVER)/ ); return "Attribute TTS_UseMP3Wrap is required by Attribute TTS_SentenceAppendix! Please delete it first." if(($a[0] eq "del") && (AttrVal($hash->{NAME}, "TTS_SentenceAppendix", undef))); } elsif ($a[2] eq "TTS_SentenceAppendix") { - return "This Attribute is only available in direct mode" if($hash->{MODE} ne "DIRECT"); + return "This Attribute is only available in direct or server mode" if($hash->{MODE} !~ m/(DIRECT|SERVER)/ ); return "Attribute TTS_UseMP3Wrap is required!" unless(AttrVal($hash->{NAME}, "TTS_UseMP3Wrap", undef)); my $file = $TTS_CacheFileDir ."/". $value; @@ -444,10 +452,11 @@ sub Text2Speech_Set($@) } # Abbruch falls Disabled - return undef if(AttrVal($hash->{NAME}, "disable", "0") eq "1"); + return "no set cmd on a disabled device !" if(IsDisabled($me)); + if($cmd eq "tts") { - if($hash->{MODE} eq "DIRECT") { + if($hash->{MODE} eq "DIRECT" || $hash->{MODE} eq "SERVER") { readingsSingleUpdate($hash, "playing", "1", 1); Text2Speech_PrepareSpeech($hash, join(" ", @a)); $hash->{helper}{RUNNING_PID} = BlockingCall("Text2Speech_DoIt", $hash, "Text2Speech_Done", $TTS_TimeOut, "Text2Speech_AbortFn", $hash) unless(exists($hash->{helper}{RUNNING_PID})); @@ -789,40 +798,70 @@ sub Text2Speech_DoIt($) { return undef; } - if(AttrVal($hash->{NAME}, "TTS_UseMP3Wrap", 0)) { - # benutze das Tool MP3Wrap um bereits einzelne vorhandene Sprachdateien - # zusammenzuführen. Ziel: sauberer Sprachfluss - my @Mp3WrapFiles; - my @Mp3WrapText; - - $TTS_SentenceAppendix = $myFileTemplateDir ."/". $TTS_SentenceAppendix if($TTS_SentenceAppendix); - undef($TTS_SentenceAppendix) if($TTS_SentenceAppendix && (! -e $TTS_SentenceAppendix)); + my @Mp3WrapFiles; + my @Mp3WrapText; + + $TTS_SentenceAppendix = $myFileTemplateDir ."/". $TTS_SentenceAppendix if($TTS_SentenceAppendix); + undef($TTS_SentenceAppendix) if($TTS_SentenceAppendix && (! -e $TTS_SentenceAppendix)); - #Abspielliste erstellen - foreach my $t (@{$hash->{helper}{Text2Speech}}) { - 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 + #Abspielliste erstellen + foreach my $t (@{$hash->{helper}{Text2Speech}}) { + if(-e $t) { + # falls eine bestimmte mp3-Datei mit absolutem Pfad gespielt werden soll + $filename = $t; + $file = $filename; + Log3 $hash->{NAME}, 4, "Text2Speech: $filename als direkte MP3 Datei erkannt!"; + } elsif(-e $TTS_CacheFileDir."/".$t) { + # falls eine bestimmte mp3-Datei mit relativem Pfad gespielt werden soll + $filename = $t; $file = $TTS_CacheFileDir."/".$filename; - if(-e $file) { - push(@Mp3WrapFiles, $file); - push(@Mp3WrapText, $t); - } else {last;} + Log3 $hash->{NAME}, 4, "Text2Speech: $filename als direkte MP3 Datei erkannt!"; + } else { + $filename = md5_hex($language{$TTS_Ressource}{$TTS_Language} ."|". $t) . ".mp3"; + $file = $TTS_CacheFileDir."/".$filename; + Log3 $hash->{NAME}, 4, "Text2Speech: Textbaustein ist keine direkte MP3 Datei, ermittle MD5 CacheNamen: $filename"; + } + + if(-e $file) { + push(@Mp3WrapFiles, $file); + push(@Mp3WrapText, $t); + } else { + # es befindet sich noch Text zum Download in der Queue + if (AttrVal($hash->{NAME}, "TTS_SpeakAsFastAsPossible", 0) == 0) { + Text2Speech_Download($hash, $file, $t); + if(-e $file) { + push(@Mp3WrapFiles, $file); + push(@Mp3WrapText, $t); + } + } else { + last; + } } - push(@Mp3WrapFiles, $TTS_SentenceAppendix) if($TTS_SentenceAppendix); + last if (AttrVal($hash->{NAME}, "TTS_UseMP3Wrap", 0) == 0); + # ohne mp3wrap darf nur ein Textbaustein verarbeitet werden + } - if(scalar(@Mp3WrapFiles) >= 2) { - Log3 $hash->{NAME}, 4, "Text2Speech: Bearbeite per MP3Wrap jetzt den Text: ". join(" ", @Mp3WrapText); + push(@Mp3WrapFiles, $TTS_SentenceAppendix) if($TTS_SentenceAppendix); + + if(scalar(@Mp3WrapFiles) >= 2 && AttrVal($hash->{NAME}, "TTS_UseMP3Wrap", 0) == 1) { + # benutze das Tool MP3Wrap um bereits einzelne vorhandene Sprachdateien + # zusammenzuführen. Ziel: sauberer Sprachfluss + Log3 $hash->{NAME}, 4, "Text2Speech: Bearbeite per MP3Wrap jetzt den Text: ". join(" ", @Mp3WrapText); - my $Mp3WrapPrefix = md5_hex(join("|", @Mp3WrapFiles)); - my $Mp3WrapFile = $TTS_CacheFileDir ."/". $Mp3WrapPrefix . "_MP3WRAP.mp3"; + my $Mp3WrapPrefix = md5_hex(join("|", @Mp3WrapFiles)); + my $Mp3WrapFile = $TTS_CacheFileDir ."/". $Mp3WrapPrefix . "_MP3WRAP.mp3"; - if(! -e $Mp3WrapFile) { - $cmd = "mp3wrap " .$TTS_CacheFileDir. "/" .$Mp3WrapPrefix. ".mp3 " .join(" ", @Mp3WrapFiles); - $cmd .= " >/dev/null" if($verbose < 5); + if(! -e $Mp3WrapFile) { + $cmd = "mp3wrap " .$TTS_CacheFileDir. "/" .$Mp3WrapPrefix. ".mp3 " .join(" ", @Mp3WrapFiles); + $cmd .= " >/dev/null" if($verbose < 5); - Log3 $hash->{NAME}, 4, "Text2Speech: " .$cmd; - system($cmd); - } + Log3 $hash->{NAME}, 4, "Text2Speech: " .$cmd; + system($cmd); + } + + if ($hash->{MODE} ne "SERVER") { + # im Falls Server, nicht die Datei abspielen if(-e $Mp3WrapFile) { $cmd = Text2Speech_BuildMplayerCmdString($hash, $Mp3WrapFile); $cmd .= " >/dev/null" if($verbose < 5); @@ -832,30 +871,15 @@ sub Text2Speech_DoIt($) { } else { Log3 $hash->{NAME}, 2, "Text2Speech: Mp3Wrap Datei konnte nicht angelegt werden."; } - - return $hash->{NAME} ."|". - ($TTS_SentenceAppendix ? scalar(@Mp3WrapFiles)-1: scalar(@Mp3WrapFiles)) ."|". - $Mp3WrapFile; } + + return $hash->{NAME} ."|". + ($TTS_SentenceAppendix ? scalar(@Mp3WrapFiles)-1: scalar(@Mp3WrapFiles)) ."|". + $Mp3WrapFile; } - Log3 $hash->{NAME}, 4, "Text2Speech: Bearbeite jetzt den Text: ". $hash->{helper}{Text2Speech}[0]; - if(-e $hash->{helper}{Text2Speech}[0]) { - # falls eine bestimmte mp3-Datei mit absolutem Pfad gespielt werden soll - $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!"; - } else { - $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: Bearbeite jetzt den Text: ". $hash->{helper}{Text2Speech}[0]; if(! -e $file) { # Datei existiert noch nicht im Cache Text2Speech_Download($hash, $file, $hash->{helper}{Text2Speech}[0]); @@ -863,7 +887,9 @@ sub Text2Speech_DoIt($) { Log3 $hash->{NAME}, 4, "Text2Speech: $file gefunden, kein Download"; } - if(-e $file) { # Datei existiert jetzt + if(-e $file && $hash->{MODE} ne "SERVER") { + # Datei existiert jetzt + # im Falls Server, nicht die Datei abspielen $cmd = Text2Speech_BuildMplayerCmdString($hash, $file); $cmd .= " >/dev/null" if($verbose < 5); @@ -899,6 +925,8 @@ sub Text2Speech_Done($) { push(@text, $hash->{helper}{Text2Speech}[$i]); } Text2Speech_WriteStats($hash, 1, $filename, join(" ", @text)) if (AttrVal($hash->{NAME},"TTS_noStatisticsLog", "0")==0); + + readingsSingleUpdate($hash, "lastFilename", $filename, 1); } delete($hash->{helper}{RUNNING_PID}); @@ -1024,6 +1052,14 @@ sub Text2Speech_WriteStats($$$$){ +
define <name> Text2Speech <alsadevice>
define <name> Text2Speech <host>[:<portnr>][:SSL] [portpassword]
+ Server : define <name> Text2Speech none
Das Modul wandelt Text mittels verschiedener Provider/Ressourcen in Sprache um. Dabei kann das Device als - Remote oder Lokales Device konfiguriert werden. + Remote, Lokales Device oder als Server konfiguriert werden.