Dies ist der zweite Teil des Tutorials (daher heißt es auch Teil 2 im Titel). Der erste Teil findet sich hier.
Hedda spricht (eine Zeile) Mittelhochdeutsch!
Wir legen los und in Visual Studio ein neues Projekt an:
![]()
Aus dem Auswahlmenü wählen wir die Konsolen-App (Net-Framework), der wir einen sprechenden Namen geben. Wie wäre es mit „Mittelhochdeutsches Hörbuch1“? Unter welchem Namen auch immer, das Projekt wird erzeugt und uns die Datei Program.cs präsentiert:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mittelhochdeutsches_Hörbuch1
{
class Program
{
static void Main(string[] args)
{
}
}
}
Bevor wir unseren Computer zum Sprechen bringen können, müssen wir noch die zur Spracherzeugung benötigte Bibliothek einbinden. Dazu öffnen wir den Reiter „Projekt/Verweis hinzufügen“.

In den Assemblys aktivieren wir „System.Speech“ mit einem Häkchen:

Dann ändern wir die Ausgangsdatei (per copy & paste oder abtippen) folgendermaßen ab und starten mit F5 unser erstes Experiment:
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 Program
{
static void Main(string[] args)
{
SpeechSynthesizer synth = new SpeechSynthesizer();
synth.Speak("Swêr rehtiu wort gemerken kan,"); }
}
}
}
Jetzt sollten wir eine Stimme hören, die mit der erwarteten falschen Aussprache unseren Text spricht. Es ist gut möglich, dass diese Stimme uns unbekannt ist und nicht derjenigen entspricht, die wir von der Windows-Sprachausgabe gewohnt sind. Auf meinem Rechner ist es jedenfalls eine weibliche Stimme und damit definitiv nicht Stefan, die ich höre. Der Grund hierfür ist, dass Stefan dafür vorgesehen ist, Deutsch mit mir zu sprechen und anscheinend nur dazu in der Lage ist. Ihm fehlt die Möglichkeit, sich an ein anderes Idiom anzupassen. Etwas technischer formuliert: Das Sprachprofil, das mit dem Namen Stefan verbunden wird, bietet nicht alle Möglichkeiten, die durch die Programmierschnittstelle System.Speech.dll vorgesehen sind, und steht uns damit nicht zur Verfügung. Oder vielmehr: mir, zum Zeitpunkt, an dem ich dies schreibe. Denn selbstverständlich kann sich das mit dem nächsten Windows-Update ändern oder mit der nächsten Generation der Speech-Schnittstelle.
Wir können uns, wenn wir wollen, beim nächsten Durchlauf unseres kurzen Programms die uns zur Verfügung stehenden Stimmen vorstellen lassen, indem wir unsere statische Methode Main leicht verändern:
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 Program
{
static void Main(string[] args)
{
SpeechSynthesizer synth = new SpeechSynthesizer();
foreach (var voice in synth.GetInstalledVoices())
{
synth.SelectVoice(voice.VoiceInfo.Name);
synth.Speak(voice.VoiceInfo.Description);
synth.Speak("Swêr rehtiu wort gemerken kan,"); }
}
}
}
}
Mir stehen eine weibliche Stimme mit dem Namen Hedda zur Verfügung, die deutsch spricht, und eine Amerikanerin namens Sira. Ob es sich bei der um eine Verwandte von Apples ureigener Siri handelt? Und wer hat der „Deutschen“ den so typisch deutschen Namen Hedda verpasst? Die Auswahl der Stimmen ist sehr begrenzt, aber zumindest durch die Namensgebung für einen kleinen nerdigen Lacher gut. Wer wie ich mit Hedda leben muss (Nein, Sira kann ich mir nicht als Vorleserin der Aventiure vorstellen), mag vielleicht mit der Option käuflicher Stimmenprofile liebäugeln, aber eigentlich reicht ja eine Stimme für unser Vorhaben.
Sofern wir sie dazu bringen können, phonetisch korrektes Mittelhochdeutsch von sich zu geben.
Hedda sollte also eine Lautschrift verstehen, beziehungsweise in der Lage sein, einen in dieser transkribierten Text angemessen wiederzugeben. Genau das verspricht uns die Speech.dll, wenn sie Hedda unter den installierten – und der Norm gerechten – Stimmen aufführt. Und genau das werden wir jetzt austesten. Wenn wir es (vorerst) nicht ganz so kritisch sehen wollen, gibt es in dieser ersten Zeile unseres Textes ja nur ein Wort, das wirklich so falsch klingt, dass wir handeln müssen: rehtiu. Die anderen Wörter sind in ihrer hochdeutschen Aussprache nicht perfekt, aber, wenn wir ein paar Augen zudrücken, halbwegs akzeptabel. Konzentrieren wir uns auf rehtiu und versuchen Hedda beizubringen, das etwas besser zu artikulieren:
static void Main(string[] args)
{
SpeechSynthesizer synth = new SpeechSynthesizer();
string ssmlRehtiu = @"<speak version=""1.0"" xml:lang=""de-DE"">"
+ @<phoneme alphabet=""ups"" ph=""S1 R EH C . T YH lng"">"
+ "Rehtiu</Phoneme></speak>";
synth.SpeakSsml(ssmlRehtiu);
}
Das klingt doch schon besser, oder? Was wir hier benutzen, ist der SSML-Standard, ein XML-Codierungsschema zur Steuerung von Sprachausgaben (Speech Synthesis Markup Language). Wenn wir eine dafür passendere Formatierung wählen sieht das ungefähr so aus:
<speak version="1.0" xml:lang="de-DE">
<phoneme Alphabet="ups" ph="S1 R EH C . T YH lng"&>rehtiu</Phoneme>
</speak>
Das <speak>-Tag klammert eine Sprachausgabe ein. Innerhalb des <phoneme>-Tags finden wir unseren Ausgabetext wieder, so wie wir ihn kennen („kennen“ heißt, in der Buchstabendarstellung). Die Lautschriftfassung findet sich im Attribut ph = „S1 R EH C . T YH lng“. Es handelt sich um das Lautschriftsystem UPS, eine Microsoft-Eigenkreation. Dass wir dieses Kodierungssystem verwenden, müssen wir natürlich auch angeben: alphabet = „ups“. Im <speak>-Tag vermerken wir noch (verpflichtend!) die Version des Sprachstandards auf die wir uns beziehen und die Sprache, an der sich unsere Hedda orientieren soll. „de-DE“ steht selbstverständlich für das in Deutschland gesprochene Deutsch (im Gegensatz zum Schweizerischen beispielsweise).
Soweit so gut. Hedda ist in der Lage Text in einer Lautschrift zu verstehen. Tatsächlich sind es sogar drei Lautschriften, allen voran das internationale phonetische Alphabet (IPA). Dieses besteht bekanntlich in seinem Kern aus den lateinischen Buchstaben, die um einige Graphien ergänzt wurden, die besonders Altphilologen gut vertraut sind und normalerweise in der Sprache, der sie entnommen wurden, Laute repräsentieren, die es im Lateinischen nicht gab. Viele dieser Zeichen finden sich nicht auf der Standardtastatur und nicht im Standard-ASCII-Zeichensatz. Beispielsweise der ʃ-Laut in hübsch: [‚hypʃ]). Aus diesem Grund gibt es die beiden anderen Varianten der Lautkodierung, die Hedda versteht. Für Computerprogramme leicht zu verarbeiten ist die SAPI-ID (SAPI = Speech API), ein System, das jedem der IPA-Zeichen einen Zahlenwert zuweist: hübsch wird als [02C8 0068 028F 0283] erfasst. Uns kommt das weniger entgegen und deshalb haben wir uns für UPS entschieden, um mit Hedda zu kommunizieren.
UPS nutzt nur Zeichen, die auf der Tastatur (und im ASCII-Code) vorkommen. Häufig, aber nicht immer, entsprechen auch hier einzelne Buchstaben den lateinischen Lautwerten (wenn auch eher in der englischen Aussprache). Komplexere oder (vom englisch getönten Latein her betrachtet) ungewöhnlichere Laute werden durch Buchstabenkombinationen (EH AX etc.) wiedergegeben, die jeweils eine Einheit der Lautzeichenkette repräsentieren. Um die Segmente deutlich voneinander zu trennen muss ein Leerzeichen zwischen ihnen eingefügt werden.
ACHTUNG! Hedda, bzw. die Speech.dll, ist in dieser Hinsicht ausgesprochen pingelig! Fehlt das Leerzeichen, und lässt sich kein einzelner Lautwert finden, der einer Buchstabenfolge zugeordnet werden kann, bricht die Sprachausgabe ab!
In unserem Beispiel findet sich unter anderem auch die Kodierung lng. Sie gibt keinen Lautwert wieder, sondern zeigt an, dass der vorangehende Laut lang gesprochen werden soll (IPA verwendet den Doppeltpunkt [:] zur Bezeichnung der Länge). Auch solche Signale müssen durch Leerzeichen von ihren Nachbarn getrennt werden. Die weitere Erläuterung der Symbole der Lautschrift erspare ich uns vorerst. Sie findet sich schließlich ausführlich auf den Supportseiten von Microsoft dokumentiert.
Machen wir erst einmal weiter: Wir lassen Hedda den Beginn der Zeile sprechen und akzeptieren ihr modernes Hochdeutsch als Mittelhochdeutsch, fügen dann das problematische rehtiu in Lautschrift ein, und setzten danach den Text fort – jetzt wieder Neuhochdeutsch-Pseudomittelhochdeutsch:
static void Main(string[] args)
{
SpeechSynthesizer synth = new SpeechSynthesizer();
string ssmlRehtiu = @""
+ @""
+ "rehtiu";
synth.Speak("Swêr");
synth.SpeakSsml(ssmlRehtiu);
synth.Speak("wort gemerken kan,");
}
Das
hört sich
jetzt
sehr
abgehackt an. Vielleicht sollten wir doch gleich die ganze Zeile in phonetischer Schreibweise erfassen. Dann brauchen wir auch nicht so viele faule Kompromisse einzugehen:
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 Program
{
static void Main(string[] args)
{
SpeechSynthesizer synth = new SpeechSynthesizer();
foreach (var voice in synth.GetInstalledVoices())
{
synth.Speak(voice.VoiceInfo.Description);
string phoneme = " S1 S V EH lng R . "
+"S1 R EH C . T YH lng . "
+"S1 V AOX T . "
+"G EH . S1 M EX R . K EX N . "
+"S1 K A N _,";
string grapheme = "Swêr rehtiu wort gemerken kan,";
synth.SpeakSsml(@""
+grapheme+"");
}
}
}
}
Wenn wir also unseren Text einfach Zeile für Zeile in Lautschrift umsetzen und diese dann zu einer sehr, sehr, seeeehr lange Zeichenkette zusammensetzen … die wir dann natürlich noch zwischen die SSML-tags quetschen …
Nein. Das ergibt selbst dann kaum entzifferbare Bandwurmkonstruktionen, wenn wir Hedda eine Verschnaufpause am Ende jeder Zeile zugestehen, wo es nicht so auffällt. Und überhaupt wäre es letztlich deutlich einfacher, den Text direkt selber einzusprechen. Wir müssen und werden einen Weg finden, der mit weniger Aufwand zum Ziel führt.
… im dritten Teil des Projektes.
