This article has been localized into Russian by the community.
Распознавание речи
В предыдущей статье мы обсуждали, как можно перевести текст в речь, используя класс SpeechSynthesizer. В этой статье мы, наоборот, изучим перевод речи в текст. Для этого будем использовать класс SpeechRecognition, который хранится в сборке System.Speech. Эта сборка не является частью решения по умолчанию, но мы можем легко добавить её. В зависимости от версии Visual Studio, которую вы используете, процесс выглядит так:
После добавления ссылки в проект, давайте начнём с самого простого примера распознавания речи:
<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();
}
}
}
Это всё - текст на скриншоте выше был продиктован через микрофон и вставлен в TextBox с использованием распознавания речи.
Когда вы проинициализируете объект SpeechRecognizer, Windows запустит приложение для распознавания голоса, которое сделает за нас всю сложную работу и отправит результат в активную программу, в данном случае, в нашу. Само приложение выглядит так:
Если функция распознавания речи на вашем компьютере используется впервые, то Windows запустит мастер для настройки начальных параметров.
Первый пример позволит распознавать диктуемый текст в вашем приложении, что уже хорошо, но как насчет команд? Здесь Windows и WPF будут работать сообща и превратят кнопки в команды, активируемые голосом, без дополнительных сложностей. Вот пример:
<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");
}
}
}
Попробуй запустить листинг выше и начать проговаривать команды в микрофон, которые хочешь исполнить. Например, сказав слово "Save", ты вызовешь обработчик события Click у кнопки btnSave. Одновременно с этим, твои слова будут отображаться в элементе TextBox. Круто, разве нет?
Специальные команды
В вышеприведённом примере Windows автоматически перейдёт в режим диктовки, как только фокус перейдёт к текстовому полю. Затем Windows будет пытаться различать диктовку и команды, что, конечно, может оказаться сложным в некоторых ситуациях.
Итак, если в вышеприведенных примерах основное внимание уделялось диктовке и взаимодействию с элементами пользовательского интерфейса, то в следующем примере мы сосредоточимся на возможности прослушивать и интерпретировать только определенные команды. Это также означает, что диктовка будет полностью игнорироваться, даже если поля ввода текста имеют фокус.
Для этой цели мы будем использовать класс SpeechRecognitionEngine вместо класса SpeechRecognizer. Огромная разница между ними заключается в том, что для класса SpeechRecognitionEngine не требуется работающее распознавание речи Windows, и вы не будете знакомиться с руководством по распознаванию голоса. Вместо этого он будет использовать базовое распознавание голоса и слушать только грамматику, которую вы вводите в класс.
В следующем примере мы передадим набор команд в систему распознавания голоса. Задача в том, что-бы она распознала два слова: команду/свойство и значение, которые в данном случае будут использованы что-бы изменить цвет, размер и толщину букв текста в подписи элемента управления, исключительно на основании ваших голосовых команд. Прежде чем я покажу вам полный текст примера кода, я хотел бы привлечь ваше внимание к тому, каким именно образом мы добавляем команду в систему распознавания голоса. Вот пример кода:
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));
Мы использовали GrammarBuilder чтобы построить набор правил грамматики, который мы в дальнейшем сможем передать в систему распознавания. Этот класс имеет несколько перегруженных вариантов метода добавления параметров, наиболее простой Append(), без параметров. Этот метод берет список вариантов. Мы создали экземпляр класса Choices с первой частью параметров - командой/свойством к которой мы хотим получить доступ. Этот список мы передали в GrammarBuilder методом Append().
Теперь, каждый раз когда мы вызываем метод append класса GrammarBuilder мы говорим ему какие слова слушать. В нашем случае мы хотим, что-бы он слушал два слова, поэтому мы создали второй набор слов, который содержит значения для команд/свойств из первого списка. Мы добавили необходимые значения для каждой из команд - один набор значений для параметра weight, один набор значений для параметра color и один набор значений для параметра size. Все эти наборы параметров мы добавили в один и тот-же экземпляр класса Choices и затем передали его командой append в GrammarBuilder.
В итоге, мы передали GrammarBuilder в класс SpeechRecognitionEngine с помощью вызова метода LoadGrammer(), который требует экземпляр класса Grammar в качестве входного параметра - в нашем случае он основан на классе GrammarBuilder.
Итак, после всех пояснений, давайте посмотрим на весь код:
<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();
}
}
}
На картинке вы видите окно итогового приложения после того как я дал голосом команды "weight bold" (шрифт жирный) и "color blue" (цвет синий) - довольно круто, не так-ли?
Грамматические аспекты этого примера уже были объяснены выше, и интерфейс очень простой, так-что давайте сосредоточимся на остальном коде нашего приложения.
Мы используем ToggleButton что-бы включить или выключить прослушивание, что осуществляется вызовами методов RecognizeAsync() и RecognizeAsyncStop(). Метод RecognizeAsync() получает параметр, который информирует ядро распознавания должно ли оно распознать одно слово или несколько слов. В нашем случае мы хотим дать несколько команд, поэтому используется параметр Multiple. Таким образом, что-бы начать распознание, просто щелкните по кнопке, что-бы остановить прослушивание просто щелкните еще раз. Состояние визуализируется состоянием кнопки, которая утоплена когда прослушивание включено и не утоплена когда прослушивание выключено.
Теперь, кроме создания объекта Grammar, с правилами, наиболее интересная часть где мы выполняем действия в зависимости от того какая команда поступила. Это делается в обработчике событий SpeechRecognized, который мы подключили в конструкторе. Мы используем распознанные команды что-бы обновить надпись названием распознанной команды и затем мы используем свойство Words что-бы получить непосредственно команду.
В первую очередь нужно проверить, что распознано точно два слова - команда/свойство и значение. Если это так, мы в первую очередь проверяем часть содержащую команду и затем, для каждой возможной команды мы проверяем соответствующие значения.
Для команд толщины шрифта и цвета мы можем преобразовать распознанное значение в значение, которое надпись может понять с помощью конвертера, но для значения размера мы должны сделать преобразование вручную, так как параметр который я выбрал для этого примера не может быть преобразован автоматически. Имейте в виду, что вы должны обрабатывать исключения для всех команд, так как команда вроде "weight blue" (толщина голубой) попытается присвоить значение "голубой" свойству FontWeight, что естественно вызовет исключение.
Заключение
Как вы можете видеть, распознание речи в среде WPF одновременно просто и функционально - особенно последний пример должен дать вам представление, насколько функционально. Обладая возможностью использовать диктовку и/или специальные голосовые команды, вы можете предоставлять отличные варианты альтернативного ввода пользователям своего приложения.