Wie bringt man seine Ubuntubox dazu, beliebige Texte (auch dynamisch generierte) mit einer Computerstimme vorlesen zu lassen?
Kurzer Hinweis für alle, die nur eine schnelle Lösung suchen, um statische Texte in Sprache umzuwandeln: Die in diesem Artikel beschriebene Lösung habe ich inzwischen als Online-Tool OTAC implementiert. Einfach Text eingeben – zurück kommt die Audio-Datei.
Der hier beschriebene Weg ist dabei nur einer von mehreren möglichen. Damit der Computer sagt, was wir wollen, basteln wir uns ein kleines Skript mit dem Namen “robo”, das auf der Sprachsynthetisierungssoftware “festival” aufbaut. Außerdem benötigen wir eine Möglichkeit, die durch diese Software erzeugten wav-Dateien über die Konsole abzuspielen. Hier benutze ich den Befehl “play” aus dem Paket “sox”. Unter Ubuntu installieren wir die benötigten Pakete mit:
sudo apt-get install festival sox

“festival” beinhaltet den Befehl “text2wave”, der (nomen est omen!) eine Textdatei in eine wav-Datei umwandeln kann. Beispiel:
text2wave /etc/hostname -o /tmp/hostname.wav
Obiger Befehl liest die Datei /etc/hostname (den Namen des Computers, hier: “ubuntukiste”) ein, wandelt den Text nach wav um und legt die generierte Sounddatei als /tmp/hostname.wav ab. Spielen wir nun die Datei /tmp/hostname.wav mit einem beliebigen Player (hier: play) ab, sagt der Computer: “ubuntukiste”. Für den Einzelgebrauch ist dies mehr als gut genug, doch wir wollen uns den Ablauf in einem Skript namens “robo” möglichst so automatisieren, dass all diese Schritte zusammengefasst werden und “robo” auch skriptgenerierte Texte vorlesen kann:
#!/bin/sh
TXTTMP=$HOME/.tmp.txt # where to save text
WAVTMP=$HOME/.tmp.wav # where to save audio
echo $@ > $TXTTMP # save input as text
text2wave $TXTTMP -o $WAVTMP # convert to audio
echo `cat $TXTTMP` # write to console
play -q $WAVTMP # play as audio
exit # bye bye
[Update] Die jeweils aktuellste Version des Skripts kann hier heruntergeladen werden: [/Update]
Das Skript speichern wir nun als /usr/local/bin/robo ab und machen es ausführbar:
sudo chmod +x /usr/local/bin/robo
Im Prinzip ist unser kleiner Sprachroboter nun fertig und einsetzbar.
Direkte Texteingabe:
Damit “robo” z.B. “hello world” sagt starten wir ihn mit:
robo hello world
Vorhandene Textdatei einlesen:
“robo” soll uns nun die Datei /home/ubufreak/greetings vorlesen, in die wir den Satz “hello ubuntuusers, how are you?” hinterlegt haben:
robo `cat /home/ubufreak/greetings`
Dynamische Texte: Zur Erinnerung: Alles zwischen den seltsam anmutenden Anführungszeichen (`backticks`) wird als Befehlssubstitution ausgeführt, d.h. der zwischen den `backticks` stehende Befehl wird zuerst ausgeführt und dann die Ausgabe dieses Befehls an “robo” als Argument übergeben. Dies können wir uns auch für andere Befehle als “cat” zu Nutze machen. Beispiel:
robo `uname -r`
Mit obigem Befehl wird uns “robo” die aktuelle Kernelversion vorlesen. Ein weiteres Beispiel:
robo "The time ... `date +'%H:%M'`"
Mit diesem Befehl liest “robo” die aktuelle Zeit ein und sagt dann: “The time: 8:37″. Den ersten Teil nimmt “robo” dabei als direkten Text auf, die Zeit selbst über die dynamisch generierte Kommandosubstitution. (In solchen Fällen die komplette Befehlskette mit regulären Anführungszeichen einrahmen). So ein Miniskript können wir z.B. in der Datei /etc/cron.hourly ablegen und ausführbar machen, damit “robo” uns die Zeit jede Stunde ansagt.
Weierführende Infos: Damit sollten die grundlegenden Möglichkeiten von “robo” deutlich geworden sein. Grundsätzlich sollte “robo” auch unter anderen Linuxsystemen funktionieren. “festival” ist standardmäßig mit einer männlichen englischsprachigen Stimme konfiguriert. Eine deutsche Stimme gibt es noch nicht. Längere Textdateien benötigen eine gewisse Zeit zum konvertieren. Mit “robo” könnten wir z.B. auch bei sicherheitsrelevanten Ereignissen zeitnah alarmiert werden, z.B. könnten wir ein kleines Skript schreiben, dass regelmäßig die Logfiles nach bestimmten Ereignissen (z.B. Zugriffsversuch per SSH) grept und dies umgehend an uns verpetzt: “Attention! Secure Shell connection attempt from IP ***.***.***.***”.
Schöner Beitrag. Vielen Dank!
Sehr schöner Beitrag – ebenfalls ein Danke von meiner Seite.
Das schönste wäre jetzt noch, wenn jemand finden würde, woher man eine “deutsche Stimme” bekommen könnte. Für Kubuntu 7.04 habe ich das damals hin bekommen:
http://forum.ubuntuusers.de/topic/sprachausgabe-de-erfolgreich-installiert-wiki/
Keine Ahnung, ob das immer noch so oder ähnlich funktionieren würde. Verwende seit KDE4 wieder Ubuntu.
Man sollte aber darauf verzichten, allzu lange Texte vorlesen zu lassen, da die Erstellung der Sounddateien recht lange benötigt. Aber um Status-Skripte mit sinnvolleren Lautmeldungen als dem üblichen Piepsen zu versehen, reicht es allemal aus. (Sonderzeichen sollte man aber vorher entfernen)
Klasse Skript!
ich wuerde vor dem exit noch die zwei zeilen
rm $TXTTMP
rm $WAVTMP
einfuegen, damit die temporaeren Dateien auch wieder geloescht werden.
ansonsten sehr nettes skript
Ich habe eine umsetzung für deutsche Sprachausgabe mit mbrola (http://tcts.fpms.ac.be/synthesis/mbrola.html) gemacht. Dort wird zunächst ein Text in eine Phonetikdatei umgewandelt und in einem zweiten Schritt aus dieser *.pho eine *.wav gemacht.
Die Sprachausgabe wirkt recht natürlich. Auf der obigen Seite gibts irgendwo auch Klangbeispiele.
Die ganzen Temporären Dateien lege ich dabei im Verzeichnis /dev/shm/ ab. Da es sich dabei quasi um eine Ramdisk handelt vermeide ich viele Festplattenzugriffe und erhöhe die Geschwindigkeit deutlich.
Wenn jemand mein (etwas hässliches) Skript möchte, dann kann er sich gerne melden.
Jetzt noch ne (fast offtopic) Frage: Wie kann ich, wie im Artikel erwähnt, aus den Logdateien Zugriffsversuche auf ssh grepen?
Gruß Julian
Warum pipest du die Daten nicht einfach, statt temporäre Dateien zu erzeugen?
echo “hello” | text2wave | aplay
hat doch den gleichen Effekt… Ich habe aplay statt play genommen, um kein sox installieren zu müssen (Wenn man PulseAudio verwendet, kann man statt aplay auch paplay nehmen)
Ich habe gerade herausgefunden, wie man eine deutsche Sprachausgabe erhält. Die Qualität ist zwar nicht sonderlich, aber vielleicht interessiert es den ein oder anderen.
Zuerst einmal sollte espeak installiert sein:
sudo aptitude install espeak
Das Skript zur Ausgabe sieht dann folgendermaßen aus:
#!/bin/sh
TXTTMP=$HOME/.tmp.txt
echo $@ > $TXTTMP
echo `cat $TXTTMP`
espeak -v de -f $TXTTMP
exit
Vielleicht findet jemand noch eine Möglichkeit, den Umweg über die Textdatei weg zu lassen? Mir ist es noch nicht gelungen.
Die möglichen Sprachen kann man sich via
espeak –voices
ausgeben lassen.
Viel Erfolg!
Okay, hab es nun doch herausgefunden:
#!/bin/sh
echo $@ | espeak -v de
exit
So fällt der Umweg über die Textdatei weg. Allerdings weiß ich jetzt nicht, ob das ressourcensparender ist
@Plippo:
Also wenn ich ein WAV in aplay gepiped habe, dann hat er das nicht abgespielt. Er hatte dann irgendein porblem mit der Samplingrate oder so. Also musste ich den Schritt über eine WAV-Datei gehen.
Dieses Problem kann eventuell ja auch Mbrola-spezifisch sein.
Gruß Julian
@ALLE: Vielen Dank für Euer feedback
@Mathias: Längere Textdateien breche ich für gewöhnlich in kleinere Teile, zb:
robo `cat text1`
robo `cat text2`
@piccolino81 und Julian:
Zum Thema “deutsche Stimme” und espeaks: Da ich mehrere Jahre nur Englisch gesprochen habe und immer noch viel Englisch spreche, war die “deutsche Stimme” nie eine Bedingung für mich. Ich werde Eure Idee dazu jedoch bald mal ausprobieren und bin gespannt wie sich das unter espeaks und Deutsch anhört.
@knopwob & plippo
Gute Idee von knopwob, temp. dateien zu löschen, auf jeden Fall besser als meine. Noch besser natürlich der Ansatz von plippo, den ganzen Kram einfach zu pipen. Dann gibt’s auch keine temporären Dateien, die man nicht löschen braucht. Funzt gut. Werde das bald mal auf performance hin testen…
@Julian: greppen nach ssh: Ist nicht so einfach auf die Schnelle zu beantworten. Solche events sollten aber in /var/log/kern.log oder /var/log/syslog auflaufen. Der entscheidende string wäre dann: DPT=22 (Destination Port ssh). Sobald ein grep mindestens eine Zeile ausspuckt, deutet dies auf einen Verbindungsversuch auf Port 22 hin. (Sorry, sehr rudimentär, gilt aber auch für outbound!)
@ Julian und plippo:
Thema play oder aplay
aplay -q funktioniert bestens (bei mir auch durch die pipe). sox nehme ich, weil ich es eh installiert habe, es kann noch viel mehr tolle sachen.
1. echo `cat bla` ist Quatsch! cat bla reicht völlig.
2. ein exit am Ende des Script ist Quatsch! exit ist für Funktionen. Es würde am Ende der Datei nur Sinn machen, wenn man einen Exitcode != 0 erzeugen will, und auch das ist am Ende der Datei völlig unerwünscht. Höchstens in ner if([[ $fehler == true ]] && exit 1 oder so am Ende).
3. TMP-Dateien werden in /tmp erzeugt. Man kann dazu auch wunderschön mktemp -d verwenden, und in diesem Dir dann die Dateien. Dann kann man auch das löschen weglassen, da das bei nem Neustart eh entfällt.
4. “Eine deutsche Stimme gibt es noch nicht.” komisch was hör ich dann gerade?
@linopolus:
1. Der Quatsch funzt zwar auch so, aber deine ist wohl die bessere Lösung
2. Der Quatsch funzt zwar auch so, aber deine ist wohl die bessere Lösung
3. ~/.tmp/* funzt zwar auch so, aber deine ist wohl die bessere Lösung
4. Wo hast Du denn die deutsche Stimme für Festival her? In den Paketquellen konnte ich keine finden. Wäre Super, wenn Du so nett wärst, diese Info weiterzureichen…
Hab Arch Linux und daher keine Ahnung obs da n Paket in Ubuntu gibt. In Arch Linux installier ich auf jeden Fall ausm AUR mbrola-voices-de{1..8} oder so ähnlich.
@linopolusEs war mir beim Schreiben des Artikels nicht bewusst, dass mbrola und festival zusammen funktionieren. Siehe auch den Kommentar von Julian und piccolino81 weiter oben. Habs mal schnell gegoogelt und bin auf dieses Howto gestoßen…
http://www.cstr.ed.ac.uk/projects/festival/mbrola.html
Ich bin begeistert ans Forschen gegangen, und habe nun dein Robo mit mbrola zur deutschen Sprache überredet. Ich habe im Grunde mbrola nach der Anleitung: installiert. (es sind noch zwei kleine Fehler drin. Es müssen die beiden Dateien /usr/share/txt2pho/pipefilt/pipefilt und /usr/share/txt2pho/preproc/preproc ausführbar gemacht werden.
Dein Script wird nur an einer Stelle geändert:
sayit $TXTTMP -o $WAVTMP
Nun die beiden Scripte say und sayit :
#!/bin/bash (say)
ROOT=/usr/share/txt2pho # Where are the needed files?
VOICE=/usr/share/mbrola/de2/de2 # Path to the mbrola-voice
SEX=m # m/f Which sex has your voice?
if [ "$1" = "-u" ]; then
conversion=”| recode UTF-8..lat1″
file=$2
else
conversion=”"
file=$1
fi
eval “cat $file $conversion |
sed ‘s/@/ ät /g’ |
$ROOT/pipefilt/pipefilt |
$ROOT/preproc/preproc $ROOT/preproc/Rules.lst $ROOT/preproc/Hadifix.abk |
$ROOT/txt2pho -$SEX -p $ROOT/data/ |
mbrola $VOICE – -.au |
play -q -t au -”
———————————
#!/bin/bash (sayit)
#by vago
#
if [ "$*" = "" ]; then
echo “usage: sayit Hello World ”
exit 1
else
echo $* | say
fi
Ach so zur syntax:
echo “Hallo Computer. Sag was.” | say
say textdatei.txt
sayit Hallo Computer. Sag was.
Schönen Gruß Roughtrade (Ubuntuusers.de)
So, geschafft! Dank Roughtrade und seinem Hinweis auf hat es dann endlich nach etlichen Fehlermeldungen geklappt.
Wichtig war die Erstellung der /etc/txt2pho, ansonsten klappt es nämlich nicht – noch nicht mal durch die manuelle Übergabe.
Ich habe das Skript von Roughtrade ein wenig gekürzt und als say.sh gespeichert:
#/bin/bash
ROOT=/usr/share/txt2pho
VOICE=/usr/share/mbrola/de6/de6
SEX=m
echo $@ |
$ROOT/txt2pho -$SEX |
mbrola $VOICE – -.au |
play -q -t au -
Verwendung:
./say.sh Hallo Welt
cat datei | ./say.sh
Grüße
Piccolino81
Pingback: Sprachroboter unter Linux basteln « Don’t panic – Die Computerkneipe
@plippo
Zum Thema: “Warum das ganze nicht pipen?”
Wie Julian ja berichtet, funktioniert das bei ihm nicht mit aplay. Seltsamerweise habe ich genau das Gegenteil erfahren. Deine (eigentlich bessere Idee – eleganter und schöner code) funktioniert bei mir mit aplay wunderbar, genau so wie du es vorgeschlagen hast – doch bei mir funktioniert es nicht mit “play”! Das text2wave mosert dann rum, es wäre kein inputfile angegeben. Ohne Pipe und mit temporären Dateien und mit “play” hat das Skript bei mir seit 2 Jahren 100 % problemlos funktioniert. Auch wenn der Code nicht so elegant ist, halte ich es da einfach mit der leicht abgewandelten Devise: “Never change a running script”.
@ piccolino81, roughtrade, Julian,
Zum Thema “Deutsche Stimme”: Wow, da habt Ihr Euch ja mächtig ins Zeug gelegt und richtig Recherche betrieben. Das Thema scheint ja nicht so ganz einfach zu sein. Ich befürchte fast, dass nur ein kleiner Teil der User, die Früchte Eurer Arbeit ernten kann, da diese sich in einer Reihe von Kommentaren “verstecken”. Eure Mühe hätte eigentlich einen eigenen Howto-Beitrag oder gar Wiki-Eintrag verdient…
@linuxnetzer
Ich kann Dir gerne die detaillierte Anleitung für die deutsche Version via Mail schicken. Dann wäre es doch ein guter Stoff für einen neuen Blog Artikel, der dann ja auch automatisch im Planet landet, wo ich Dich ja auch gefunden habe. Selbst einen WIKI Artikel zu schreiben, traue ich mir noch nicht zu.
Sehr schöner Artikel
Die Firma dankt!
@roughtrade
ok, wie per email vereinbart bin freue ich mich auf die anleitung. Zusammen kriegen wir dann hoffentlich n schönes howto zusammen, wie das ganze auch auf deutsch funzt. Greetinx to the all fishheads from franconia!
Stay tuned…
Hallo, super Tutorial – gefällt mir echt gut dieser “Roboter”.
Hab das ganze gleich mal in meinem Blog verlinkt
Pingback: 8 Fragen an Planetenblogger « LinuxNetzer Blog
Pingback: Texte online in Sprache umwandeln