Festival-Kurztutorial

siehe auch:Festival im Emacs.

Festival auf den Linux-Rechnern

(z.B. Loeffelente im Studentenpool)

Achtung, das installierte Betriebssystem (Fedora) enthält auch eine Version von Festival, allerdings eine ohne deutsche Stimmen. Wenn man im Terminal den Befehl festival eingibt, erhält man per Default diese englische Version.

Festival starten

IMS Festival, das auch deutsche Stimmen enthät, bekommt man im Terminal mit

/mount/resources/speech/festival/Festival_1.95/festival/bin/festival

Die Rechner im Studentenpool haben teilweise Probleme mit Pulseaudio (man hört nur Knirschen statt des Sprachsignals). Das Abspielen funktioniert bei diesen Rechnern besser, wenn dem Festival-Befehl noch ein padsp vorangestellt wird, also:

padsp /mount/resources/speech/festival/Festival_1.95/festival/bin/festival

Es wird empfohlen, in die Datei .tcshrc folgende Zeile einzutragen:

alias festival "padsp /mount/resources/speech/festival/Festival_1.95/festival/bin/festival"

Alternativ kann man diesen Befehl auch im aktuellen Terminal ausfüren. Danach kann man IMS Festival einfach durch Tippen von

festival

im Terminal starten. Dadurch ist man im sog. interaktiven Modus, und festival nimmt Befehle entgegen.
Alternativ kann man auch Text über standard in direkt synthetisieren lassen, z.B.
echo "Hallo" | festival --tts

Evtl. muss die Variable LD_LIBRARY_PATH um das Verzeichnis /usr/local/lib ergänzt werden (eine der beiden folgenden Varianten)


 

Eine Beispielsitzung

Festival in der Shell starten mit: padsp /mount/resources/speech/festival/Festival_1.95/festival/bin/festival

festival>(SayText "Hallo")
#<Utterance 0x407e33e8>

Der Befehl "SayText" liefert eine "Utterance"/Auesserung zurueck,
deren festival-interne ID man in der zweiten Zeile sehen kann.

<<< Anmerkung: Je nach init-File wird als Default eine bestimmte Stimme
verwendet. Im interaktiven Modus koennen Stimmen explizit geladen
werden, z.B. mit:

(voice_german_de4)

Welche Stimmen verfuegbar sind, kann man sehen, wenn man beginnt,
(voice... einzutippen und dann mit dem Tab moegliche
Vervollstaendigungen anzeigen laesst.

Welche Stimme geladen ist, ist in der Variable current-voice gespeichert.

Deren Wert kann man sich anzeigen lassen, indem man tippt:

current-voice

Ende Anmerkung. >>>
 

Die erzeugte Utterance-Struktur anschauen:

im Text-Modus interaktiv:

Die entsprechende Aeusserung in einer Variable utt speichern:

(set! utt (SayText "Hallo"))
 

Es werden mehrere "Relationen" erzeugt. Die Relationen verbinden
Listen von Elementen miteinander. Alle zu einer Relation gehoerenden
Elemente sind in einer Liste angeordnet. In manchen Faellen sind die
Relationen keine flachen Listen, sondern hierarchisch strukturiert,
d.h., die Listen enthalten geschachtelte Listen. Manche Relationen
sind indirekt miteinander verknuepft, weil sie dieselben Elemente
enthalten.

Nachsehen, welche Relationen in einer Aeusserungsstruktur vorkommen:

festival> (utt.relationnames utt)
(Token
 Word
 Phrase
 Syllable
 Segment
 SylStructure
 IntEvent
 Intonation
 Target
 Wave)

Einzelne Relationen ansehen:

festival> (utt.relation_tree utt 'Word)
((("Hallo" ((id "_2") (name "Hallo") (pbreak "NB") (pos "ITJ")))))

Die Relation "Word" enthaelt hier also nur ein Element, naemlich das
Wort "Hallo". Dieses Element hat ausser seinem Namen noch weitere
Attribute: eine ID-Nummer, die Information, ob nach dem Wort eine
Phrasengrenze folgt (in diesem Fall nicht: NB fuer no break), und ein
part-of-speech-Tag ("ITJ" fuer Interjektion). Der Name wird zweimal
aufgefuehrt, einmal in der Liste der Attribute, und einmal als
Bezeichnung des uebergeordneten Elements (erstes Element der Liste).

festival> (utt.relation_tree utt 'Token)
((("Hallo"
   ((id "_1") (name "Hallo") (whitespace "") (prepunctuation "")))
  (("Hallo" ((id "_2") (name "Hallo") (pbreak "NB") (pos "ITJ"))))))

Die Relation "Token" enthaelt ebenfalls ein (allerdings
geschachteltes) Element, das ebenfalls den Namen "Hallo" traegt. Es
ist vermerkt, dass dem Token "Hallo" im Text kein Whitespace voranging, und
dass keine Prepunktuation (wie z.B. oeffnende Klammern, oeffnende
Quotes) vorhanden war. Unterhalb dieses Tokens haengt noch das Wort
"Hallo", das identisch ist mit dem oben genannten Element in der
Wortrelation (es traegt dieselbe ID).

Der Unterschied zwischen diesen 2 Ebenen in der Token-Relation zeigt sich z.B. bei Komposita mit Bindestrich oder bei Abkuerzungen:

festival> (set! utt (SayText "Halli-hallo"))
#<Utterance 0x407865a8>
festival> (utt.relation_tree utt 'Token)
((("Halli-hallo"
   ((id "_1")
    (name "Halli-hallo")
    (whitespace "")
    (prepunctuation "")
    (token_pos "comb_abbr")))
  (("Halli" ((id "_2") (name "Halli") (pbreak "NB") (pos "nil"))))
  (("hallo" ((id "_3") (name "hallo") (pbreak "NB") (pos "ITJ"))))))

Hier haengen am uebergeordneten Token "Halli-hallo" zwei Woerter,
"Halli" und "hallo". Aehnlich bei Abkuerzungen: dann findet man auf
der Wortebene die Woerter, zu denen die Abkuerzung expandiert werden
soll.

festival> (utt.relation_tree utt 'Token)
((("Hallo"
   ((id "_1") (name "Hallo") (whitespace "") (prepunctuation "")))
  (("Hallo" ((id "_3") (name "Hallo") (pbreak "NB") (pos "ITJ")))))
 (("usw"
   ((id "_2")
    (name "usw")
    (punc ".")
    (whitespace " ")
    (prepunctuation "")))
  (("und" ((id "_4") (name "und") (pbreak "NB") (pos "KO"))))
  (("so" ((id "_5") (name "so") (pbreak "NB") (pos "ADV"))))
  (("weiter" ((id "_6") (name "weiter") (pbreak "BB") (pos "ADJ"))))))

Ein weiteres Beispiel fuer eine hierarchische Relation ist die
SylStructure-Relation: Dort gibt es die Wortebene, darunter die
Silbenebene, und darunter die Segmentebene:
 

((("Hallo" ((id "_2") (name "Hallo") (pbreak "NB") (pos "ITJ")))
  (("ha" ((id "_4") (name "ha") (stress 0)))
   (("h"
     ((id "_5")
      (name "h")
      (dur_factor -0.00021387900051195)
      (end 0.43829074501991))))
   (("a"
     ((id "_6")
      (name "a")
      (dur_factor 0.00046763601130806)
      (end 0.558285176754)))))
  (("lo:" ((id "_7") (name "lo:") (stress 1)))
   (("l"
     ((id "_8")
      (name "l")
      (dur_factor -0.00017604799359106)
      (end 0.60260915756226))))
   (("o:"
     ((id "_9")
      (name "o:")
      (dur_factor -7.5191303039901e-05)
      (end 0.70340883731842)))))))
 

Bei der Synthese wird immer eine Reihe von Modulen durchlaufen. In
vielen Faellen wird durch ein Modul eine neue Relation erzeugt,
manchmal werden auch nur bestehende Relationen modifiziert (z.B. wird
die Silbenstruktur durch den Lexikon-Lookup aufgebaut, die
Dauerinformation kommt aber erst spaeter durch das Dauermodul dazu).

Welche Module durchlaufen werden, wird vom jeweiligen "UttType"
bestimmt. Dieser ist, wenn man SayText bzw. den TTS-Modus benutzt,
automatisch "Text". Benutzt man die Funktion SayPhones (SayPhones '(h
a l o:)), so ist der UttType "Phones". Welche UttTypes definiert sind,
und welche Module jeweils nacheinander durchlaufen werden, ist in der
Variable UttTypes gespeichert. Die Variable kann man sich im
interaktiven Modus anschauen, indem man ihren Namen ohne Klammern
eingibt:

festival> UttTypes

Man erhaelt damit eine Liste von Listen: eine pro UttType. In jeder
dieser Listen ist das erste Element der UttType, und die danach
folgende Liste gibt die Module an, die durchlaufen werden muessen.
Fuer den Text-Modus ist die Abfolge:

 (Text
  (Initialize utt)
  (Text utt)
  (Token_POS utt)
  (Token utt)
  (POS utt)
  (Phrasify utt)
  (Word utt)
  (Pauses utt)
  (Intonation utt)
  (PostLex utt)
  (Duration utt)
  (Int_Targets utt)
  (Wave_Synth utt))

Der Befehl

(set! utt1 (SayText "Hallo"))

ist aequivalent zu

(set! utt1 (Utterance Text "Hallo"))
(utt.synth utt1)
(utt.play utt1)

(In der ersten Zeile wird eine Aeusserung vom Typ "Text" in der
Variable utt1 gespeichert, diese wird dann mit utt.synth synthetisiert
und mit utt.play abgespielt.)

(utt.synth utt) ist im Modus Text wiederum aequivalent zu der im UttType Text angegebenen Abfolge von Befehlen:

(Initialize utt)
(Text utt)
(Token_POS utt)
(Token utt)
(POS utt)
(Phrasify utt)
(Word utt)
(Pauses utt)
(Intonation utt)
(PostLex utt)
(Duration utt)
(Int_Targets utt)
(Wave_Synth utt)

Die einzelnen Module in Kuerze:

Text:

Zerlegt den Text in einzelne Tokens.

Token_POS:

"Raet", zu welchem Typ das Token gehoert (z.B. Ordinalzahl, Bruch,
Abkuerzung, Datum, Uhrzeit, Jahr). Ist besonders wichtig fuer
Abkuerzungen und Zahlen, die vom Typ her ambig sind und daher
unterschiedlich ausgesprochen werden sollten - Token_POS disambiguiert
sie und versieht sie aufgrund des Kontexts mit einem eindeutigen Label
fuer den Typ.

Token:

Fuegt die Wort-Relation zur Token-Relation hinzu, d.h., Aufspaltung
von Komposita mit Bindestrichen in mehrere Woerter, Espansion von
Abkuerzungen, usw.

POS:

Der part-of-speech-Tagger, wenn vorhanden. In den obigen Beispielen
wurde keiner verwendet. Er wird z.B. eingesetzt, wenn die Stimme
(voice_german_de4_linginto) verwendet wird. Die Tags sind
anschliessend als Attribute in der Wort-Relation vorhanden.

Phrasify:

Bestimmt, an welchen Stellen Phrasengrenzen eingefuegt werden
sollen. Das Ergebnis kann man an den Attributen pbreak in der
Wortrelation sehen: B steht fuer kleine Breaks (entspricht
intermediaeren Phrasengrenzen), BB fuer grosse "big" Breaks (entspricht
Intonationsphrasengrenzen), und NB fuer no break sonst.

Word:

Lexikon-Lookup. Dadurch wird die Silbenstrukturrelation
("SylStructure") sowie die flachen Relationen Segment und Syllable
erzeugt. Lieben keine POS-Tags von einem Tagger vor, werden ausserdem die Tags aus dem Lexikon uebernommen.

Eintraege im Lexikon koennen folgendermassen abgerufen werden:

festival> (lex.lookup "Hallo")
("hallo" ITJ (((h a) 0) ((l o:) 1)))

Die Phoneme werden in Sampanotation angegeben, pro Silbe eine Liste,
die zunaechst eine Liste der Phoneme und dann 0 oder 1 fuer unbetont
oder betont enthaelt.

Neue Eintraege koennen ebenfalls hinzugefuegt oder alte ueberschrieben
werden:

(lex.add.entry '("hallo" ITJ (((h a ) 1) ((l o:) 0))))
festival> (lex.lookup "Hallo")
("hallo" ITJ (((h a) 1) ((l o:) 0)))
 

Pauses:

Einfuegen von Pausen an den durch Phrasify gekennzeichneten Stellen
(Ersichtlich an einem Stillesegment ("_") in der Segmentrelation).
 

Intonation:

Die Intonationsgenerierung (hier nur abstrakte Labels, fuer
o.g. Stimmen ToBI-Labels). Ersichtlich an der Relation "Intonation",
in der an jeder akzentuierten Silbe und an jeder Silbe, auf der ein
Grenzton realisiert wird, das entsprechende Label haengt.

festival> (utt.relation_tree utt 'Intonation)
((("ha" ((id "_4") (name "ha") (stress 1)))
  (("H*L" ((id "_12") (name "H*L"))))))
 

PostLex:

Postlexikalische Regeln, hier recht unbedeutend.
 

Duration:

Lautdauer, ersichtlich an den end-Attributen in der Segment-Relation.
 

IntTargets:

Umsetzung der durch Intonation vorgegebenen Labels in tatsaechliche
F0-Targets. Zu sehen in der Relation "Target" (ein oder mehrere
Targets auf verschiedenen Segmenten, angegeben durch F0-Wert und
Position innerhalb des Segments (?)).
 

Wave_Synth:

Die tatsaechliche Synthese. Nirgends zu sehen, bloss zu hoeren ;-)
 
 

© Antje Schweitzer, 03.04.12