Dies ist der dritte Teil des Tutorials. Der Beginn findet sich hier.
Der Blick ins Lexikon hilft!
Wir wollen, dass Hedda uns einen mittelhochdeutschen Text flüssig vorliest und das in möglichst korrekter Aussprache. Um beiden Anforderungen gerecht zu werden, blieb uns nichts anderes übrig, als den gesamten Text in Lautschrift zu übersetzen und dann im Ganzen an Hedda zur Aussprache zu übergeben. Denn als wir versuchten, den Text in Einzelpassagen zu zerlegen, die wir Hedda in unterschiedlichem Format übergaben, war die Unterbrechung und der Neuansatz bei jeder Einheit deutlich und störend zu hören. Dabei könnte es doch so einfach sein: Hedda ist für die Ausgabe moderner deutscher Texte optimiert und bei diesen hat sie keine Schwierigkeiten, sie relativ unfallfrei und sogar mit so etwas wie einer angemessenen Betonung in flüssigem Vortrag vorlesen. Trifft sie dabei auf ihr unbekannte Wörter, liest sie ohne Unterbrechung so, wie es der üblichen Aussprache deutscher Buchstaben entspricht. Da sich jedoch das Mittelhochdeutsche nicht in jeder Hinsicht vom modernen Hochdeutsch unterscheidet, könnten wir häufig ihre Voreinstellungen übernehmen und müssten nur in Einzelfällen eingreifen. Wir bräuchten dafür nur eine Möglichkeit, Hedda einen Text im Ganzen zum Lesen vorzulegen, in dem Passagen in Lautschrift auf solche in Buchstabenschrift folgen und umgekehrt.
Tatsächlich ist das möglich. Als Prompt wird in der englischsprachigen Terminologie der speech.dll eine sprachliche Äußerung verstanden und mit der Klasse Promptbuilder gibt es die Möglichkeit, eine solche Äußerung aus elementaren und heterogenen Bestandteilen zusammenzusetzen. Probieren wir es gleich aus:
static void Main(string[] args)
{
SpeechSynthesizer synth = new SpeechSynthesizer();
PromptBuilder pb = new PromptBuilder();
pb.AppendText("Swêr");
pb.AppendSsmlMarkup(@"<phoneme alphabet=""ups"" ph=""S1 R EH C . T Y lng"">rehtiu</phoneme>");
pb.AppendText("wort gemerken kan,");
synth.Speak(pb);
}
Heureka! Mit dieser Technik können wir uns viel Arbeit ersparen. Eine zusätzliche Ersparnis ergibt sich durch die Verwendung eines Lexikons. In diesem vermerken wir die Sonderfälle der Aussprache für Hedda. Diese müssen wir dann nur einmal angeben und nicht bei jedem Vorkommen im Text. Wir müssen Hedda nur mitteilen, dass sie von nun an für jedes Wort im Text überprüfen soll, ob es zu diesem einen Eintrag im Lexikon der Sonderfälle gibt:
static void Main(string[] args)
{
SpeechSynthesizer synth = new SpeechSynthesizer();
PromptBuilder pb = new PromptBuilder();
// Ein Lexion zur Aufnahme der abweichend auszusprechenden Wortformen:
Dictionary<string, string> Lexikon = new Dictionary<string, string>
{
["rehtiu"] = "S1 R EH C . T Y lng"
};
// unser Beispieltext:
string Text = "Swêr rehtiu wort gemerken kan,";
/* Los geht's Hedda!
* Lies Wort für Wort.
* Überprüfe, ob das Wort im Lexikon vermerkt wurde.
* Wenn ja, dann folge der dort in Lautschrift notierten Aussprache.
* Wenn nicht ... mach's wie immer, sprich so, wie es da "geschrieben" steht.*/
foreach (string Wort in Text.Split(' '))
{
if (Lexikon.Keys.Contains(Wort))
{
//Sonderfall:
pb.AppendSsmlMarkup(@"<phoneme alphabet=""ups"" ph=""" +Lexikon[Wort]+@""">"// Die Lautschriftvariante
+Wort+"</phoneme>");// gefolgt von der Graphie
}
else
{
pb.AppendText(Wort); // Normalfall: Hochdeutsche Aussprache.
}
}
synth.Speak(pb);
}
}
An Heddas Vortrag hat sich nichts geändert, nur an der Art und Weise, wie er zustande kam. Deshalb sollen die Details noch einmal genauer betrachtet werden. Wir haben es diesmal Hedda, bzw. dem von uns geschriebenen Computerprogramm überlassen, den vorzulesenden Text in Wörter zu zerlegen. Wir tun das durch die Anweisung Split(' '), die der alten computerlinguistischen Faustregel folgt, dass als Wort jede Abfolge von Zeichen zwischen zwei Leerzeichen angesehen wird. Das ist selbstverständlich eine grobe Vereinfachung, die aber für unsere Zwecke erst einmal genügt. Wort für Wort schlägt das Programm dann unter den Schlüsselbegriffen mit Lexikon.Keys.Contains(Wort) im Lexikon nach und hängt gegebenenfalls den dort vorgefundenen Eintrag an das Prompt der Aussage an. Gibt es keinen solchen Eintrag, dann wird das Wort selber an Hedda zur späteren Verarbeitung weitergereicht.
Nun ist unser Lexikon mit exakt einem Eintrag nicht gerade umfangreich. Aber wir haben uns ja auch nicht die Aufgabe gestellt, nur eine einzige Verszeile in gesprochene Sprache umzuwandeln, sondern wollten uns ein ganzes Buch vorlesen lassen. Oder zumindest die ersten 50 Verse davon. Doch bevor wir daran gehen, werden wir ein wenig „aufräumen“.
Für unsere bisherigen Experimente haben wir nur ein paar Zeilen Programmiercode gebraucht, die wir in der statischen Methode Main untergebracht haben. Wir wollen jetzt aber zu komplexeren Arbeitsschritten übergehen, die Hedda erledigen soll. Und deshalb werden wir nun als nächstes eine eigene Klasse für diese Aufgaben anlegen und unserem Projekt hinzufügen. Der geben wir den Namen Hedda.cs. Dann können wir auch weiter, und jetzt mit deutlich mehr Berechtigung davon sprechen, dass wir Hedda einen Befehl geben, oder Hedda etwas im Lexikon nachschlägt:

Die automatisch generierte Klasse Hedda.cs ändern wir ab, so dass sie nun so aussieht:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Speech.Synthesis;
namespace Mittelhochdeutsches_Hörbuch1
{
class Hedda
{
// Ein Lexion zur Aufnahme der abweichend auszusprechenden Wortformen:
private Dictionary<string, string> Lexikon = new Dictionary<string, string>
{
["rehtiu"] = "S1 R EH C . T Y lng"
};
public void LeseZeile (string Zeile, PromptBuilder pb)
{
/* Los geht's Hedda!
* Lies Wort für Wort.
* Überprüfe, ob das Wort im Lexikon vermerkt wurde.
* Wenn ja, dann folge der dort in Lautschrift notierten Aussprache.
* Wenn nicht ... mach's wie immer, sprich so, wie es da "geschrieben" steht.*/
foreach (string Wort in Zeile.Split(' '))
{
if (Lexikon.Keys.Contains(Wort))
{
//Sonderfall:
pb.AppendSsmlMarkup(@"<phoneme alphabet=""ups"" ph=""" + Lexikon[Wort] + @""">"// Die Lautschriftvariante
+ Wort + "</phoneme>");// gefolgt von der Graphie
}
else
{
pb.AppendText(Wort); // Normalfall: Hochdeutsche Aussprache.
}
}
}
}
}
Problemlos dürften sich die Elemente wiedererkennen lassen, die wir aus der Methode Main in Program.cs übernommen haben: Das Lexikon, die Schleife, in der wir Hedda die Worte der Zeile lesen lassen … Nur, dass wir diese jetzt in eine Methode der Klasse Hedda.cs, einen Befehl an Hedda, verpackt haben. Wichtig ist es übrigens, nicht die Änderung an den using – Anweisungen zu übersehen: Damit Hedda ihren Dienst tun kann, braucht auch sie die Speech.dll. „Unsere“ Methode Main müssen wir auch noch ändern, sie sieht jetzt so aus:
static void Main(string[] args)
{
SpeechSynthesizer synth = new SpeechSynthesizer();
PromptBuilder pb = new PromptBuilder();
Hedda Hedda = new Hedda();
//Unsere Textdatei:
StreamReader Textdatei = new StreamReader("D:/Lanzelet 1 - 49.txt");
string Zeile;
/* Schleife:
* Solange bis das Ende der Textdatei erreicht ist (der Befehl ReadLine() null ergibt)
* Lies die nächste Zeile ein
* Und übergib sie an Hedda zum Vorlesen.
*/
while(( Zeile = Textdatei.ReadLine())!=null)
{
Hedda.LeseZeile(Zeile, pb);
}
synth.Speak(pb);
}
[/csharp]
<em><strong><span style="color: #ff0000;">ACHTUNG!</span></strong> Ich habe die Textdatei, die wir verwenden, auf meinem Rechner im Hauptverzeichnis von Laufwerk D: abgespeichert. Wenn sie auf einem anderen Laufwerk oder Verzeichnis gespeichert wurde, muss die Pfadangabe natürlich entsprechend angepasst werden.</em>
Und auch in <code><span style="font-family: courier new,courier,monospace;">Program.cs</span></code> müssen wir noch eine <code><span style="font-family: courier new,courier,monospace;">using</span></code>-Zeile einfügen, damit <code><span style="font-family: courier new,courier,monospace;">Main</span></code> die Datei einlesen kann:
[code language="csharp"]using System.IO;
Dann können wir uns entspannt zurücklehnen und unser erstes Hörbuch genießen …
Na?
Zufrieden?
Wie …? Wirklich? … gar nicht?
Es ist wohl nicht zu leugnen: Wir haben noch etwas Arbeit vor uns!!!
Wir mögen damit zurechtkommen, wenn Hedda beim Vortrag einer einzelnen Verszeile, deren Text wir am Bildschirm mitlesen können, ein einziges Wort als völlig unverständliche Lautfolge umsetzt. Wenn wir sie jedoch eine längere Passage lesen lassen, ist es kaum noch möglich ihr zu folgen. Wir sollten uns jedoch vom ziemlich katastrophalen ersten Höreindruck nicht enttäuschen lassen. Schließlich enthält unser Lexikon ja bisher nur einen Eintrag! So kann es kaum eine Hilfe darstellen. Füllen wir es doch mit ein paar Einträgen und ändern die Klasse Hedda.cs entsprechend ab:
// Ein Lexion zur Aufnahme der abweichend auszusprechenden Wortformen:
private Dictionary<string, string> Lexikon = new Dictionary<string, string>
{
["rehtiu"] = "S1 R EH C . T Y lng",
["gemerken"]="G EX . S1 M EH R . K EX N",
["gedenke"]="G EX . S1 D EH N . K EX",
["wie"] = "S1 V I lng + EX",
["ein"] = "E + I N",
["wîze"] = "S1 V I lng . Z EX",
["hie"] = "S1 H I lng + EX",
["bî"] = "S1 B I lng",
["zîten"]= "S1 TS I lng . T EX N",
["sît"] = "S1 S I lng T",
["diu"] = "D Y lng",
["dûhte"] = "S1 D U lng X . T EX",
["niht"] = "N IH C T",
["gemuot"] = "G EH . S1 M U lng + O T"
};
Die ersten fünf Verszeilen sind nach der kleinen Änderung jetzt deutlich besser zu verstehen:
… und wenn wir uns konzentrieren, begegnen uns auch im weiteren Text einige der neuen Einträge unseres Lexikons wieder und machen diesen damit vereinzelt verständlicher.
Versuchen wir es positiv zu sehen: Wir sind auf dem richtigen Weg!
Ein Weg, der allerdings noch recht lang zu werden verspricht, wie diejenigen sicher bestätigen werden, die sich die Mühe gemacht haben, die Lexikoneinträge per Hand in Hedda.cs einzutragen und sie nicht einfach per copy & paste übernommen haben. Diesen fleißigen Tippern wird dabei jedoch vermutlich auch aufgefallen sein, dass die Umsetzung der Grapheme in Phoneme sehr regelhaft erfolgt. Als UPS-Notation der Aussprache für ein î haben wir zum Beispiel in wîze, bî, zîten und sît jeweils I lng ermittelt (IPA-Notierung: [i:]). Nach unserer bisherigen Erfahrung gilt anscheinend:
Bestimmten Buchstaben (-folgen) entsprechen stets und immer wieder bestimmte (Abfolgen von) Lautzeichen.
Wir werden versuchen aus dieser Beobachtung Nutzen zu ziehen, um uns weitere (unnötige) Tipparbeit zu ersparen. Und damit beginnen wir umgehend …
… im vierten Teil unseres Projektes.
