Sprachfähige Apps entwickeln – Teil 2: Das Speech SDK

In dem vorherigen Blogbeitrag habe ich erklärt, wie man eigene Apps mit Cortana einbinden kann. Somit kann man zum Beispiel bestimmte Apps per Spracheingabe starten. Cortana leitet den Nutzer dann zur gewünschten App weiter.

Ab diesem Punkt übernimmt das Speech SDK alle weiteren Aufgaben der Sprachsteuerung. Das bedeutet: Entwickler können das Speech SDK benutzen, um ihre eigene App per Sprache steuern zu lassen. Aber nicht nur das - auch eine Sprachausgabe ist damit möglich.

Nehmen wir die GasFinder App als Beispiel, die ich im vorherigen Blogbeitrag vorgestellt habe. Der Benutzer kann Cortana starten und sagen: 'Sprit Sucher finde die nächste günstige Tankstelle'. Cortana wird dann die App öffnen und die nächste Tankstelle auf der Karte zeigen. Mittels Sprachsynthese wird dann das Ergebnis auch akustisch übermittelt. Darüber hinaus wird der Benutzer gefragt, ob die Route angezeigt werden soll. Seine Antwort wird durch Spracherkennung festgestellt und entsprechend darauf reagiert.

Wir müssen also zwei Bereiche genauer betrachten: die Spracherkennung und die Sprachsynthese.

Sprachsynthese oder Text-to-Speech ist für die künstliche Nachbildung natürlicher Sprache zuständig. Sie wandelt Text in Sprache um und nutzt dafür eine computergenerierte Stimme.
Damit wir unsere App mit Spracherkennung erweitern können, müssen wir erstmal auf SpeechSynthesis referenzieren.

using Windows.Media.SpeechSynthesis;

Nun können wir eine Methode SpeakText implementieren, die den SpeechSynthesizer benutzt, um einen Audio Stream aus Text herzustellen.
Für den SpeechSynthesizer können wir auch eine bestimmte Sprache festlegen. In diesem Fall habe ich mich für eine deutsche, weibliche Stimme entschieden, die ähnlich zu Cortana klingt.
Den Audio Stream können wir schließlich mittels einem MediaElement abspielen lassen.

private async Task SpeakText(string textToSpeak)
{
  using (var speech = new SpeechSynthesizer())
            {
  //Retrieve the first German female voice
  speech.Voice = SpeechSynthesizer.AllVoices.First(i => (i.Gender == VoiceGender.Female && i.Description.Contains("Germany")));

  //Generate the audio stream from plain text
  SpeechSynthesisStream ttsStream = await speech.SynthesizeTextToStreamAsync(textToSpeak);
  mediaPlayer.SetSource(ttsStream, ttsStream.ContentType);
  mediaPlayer.Play();
  mediaPlayer.CurrentStateChanged += OnStateChanged;
  }          
}

Um die Aussprache zu verfeinern kann man auch Speech Synthesis Markup Language (SSML) einsetzen.
SSML ist ein W3C Standard für die Optimierung der Sprache. Damit kann man zum Beispiel die Tonhöhe und die Geschwindigkeit anpassen.

Die App kann somit sprechen, aber noch nicht zuhören. Dafür müssen wir die SpeechRecognition API nutzen.

using Windows.Media.SpeechRecognition;

Wir implementieren eine Methode namens RecognizeSpeech, die für die Spracherkennung zuständig ist.
Innerhalb dieser Methode erstellen wir erstmal eine Instanz der SpeechRecognizer-Klasse und verwenden die SpeechRecognizer.Constraints-Eigenschaft, um Einschränkungen für die Spracherkennung hinzuzufügen.
Es gibt unterschiedliche Einschränkungen, die wir nutzen können. Zum Beispiel bietet sich für das Diktieren oder Websuche eine vordefinierte Grammatik an. Bei Verwendung dieser Grammatik wird die Spracherkennung remote durchgeführt. Deswegen ist in diesem Fall eine Internetverbindung nötig.
Die benutzerdefinierte Grammatik ist hingegen lokal vorhanden und bietet unter Umständen eine besser Leistung.
Für unser Demo erstellen wir eine benutzerdefinierte Grammatik in Form einer Liste mit Wörter und Wortgruppen, namens listConstraint. Diese enthält die mögliche Antworten vom Nutzer, die in einem Array namens possibleAnswers enthalten sind.
Die listConstraint müssen wir kompilieren indem wir CompileConstraintsAsync() aufrufen. Nun können wir die Erkennung starten. Dafür benutzen wir ein vordefiniertes User Interface, um Benutzern visuelles Feedback zu geben. 

 

private async Task<SpeechRecognitionResult> RecognizeSpeech()
{
  try
  {
    if (recognizer == null)
    {
       recognizer = new SpeechRecognizer();
       string[] possibleAnswers = { "Ja", "Klar", "Natürlich", "Nein", "Ne" };
       var listConstraint = new SpeechRecognitionListConstraint(possibleAnswers, "Answer");
       recognizer.UIOptions.ExampleText = @"Bsp. 'ja','nein'";
       recognizer.Constraints.Add(listConstraint);

       await recognizer.CompileConstraintsAsync(); 
    }               
                
    SpeechRecognitionResult result = await recognizer.RecognizeWithUIAsync();
    return result;
  }
  catch(Exception exception)
  {
       const uint HResultPrivacyStatementDeclined = 0x80045509;
       if ((uint)exception.HResult == HResultPrivacyStatementDeclined)
       {
          var messageDialog = new Windows.UI.Popups.MessageDialog("You must accept the speech privacy policy");
          messageDialog.ShowAsync().GetResults();
       }
       else
       {
          Debug.WriteLine("Error: " + exception.Message);
       }
   }
   return null;         
}

Nachdem wir RecognizeSpeech in der Listen-Methode aufrufen, können wir prüfen ob das gesprochene Wort erkannt wurde. Wir können auch abfragen, wie hoch die Konfidenz der Erkennung war und entsprechend reagieren. Schließlich, falls die Antwort des Nutzers erkannt wurde und positiv ist, wird die Route zur Tankstelle auf der Karte dargestellt.

In diesem Blogbeitrag haben wir gelernt, wie man eine einfache Sprachinteraktion mithilfe von Sprachsynthese und Spracherkennung implementieren kann.
Weitere Informationen dazu und mehr Material über die Einbindung eigener Apps mit Cortana, gibt es auch auf folgende Webseiten: