2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

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
This commit is contained in:
Tobias.Faust 2017-03-07 11:52:27 +00:00
parent 2f9adecd2d
commit b070c83a17
2 changed files with 118 additions and 57 deletions

View File

@ -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

View File

@ -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,9 +798,6 @@ 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;
@ -800,17 +806,47 @@ sub Text2Speech_DoIt($) {
#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
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;
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 {last;}
} 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;
}
}
last if (AttrVal($hash->{NAME}, "TTS_UseMP3Wrap", 0) == 0);
# ohne mp3wrap darf nur ein Textbaustein verarbeitet werden
}
push(@Mp3WrapFiles, $TTS_SentenceAppendix) if($TTS_SentenceAppendix);
if(scalar(@Mp3WrapFiles) >= 2) {
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));
@ -823,6 +859,9 @@ sub Text2Speech_DoIt($) {
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,38 +871,25 @@ 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;
}
}
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";
}
if(! -e $file) { # Datei existiert noch nicht im Cache
Text2Speech_Download($hash, $file, $hash->{helper}{Text2Speech}[0]);
} else {
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($$$$){
</ul>
</li>
<li>
<b>Server Device</b>
<ul>
In case of an usage of an Server, only the mp3 file will be generated.It makes no sence to use the attribute <i>TTS_speakAsFastAsPossible</i>.
Its recommend, to use the attribute <i>TTS_useMP3Wrap</i>. Otherwise only the last audiobrick will be shown is reading <i>lastFilename</i>.
</ul>
</li>
</ul>
</ul>
@ -1143,6 +1179,13 @@ sub Text2Speech_WriteStats($$$$){
please take care to cleanup your cachedirectory by yourself.
</li>
<li>TTS_speakAsFastAsPossible<br>
Trying to get an speach as fast as possible. In case of not present audiobricks, you can hear a short break.
The audiobrick will be download at this time. In case of an presentation of all audiobricks at local cache,
this attribute has no impact.<br>
Attribute only valid in case of an local or server instance.
</li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
<li><a href="#disable">disable</a><br>
@ -1179,9 +1222,10 @@ sub Text2Speech_WriteStats($$$$){
<ul>
<b>Local : </b><code>define &lt;name&gt; Text2Speech &lt;alsadevice&gt;</code><br>
<b>Remote: </b><code>define &lt;name&gt; Text2Speech &lt;host&gt;[:&lt;portnr&gt;][:SSL] [portpassword]</code>
<b>Server : </b><code>define &lt;name&gt; Text2Speech none</code><br>
<p>
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.
</p>
<li>
@ -1226,6 +1270,15 @@ sub Text2Speech_WriteStats($$$$){
</ul>
</li>
<li>
<b>Server Device</b>
<ul>
Im Falle der Verwendung als Server, wird nur die MP3 Datei erstellt und als Reading lastFilename dargestellt. Es macht keinen Sinn
hier das Attribut <i>TTS_speakAsFastAsPossible</i> zu verwenden. Die Verwendung des Attributes <i>TTS_useMP3Wrap</i> wird dringend empfohlen.
Ansonsten wird hier nur der letzte Teiltext als mp3 Datei im Reading dargestellt.
</ul>
</li>
</ul>
</ul>
@ -1256,7 +1309,7 @@ sub Text2Speech_WriteStats($$$$){
Hintergrund ist die Tatsache, das die Google Sprachengine nur 100Zeichen zul&auml;sst.<br>
Im Standard wird nach jedem Satzende geteilt. Ist ein einzelner Satz l&auml;nger als 100 Zeichen,
so wird zus&auml;tzlich nach Kommata, Semikolon und dem Verbindungswort <i>und</i> geteilt.<br>
Achtung: Nur bei einem lokal definierter Text2Speech Instanz m&ouml;glich und nur Nutzung der Google Sprachengine relevant!
Achtung: Nur bei einem lokal definierter Text2Speech Instanz m&ouml;glich und nur bei Nutzung der Google Sprachengine relevant!
</li>
<li>TTS_Ressource<br>
@ -1356,6 +1409,13 @@ sub Text2Speech_WriteStats($$$$){
Wenn dieses hier dektiviert wird muss sich der User selbst darum kuemmern.
</li>
<li>TTS_speakAsFastAsPossible<br>
Es wird versucht, so schnell als möglich eine Sprachausgabe zu erzielen. Bei Sprachbausteinen die nicht bereits lokal vorliegen,
ist eine kurze Pause wahrnehmbar. Dann wird der benötigte Sprachbaustein nachgeladen. Liegen alle Sprachbausteine im Cache vor,
so hat dieses Attribut keine Auswirkung.<br>
Attribut nur verfügbar bei einer lokalen oder Server Instanz
</li>
<li><a href="#readingFnAttributes">readingFnAttributes</a>
</li><br>