Neues Modul: Text2Speech

Begonnen von Tobias, 07 Januar 2014, 12:57:23

Vorheriges Thema - Nächstes Thema

mumpitzstuff

#825
Danke für den Tipp!!! Habe jetzt das Userreading angepasst und es funktioniert.

ur_fc0_text { my $val = (ReadingsVal("$name","fc0_text","Fehler"));; $val =~ s/[^\d]*°C/ grad celsius/;; $val =~ s/[^\d]*km\/h/ kmh/;; $val =~ s/[^\d]*%/ prozent/;;return $val;; }

Hanjo

#826
Hallo Tobias,

mir sind ein paar Probleme mit dem Parsen von Template MP3s aufgefallen. So war es u.A. nicht möglich in einer Ansage mehrere verschieden Templates zu verwenden. Außerdem fand ich die (Zwangs-)Trennung nicht ideal, da in der derzeitigen Version sehr viele sehr kurze Sprachschnippsel erstellt worden sind, obwohl eigentlich noch Platz gewesen wäre. Außerdem werden derzeit auch Wörter getrennt die "und" beinhalten, z.B. "Stunden", daraus wird dann "St" und "unden".

Ich habe mal einen Patch geschrieben, der diese Punkte adressiert. Der Patch korrigiert das Verhalten, dass nur ein Template verwendet werden kann. Außerdem wird jetzt versucht möglichst lange Sprachschnippsel zu erstellen (meine Lösung ist sicher noch nicht ideal, aber schon besser als das derzeitige). Darüberhinaus ist das Problem mit dem "und" behoben und es kann als letze Maßnahme auch an Leerzeichen getrennt werden. Mir war auch noch aufgefallen, dass nach einem Neustart von FHEM die gespeicherte Lautstärke ignoriert wird. Das wird jetzt auch abgefragt und auf den vorherigen Wert gesetzt.

Vielleicht kannst du ihn dir mal anschauen und bei Gefallen einchecken :)

Danke & Gruß
Hanjo

--- 98_Text2Speech.pm 2017-07-14 22:08:54.841860671 +0200
+++ 98_Text2Speech.pm 2017-07-15 11:53:12.464123999 +0200
@@ -455,8 +455,9 @@
   return "no set cmd on a disabled device !" if(IsDisabled($me));

   if($cmd eq "tts") {
     if($hash->{MODE} eq "DIRECT" || $hash->{MODE} eq "SERVER") {
+      $hash->{VOLUME} = ReadingsNum($me, "volume", 100);
       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}));
@@ -537,12 +538,12 @@

   # bei Angabe direkter MP3-Files wird hier ein temporäres Template vergeben
   for(my $i=0; $i<(@text); $i++) {
-    @FileTplPc = ($text[$i] =~ /:(\w+.+[mp3|ogg|wav]):/g);
+    @FileTplPc = ($text[$i] =~ /:(\w+?\.(?:mp3|ogg|wav)):/g);
     for(my $j=0; $j<(@FileTplPc); $j++) {
       my $time = time();
       $time =~ s/\.//g;
-      my $tpl = "FileTpl_".$time."_#".$i; #eindeutige Templatedefinition schaffen
-      Log3 $hash, 4, "$me: Angabe einer direkten MP3-Datei gefunden:  $FileTplPc[$i] => $tpl";
+      my $tpl = "FileTpl_".$time."_#".$j; #eindeutige Templatedefinition schaffen
+      Log3 $hash, 4, "$me: Angabe einer direkten MP3-Datei gefunden:  $FileTplPc[$j] => $tpl";
       push(@FileTpl, $tpl.":".$FileTplPc[$j]); #zb: FileTpl_123645875_#0:/ring.mp3
       $text[$i] =~ s/$FileTplPc[$j]/$tpl/g; # Ersetze die DateiDefinition gegen ein Template
     }
@@ -558,11 +559,15 @@
     @text = Text2Speech_SplitString(\@text, 0, $cutter, 1, "");
   }

+  Log3 $hash, 4, "$me: MaxChar = $ttsMaxChar{$TTS_Ressource}, Delimiter = $TTS_Delemiter, ForceSplit = $TTS_ForceSplit, AddDelimiter = $TTS_AddDelemiter";
+
   @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, $TTS_Delemiter, $TTS_ForceSplit, $TTS_AddDelemiter);
