This article has been localized into German by the community.
Spracherkennung (WPF zuhören lassen)
Im vorhergehenden Artikel haben wir uns damit beschäftigt wie man Text in gesprochenes Wort mittels der SpeechSynthesizer Klasse umwandeln kann. In diesem Artikel werden wir den anderen Weg beschreiben, nämlich die Umwandlung von gesprochenem Wort in Text. Um das zu machen werden wir die SpeechRecognition Klasse verwenden, die ebenfalls in der Sammlung System.Speech liegt. Diese Sammlung ist grundsätzlich nicht Teil eines neuen Projektes, aber man kann sie sehr einfach hinzufügen. Je nach verwendeter Version von Visual Studio könnte das etwa wie folgt aussehen:
Nachdem wir das erledigt haben beginnen wir mit einem sehr einfachen Spracherkennungs-Beispiel:
<Window x:Class="WpfTutorialSamples.Audio_and_Video.SpeechRecognitionTextSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SpeechRecognitionTextSample" Height="200" Width="300">
<DockPanel Margin="10">
<TextBox Margin="0,10" Name="txtSpeech" AcceptsReturn="True" />
</DockPanel>
</Window>
using System;
using System.Speech.Recognition;
using System.Windows;
namespace WpfTutorialSamples.Audio_and_Video
{
public partial class SpeechRecognitionTextSample : Window
{
public SpeechRecognitionTextSample()
{
InitializeComponent();
SpeechRecognizer speechRecognizer = new SpeechRecognizer();
}
}
}
Das ist alles was man braucht. Der Text im Screenshot wurde über ein Headset diktiert und in eine TextBox eingefügt. Das alles mit Hilfe der Spracherkennung.
Nach der Initialisierung eines SpeechRecognizer Objekts startet Windows seine Spracherkennungsapplikation, die die harte Arbeit, die wir ihr auftragen verrichtet und anschließend das Ergebnis zur aktiven Anwendung, in diesem Fall unserer, schickt. Die Microsoft Spracherkennung sieht so aus:
Wenn man noch nie zuvor die Spracherkennung genutzt hat, wird Windows eine kleine Tour starten, auf der man den Umgang mit der Software lernt und einige wichtige und notwendige Einstellungen macht.
Das erste Beispiel erlaubt einem seiner Anwendung einen Text zu diktieren. Das ist großartig, aber wie ist das mit Kommandos? Windows und WPF arbeiten hier zusammen indem Buttons mit Kommandos belegt sind, die durch die Spracheingabe erkannt und genutzt werden können und das ganze ohne extra Arbeit investieren zu müssen. Hier ein Beispiel:
<Window x:Class="WpfTutorialSamples.Audio_and_Video.SpeechRecognitionTextCommandsSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SpeechRecognitionTextCommandsSample" Height="200" Width="300">
<DockPanel Margin="10">
<WrapPanel DockPanel.Dock="Top">
<Button Name="btnNew" Click="btnNew_Click">New</Button>
<Button Name="btnOpen" Click="btnOpen_Click">Open</Button>
<Button Name="btnSave" Click="btnSave_Click">Save</Button>
</WrapPanel>
<TextBox Margin="0,10" Name="txtSpeech" AcceptsReturn="True" TextWrapping="Wrap" />
</DockPanel>
</Window>
using System;
using System.Speech.Recognition;
using System.Windows;
namespace WpfTutorialSamples.Audio_and_Video
{
public partial class SpeechRecognitionTextCommandsSample : Window
{
public SpeechRecognitionTextCommandsSample()
{
InitializeComponent();
SpeechRecognizer recognizer = new SpeechRecognizer();
}
private void btnNew_Click(object sender, RoutedEventArgs e)
{
txtSpeech.Text = "";
}
private void btnOpen_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Command invoked: Open");
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Command invoked: Save");
}
}
}
Ihr könnt das Beispiel laufen lassen und dann einmal die Kommandos verwenden wie z.B. "New" oder "Open". Das erlaubt einem Text in die TextBox zu diktieren und gleichzeitig die Kommandos für das UI zu verwenden - echt ziemlich cool!
Spezielle Kommandos
Im obigen Beispiel wechselt Windows automatisch in den Diktiermodus sobald der Fokus auf die TextBox gesetzt wird. Windows wird dann automatisch versuchen zwischen der diktierten Sprache und den Kommandos zu unterscheiden, wobei das in bestimmten Situationen schwierig werden kann.
Während das obige Beispiel sich auf diktieren und interagieren mit der UI konzentriert hat, wird sich das nächste Beispiel damit beschäftigen auf bestimmte Kommandos zu hören und diese zu interpretieren. Das heißt natürlich auch, dass das diktieren komplett ignoriert wird, auch wenn Textfelder im Focus sind.
Zu diesem Zweck werden wir die SpeechRecognitionEnging Klasse statt der SpeechRecognizer Klasse benutzen. Der große Unterschied zwischen den beiden Klassen ist, dass die SpeechRecognitionEngine die Windows eigene Spracherkennungsapplikation nicht benötigt und man somit auch keine Einrichtung dieser vornehmen muss. Stattdessen wird eine einfache Spracherkennung genutzt und nur auf spezielle Wörter gehört die man in die Klasse gibt.
Im nächsten Beispiel werden wir eine Reihe von Kommandos in die Spracherkennung füttern. Die Idee dahinter ist das die Spracherkennung auf zwei Wörter reagiert: Ein Kommando und einen Wert, der in diesem Fall genutzt wird um die Farbe, Größe und Schriftgrad eines Text Labels zu verändern. Und das nur auf Basis von Sprachkommandos. Aber bevor der gesamte Code gezeigt wird werden wir den Fokus auf das hinzufügen der Kommandos zur Spracherkennung legen. Hier ist der dazugehörige Code:
GrammarBuilder grammarBuilder = new GrammarBuilder();
Choices commandChoices = new Choices("weight", "color", "size");
grammarBuilder.Append(commandChoices);
Choices valueChoices = new Choices();
valueChoices.Add("normal", "bold");
valueChoices.Add("red", "green", "blue");
valueChoices.Add("small", "medium", "large");
grammarBuilder.Append(valueChoices);
speechRecognizer.LoadGrammar(new Grammar(grammarBuilder));
Wir nutzen den GrammarBuilder um eine Reihe Wörter festzulegen, die wir in die SpeechRecognitionEngine laden können. Es gibt mehrere Methoden um etwas hinzuzufügen, die einfachste ist aber Append(). Diese Methode kann eine ganze Liste von Auswahlmöglichkeiten aufnehmen. Wir erstellen also mit dem ersten Teil der Anweisung eine Choices Instanz, welche die Kommandos und Eigenschaften beinhaltet, die wir nutzen wollen. Die Auswahlmöglichkeiten werden mittels der Append() Methode hinzugefügt.
Jedesmal wenn wir nun den GrammarBuilder aufrufen wird dieser angewiesen auf ein Wort zu hören. In unserem Fall möchten wir aber auf zwei Wörter hören, weswegen wir eine zweite Reihe von Möglichkeiten anlegen, die die Wörter für ein bestimmtes Kommando oder Eigenschaft beinhaltet. Wir fügen eine Reihe von Werten für jedes mögliche Kommando hinzu. Einen Satz für Werte für die Auswahl des Schriftgrades, einen Satz Werte für die Farbauswahl und einen Satz Werte für die Schriftgröße. Alle werden in die gleiche Choices Instanz geladen und angefügt.
Zum Schluß laden wir alles in die SpeechRecognitionEngine Instanz indem wir die LoadGrammar() Methode aufrufen, die eine neue Grammar Instanz öffnet. In unserem Fall unter Verwendung von unserer erstellten GrammarBuilder Instanz.
Nach dieser Erklärung lasst uns nun einmal das komplette Beispiel anschauen:
<Window x:Class="WpfTutorialSamples.Audio_and_Video.SpeechRecognitionCommandsSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SpeechRecognitionCommandsSample" Height="200" Width="325"
Closing="Window_Closing">
<DockPanel>
<WrapPanel DockPanel.Dock="Bottom" HorizontalAlignment="Center" Margin="0,10">
<ToggleButton Name="btnToggleListening" Click="btnToggleListening_Click">Listen</ToggleButton>
</WrapPanel>
<Label Name="lblDemo" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48">Hello, world!</Label>
</DockPanel>
</Window>
using System;
using System.Globalization;
using System.Speech.Recognition;
using System.Windows;
using System.Windows.Media;
namespace WpfTutorialSamples.Audio_and_Video
{
public partial class SpeechRecognitionCommandsSample : Window
{
private SpeechRecognitionEngine speechRecognizer = new SpeechRecognitionEngine();
public SpeechRecognitionCommandsSample()
{
InitializeComponent();
speechRecognizer.SpeechRecognized += speechRecognizer_SpeechRecognized;
GrammarBuilder grammarBuilder = new GrammarBuilder();
Choices commandChoices = new Choices("weight", "color", "size");
grammarBuilder.Append(commandChoices);
Choices valueChoices = new Choices();
valueChoices.Add("normal", "bold");
valueChoices.Add("red", "green", "blue");
valueChoices.Add("small", "medium", "large");
grammarBuilder.Append(valueChoices);
speechRecognizer.LoadGrammar(new Grammar(grammarBuilder));
speechRecognizer.SetInputToDefaultAudioDevice();
}
private void btnToggleListening_Click(object sender, RoutedEventArgs e)
{
if(btnToggleListening.IsChecked == true)
speechRecognizer.RecognizeAsync(RecognizeMode.Multiple);
else
speechRecognizer.RecognizeAsyncStop();
}
private void speechRecognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
lblDemo.Content = e.Result.Text;
if(e.Result.Words.Count == 2)
{
string command = e.Result.Words[0].Text.ToLower();
string value = e.Result.Words[1].Text.ToLower();
switch(command)
{
case "weight":
FontWeightConverter weightConverter = new FontWeightConverter();
lblDemo.FontWeight = (FontWeight)weightConverter.ConvertFromString(value);
break;
case "color":
lblDemo.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(value));
break;
case "size":
switch(value)
{
case "small":
lblDemo.FontSize = 12;
break;
case "medium":
lblDemo.FontSize = 24;
break;
case "large":
lblDemo.FontSize = 48;
break;
}
break;
}
}
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
speechRecognizer.Dispose();
}
}
}
Auf dem Screenshot sieht man das Ergebnis, nach der Verwendung der Sprachbefehle "weight bold" und "color blue" - schon cool, oder?
Den Sprachaspekt für das Beispiel haben wir schon erklärt und das UI ist sehr simpel, also konzentrieren wir uns nun auf den restlichen Code-behind.
Wir nutzen einen ToggleButton unter Verwendung der RecognzieAsync() und RecognizeAsyncStop() Methode um das Zuhören ein oder auszuschalten. Die Methode RecognizeAsync() übergibt einen Parameter, der die Spracherkennung darüber informiert ob ein oder mehrere Erkennungen durchgeführt werden sollen. In unserem Beispiel wollen wir aber ja mehrere Kommandos übergeben, also wird hier Multiple genutzt. Möchte man also die Erkennung aktivieren klickt man einfach auf den Button und klickt erneut wenn man die Erkennung deaktivieren möchte. Der Status wird durch die Position des Buttons dargestellt. Dieser ist "gedrückt" wenn die Spracherkennung aktiviert ist und im Normalzustand wenn die Spracherkennung deaktiviert ist.
Neben der Erstellung der Befehlssätze ist der interessanteste Teil wie wir ein Kommando interpretieren. Das wird mittels des SpeechRecognized Events erledigt, das wir im Konstruktor aufrufen. Wir nutzen den vollständig erkannten Text um das Demo Label zu aktualisieren und das letzte Kommando anzuzeigen. Mittels der Words Eigenschaft können wir die einzelnen Bestandteile des gesamten Sprachkommandos erkennen und umsetzen.
Zu Beginn haben wir geprüft das es sich um exakt zwei Wörter handelt, ein Kommando und einen Wert. Wenn das der Fall ist prüfen wir zuerst den Kommando Anteil und zu jedem möglichen Kommando arbeiten wir den entsprechenden Wert ab.
Für die Kommandos Schriftgrad und Schriftfarbe können wir den Wert umwandeln in etwas was vom Label verstanden wird, indem wir einen Konverter benutzen. Für die Schriftgröße können wir die Werte manuell interpretieren, da die im Beispiel gewählten Größen nicht automatisch konvertiert werden können. Beachtet bitte mögliche Fehler abzufangen, da ein Kommando wie "weight blue" versuchen wird den Wert blue dem Schriftgrad zuzuweisen, was natürlich in einem Fehler enden wird.
Zusammenfassung
Wie ihr hoffentlich sehen konntet ist die Spracherkennung in WPF sowohl einfach wie auch sehr mächtig. Vor allem das letzte Beispiel sollte euch einen Einblick verschafft haben wie mächtig die Spracherkennung ist! Mit der Fähigkeit eine Diktierfunktion wie auch spezielle Sprachkommandos zu verwenden kann man wirklich eine sehr gute alternative Eingabemethode für seine eigene Anwendung schaffen.