-  @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, "(?<=[\\.!?])\\s*", 0, "");
+  @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, "(?<=[.!?])\\s*", 0, "");
   @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, ",", 0, "al");
   @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, ";", 0, "al");
-  @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, "und", 0, "af");
+  @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, ":", 0, "al");
+  @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, "\\bund\\b", 0, "af");
+  @text = Text2Speech_SplitString(\@text, $ttsMaxChar{$TTS_Ressource}, " ", 0, "");

   Log3 $hash, 4, "$me: Auflistung der Textbausteine nach Aufbereitung:";
   for(my $i=0; $i<(@text); $i++) {
@@ -582,22 +587,22 @@
#####################################
# param1: array : Text 2 Speech   
# param2: string: MaxChar
-# param3: string: Delemiter
-# param4: int   : 1 -> es wird am Delemiter gesplittet
+# param3: string: Delimiter
+# param4: int   : 1 -> es wird am Delimiter gesplittet
#                 0 -> es wird nur gesplittet, wenn Stringlänge länger als MaxChar
-# param5: string: Add Delemiter to String? [al|af|<empty>] (AddLast/AddFirst)
+# param5: string: Add Delimiter to String? [al|af|<empty>] (AddLast/AddFirst)
#
# Splittet die Texte aus $hash->{helper}->{Text2Speech} anhand des
-# Delemiters, wenn die Stringlänge MaxChars übersteigt.
-# Ist "AddDelemiter" angegeben, so wird der Delemiter an den
+# Delimiters, wenn die Stringlänge MaxChars übersteigt.
+# Ist "AddDelimiter" angegeben, so wird der Delimiter an den
# String wieder angefügt
#####################################
sub Text2Speech_SplitString($$$$$){
   my @text          = @{shift()};
   my $MaxChar       = shift;
-  my $Delemiter     = shift;
+  my $Delimiter     = shift;
   my $ForceSplit    = shift;
-  my $AddDelemiter  = shift;
+  my $AddDelimiter  = shift;
   my @newText;

   for(my $i=0; $i<(@text); $i++) {
@@ -606,12 +611,35 @@
       next;
     }

-    my @b = split(/$Delemiter/, $text[$i]);
-    for(my $j=0; $j<(@b); $j++) {
-      $b[$j] = $b[$j] . $Delemiter if($AddDelemiter eq "al"); # Am Satzende wieder hinzufügen.
-      $b[$j+1] = $Delemiter . $b[$j+1] if(($AddDelemiter eq "af") && ($b[$j+1])); # Am Satzanfang des nächsten Satzes wieder hinzufügen.
-      push(@newText, $b[$j]);
-    }
+    my @b;
+ if($Delimiter =~/^ $/) {
+   @b = split(' ', $text[$i]);
+ }
+ else {
+   @b = split(/$Delimiter/, $text[$i]);
+ }
+ if((@b)>1) {
+   if(length($Delimiter)==1) {
+     for(my $k=0; $k<(@b); ) {
+          if($k+1<(@b) && length($b[$k])+length($b[$k+1]) <= $MaxChar) {
+         $b[$k] = join($Delimiter, $b[$k], $b[$k+1]);
+            splice(@b, $k+1, 1);
+       }
+   else {
+   $k++;
+   }
+     }
+   }
+      for(my $j=0; $j<(@b); $j++) {
+     (my $boundaryDelimiter = $Delimiter) =~ s/^\\b(.+)\\b$/$1/g;
+        $b[$j] = $b[$j] . $boundaryDelimiter if($AddDelimiter eq "al"); # Am Satzende wieder hinzufügen.
+        $b[$j+1] = $boundaryDelimiter . $b[$j+1] if(($AddDelimiter eq "af") && ($b[$j+1])); # Am Satzanfang des nächsten Satzes wieder hinzufügen.
+        push(@newText, $b[$j]);
+      }
+ }
+ elsif((@b)==1) {
+      push(@newText, $text[$i]);
+ }
   }
   return @newText;
}

Markus64

Hallo,

ich hab ein kleines Problem mit espeak. Beim erstmaligen ausgeben von Text funktioniert alles bestens. Beim zweiten mal ausgeben des selben Text erfolgt keine Ausgabe.
Nach ein wenig suchen habe ich dan gesehen, das die cache files korrupt sind. Anstelle mp3 sind dort die stdout von espeak zu finden.
Nach ändern der Zeile 764 der
##############################################
# $Id: 98_Text2Speech.pm 13704 2017-03-14 19:33:42Z Tobias.Faust $
#
# 98_Text2Speech.pm

  } elsif ($TTS_Ressource eq "ESpeak") {
    my $FileWav = $file . ".wav";
   
-    $cmd = "sudo espeak -vde+f3 -k5 -s150 \"" . $text . "\">\"" . $FileWav . "\"";
+    $cmd = "sudo espeak -vde+f3 -k5 -s150 \"" . $text . "\" -w \"" . $FileWav . "\"";
      Log3 $hash, 4, "Text2Speech:" .$cmd;
      system($cmd);
   


funktioniert alles wieder wie gewünscht.

Vielleicht kann man das ändern.

LG
Markus

Tobias

Hi, hab es mir gemerkt, werde ich auch einbauen, komme aber gerade nicht dazu, habe hier jetzt auch noch eine Erweiterung auf niederländische sprachausgabe liegen, die kommt dann auch mit rein.

Gesendet von meinem Leap mit Tapatalk

Maintainer: Text2Speech, TrashCal, MediaList

Meine Projekte: https://github.com/tobiasfaust
* PumpControl v2: allround Bewässerungssteuerung mit ESP und FHEM
* Ein Modbus RS485 zu MQTT Gateway für SolarWechselrichter

korreander12

Hallo zusammen,

wie in « Antwort #790 am: 09 Juni 2017, 13:21:49 » von pi-user beschrieben verlangt der HIFIBerry  44,1kHz
Ich hatte heute die gleiche Herausforderung mein TTS in Verbindung mit dem HiFiBerry Amp+ in Betrieb zu bekommen.


Ich habe für mich eine Lösung gefunden, die funktioniert.
Und zwar kann eine vorgegebene Alsa-sampling-rate eingestellt werden

Dazu in /etc/asound.conf "rate 44100" für den HIFIBerry hinzufügen.

Meine /etc/asound.conf  sieht dann so aus:





pcm.!default  {
type hw card 0
rate 44100
}
ctl.!default {
type hw card 0
}




Mit der "mplayer -af resample=44100" -Option habe ich ebenfalls experimentiert, jedoch erfolglos, da bei mir irgendwie was fehlt:
Couldn't find audio filter 'resample'
[libaf] Couldn't create or open audio filter 'resample'
Error at audio filter chain pre-init!


Jedoch wie oben beschrieben über die Vorgabe als Alsa-rate funktioniert es....


Ich hoffe das hilft dem einen oder anderen. :D :D  :D

RomanticBoy83

Hallo Leute,
ich spiele Gerade mit dem Modul herum und habe etwas für's WIKI.
Mein DbLog lief auf [attr DbLogType history]. Dies führte zum Absturz des Modules in Zeile 992. Dort sollte nicht nur auf das existierende Modul geprüft werden, sondern auch auf die Einstellung zur TABLE current. Optional wäre ein WiKi-Eintrag auch möglich.

Nun zu meinem Problem:
Ich nutze einen RPi2. Auf diesem Läuft neben Fhem auch Kodi für Multimediaanwendungen. Der defaultsound von alsa geht also auf HDMI heraus.

Frage
Ich habe es bisher nicht geschafft dem mplayer den analogen Ausgang nutzen zu lassen ohne dass ich HDMI abschalte. Mit dem Program omxplayer geht das.
omxplayer -o local foo.mp3
Über Google finden sich zahlreiche Einträge, wie man den analogen Ausgang als default setzt - was auch folgendermaßen funktionierte.
amixer cset numid=3 2
Leider möchte ich den HDMI-Ausgang so belassen und nur den mplayer anweisen den analogen Ausgang zu nutzen. Hat jemand einen Tip für mich wie ich alsa konfigurieren muss? Im alsamixer wird mir auch nur das pcm-Gerät angezeigt, was schon seltsam ist. Ein Abspielen mit mplayer auf hw:0,0 ist das selbe Ergebnis wie auf hw:0,1.

mein aplay-l
**** Liste der Hardware-Geräte (PLAYBACK) ****
Karte 0: ALSA [bcm2835 ALSA], Gerät 0: bcm2835 ALSA [bcm2835 ALSA]
  Sub-Geräte: 8/8
  Sub-Gerät #0: subdevice #0
  ...
  Sub-Gerät #7: subdevice #7
Karte 0: ALSA [bcm2835 ALSA], Gerät 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
  Sub-Geräte: 1/1
  Sub-Gerät #0: subdevice #0

Otto123

Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

RomanticBoy83

Hallo Otto,
schön dass wir uns erneut über den Weg laufen und vielen Dank dafür. Ich habe wirklich alles probiert heute und habe soeben versucht das Modul auf meine Bedürfnisse anzupassen.
Die Lösung ist verdamt einfach - VIELEN DANK für das sehr übersichtliche Modul. Es ist wirklich schön zu lesen und zu verstehen. In Zeile 644 bin ich darüber gestolpert, dass ich auch andere Programme starten kann. Diese dürfen nicht mplayer heißen und werden dann mit dem [attr TTS_MplayerCall] ohne weitere Parameter aufgerufen.
ich habe nun [attr TTS_MplayerCall /usr/bin/omxplayer] -o local gesetzt. Als Berechtigungen muste ich die Gruppe video noch setzen und habe nun eine Funktionierende Ausgabe erreicht.
Anmerkung
HDMI ist bei mir noch immer primär im alsa und auch als einziges Device dort vertreten. omxplayer nutzt kein alsa, weshalb externe Karten wahrscheinlich mit diesem Weg nicht funktionieren würden.

Otto123

Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

RomanticBoy83

Danke Otto, das hatte ich gesucht - habe ja aber jetzt doch schon hinbekommen - trotzdem danke. Ich gehe exakt den selben Weg wie du. Bei mir reichen aber die Parameter zum Ausgabegerät aus, welche ich halt im Aufruf mir eingeschrieben habe.

Nachtrag
Ich habe in Zeile 987 eine Prüfung für DbLog einbauen müssen, damit mir das Modul nicht ab-raucht.
return undef if(AttrVal($defs{$DbLogDev}, "DbLogType", "History") !~ /Current/); # muss die Tabelle Current nutzen

Schlimbo

#835
Hallo zusammen,
nutze das Modul schon eine ganze Weile und gebe hierüber überwiegend deutsche Sprachnachrichten aus.
Das Attribut "TTS_Language" ist bei mir deswegen auch auf "Deutsch" eingestellt, jetzt möchte ich aber auch manchmal englische Texte ausgeben, könnte vor so einer Nachricht jetzt zwar temporär das Attribut ändern, dies hätte aber dann immer eine Konfig Änderung zur Folge und es würde im Webinterface das speichern-Symbol auftauchen.

Würde mich freuen wenn im Modul einen Möglichkeit eingebaut werden könnte, um temporär die Sprache zu verändern. (ähnlich wie es bei AMAD möglich ist: msg684219)

Gruß Schlimbo

Simon74

#836
Mir ist aufgefallen das meine morgentliche Wetteransage beim verlassen nicht mehr ausgegeben wird, dem bin ich nun nachgegangen.
Wundere mich das es hier im Forum noch nicht zu finden war, verwendet Text2Speech niemand mehr ?
Bei meinem Stretch funktionierte der mplayer Aufruf von Text2Speech nicht mehr, was an der neueren mplayer Version liegen dürfte ?

root@px3 /opt/fhem > mplayer -V
mpv 0.23.0 (C) 2000-2016 mpv/MPlayer/mplayer2 projects
built on UNKNOWN
ffmpeg library versions:
   libavutil       55.34.101
   libavcodec      57.64.101
   libavformat     57.56.101
   libswscale      4.2.100
   libavfilter     6.65.100
   libswresample   2.3.100
ffmpeg version: 3.2.8-1~deb9u1


In der Ausgabe ist folgender Fehler zu sehen: Sub-options for --vo and --ao were removed from mpv in release 0.23.0
root@px3 /opt/fhem > mpv -ao alsa:device=hw=0.0 cache/8027551bd47f8dc12fc2c5804daf99ce.mp3
Option ao: this option does not accept sub-options.
Sub-options for --vo and --ao were removed from mpv in release 0.23.0.
See https://0x0.st/uM for details.
Error parsing option ao (option could not be parsed)
Setting command line option '--ao=alsa:device=hw=0.0' failed.


Eine Liste der Ausgabedevices erhält man nun anscheinend mit:
mpv --audio-device=help

Erfolgreicher Test:
mpv --audio-device=alsa cache/8027551bd47f8dc12fc2c5804daf99ce.mp3

Ich habe mir die 98_Text2Speech.pm wie folgt angepasst das es wieder funktioniert:
nano /opt/fhem/FHEM/98_Text2Speech.pm
my $mplayerOpts     = '';
my $mplayerNoDebug  = '-really-quiet';
my $mplayerAudioOpts = '--audio-device=';


Danach die Anpassung in FHEM:
defmod MyTTS Text2Speech alsa;
reload 98_Text2Speech.pm;


f-zappa

Moin, ich habe das gleiche Problem gehabt und ähnlich gelöst, diesen Beitrag hab ich erst hinterher gesehen.
    $cmd = "espeak -vde+f3 -k5 -s150 \"" . $text . "\" -w " . $FileWav ;
Das geht auch erheblich flotter, weil er alsa, pulse & co. links liegen lässt (und falls davon gar nichts konfiguriert ist, ist es auch egal).
Da FHEM normalerweise in sein Cacheverzeichnis schreiben darf, wird auch kein sudo mehr benötigt.

Könnte man diese kleine Änderung denn bitte einpflegen? Ich schließe ungern etwas vom Update aus, aber das wäre meine einzige Alternative, weil es in der jetzigen Form einfach nicht funktioniert ...

Gruß, Uli


Zitat von: Markus64 am 27 Juli 2017, 07:49:10
Hallo,

ich hab ein kleines Problem mit espeak. Beim erstmaligen ausgeben von Text funktioniert alles bestens. Beim zweiten mal ausgeben des selben Text erfolgt keine Ausgabe.
Nach ein wenig suchen habe ich dan gesehen, das die cache files korrupt sind. Anstelle mp3 sind dort die stdout von espeak zu finden.
Nach ändern der Zeile 764 der
##############################################
# $Id: 98_Text2Speech.pm 13704 2017-03-14 19:33:42Z Tobias.Faust $
#
# 98_Text2Speech.pm

  } elsif ($TTS_Ressource eq "ESpeak") {
    my $FileWav = $file . ".wav";
   
-    $cmd = "sudo espeak -vde+f3 -k5 -s150 \"" . $text . "\">\"" . $FileWav . "\"";
+    $cmd = "sudo espeak -vde+f3 -k5 -s150 \"" . $text . "\" -w \"" . $FileWav . "\"";
      Log3 $hash, 4, "Text2Speech:" .$cmd;
      system($cmd);
   


funktioniert alles wieder wie gewünscht.

Vielleicht kann man das ändern.

LG
Markus

simonTS

und wie wird aus der Stimme jetzt ein brummendes, weihnachtliches Ho!Ho!  :)

Das würde den KAF (bei mir reicht der WAF allein leider nicht mehr aus) Faktor erheblich nach oben dreiben...

Grüße und Schöne Feiertage!!
FHEM auf wheezy@RPI-->
KNX: MDT STV-0320.01|SCN-IP000.01|AMI-1216.01|JAL-0810.01|AKD-0401.01|AKH-0800.01|BE-GTT4W.01|SCN-P360D1.01|SCN-G360K3.01|ABB-MRS/W Magnet-Reedkontakt|Zisterne:SRF06|LED:XCSOURCE WIFI Controller|

FHEM-Wohnung

Hallo,

ich habe ein Problem. Wenn ich ":info.mp3:" eingebe, ertönt der Gong. Wenn ich jetzt jedoch ":info.mp3: Es ist XXX Uhr eingebe" eingebe, ertönt weder der Gong noch die Textausgabe. Das erstemal wird etwas von Array und dann eine Buchstaben/Zahlenkombination ausgegeben. Nach erneuter Eingabe bleibt es still. Ab dann wird nichts mehr vorgelesen bzw. abgespielt.

Nur ein Neustart lässt MyTTS zum leben erwecken.

Außerdem kann ich kein FileMapping einstellen. Es wird dann der Pfad also templates und dann die Abkürzung vorgelesen. Der Gong bleibt aus.

Vielen Dank :)