APIs e Microsserviços

8 mai, 2014

Usando as APIs de Speech para fazer reconhecimento de Voz no Windows Phone 8

Publicidade

Você já pensou em dar comandos de voz para o celular de dentro de uma aplicação e ele entender e executar tal comando? Agora é possível com o Windows Phone 8, é o que chamamos de reconhecimento de voz através as APIs de Speech.

Quando falamos de voz no Win Phone 8, temos três maneiras de comandos: Text to Speech (Texto para Falar), Speech to Text (Falar para Texto) e Voice Commands (Comandos de Voz).

Para vermos na prática, abra o Visual Studio .NET 2012 e selecione New Project. Cabe ressaltar que é obrigatório que você tenha o Windows 8 e o Windows Phone SDK 8 instalados. Nos Templates, selecione Visual C# (a linguagem) e Windows Phone, conforme a figura 1. Na lista de tipos de projetos, selecione Windows Phone App, sendo que o nome do projeto chamei de ReconhecimentoVoz. Clique em OK para cirar o projeto.

Figura 1 – Novo projeto de Windows Phone
Figura 1 – Novo projeto de Windows Phone

Em seguida é exibida a caixa de diálogo perguntando qual o sistema operacional, portanto selecione Windows Phone OS 8.0 e clique em OK, conforme a figura 2.

Figura 2 – Projeto no Win Phone 8
Figura 2 – Projeto no Win Phone 8

A primeira coisa a se fazer é informar à aplicação que teremos o recurso de Speech e de microfone. Para isto, no Solution Explorer, abra a pasta Properties e dê um duplo clique no arquivo WMAppManifest.xml. Este arquivo é responsável por toda a configuração das capacidades que a aplicação terá, por exemplo, se há localização, mapas, câmera, sensores, etc. Com o arquivo aberto, veja a figura 3, selecione a guia Capabilities. Na lista, marque os itens ID_CAP_SPEECH_RECOGNITION e ID_CAP_MICROPHONE.

Figura 3 – Manifesto da aplicação
Figura 3 – Manifesto da aplicação

Pronto, pode salvar e fechar este arquivo de manifesto. Agora abra o MainPage.xaml, que é onde definimos o layout e altere os títulos para “Reconhecimento de Voz ” e “Hello Voz”. Em seguida, dentro do Grid, substitua a palavra Grid por StackPanel e adicione outros três controles dentro do StackPanel, sendo:

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="Reconhecimento de Voz" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
            <TextBlock Text="Hello Voz" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Button x:Name="btnRecVoz" Content="Hello Windows Phone 8" Click="btnRecVoz_Click_1"/>
            <TextBlock x:Name="txtStatus"/>
            <TextBlock x:Name="txtResultado"/>
        </StackPanel>
Vá para a janela de código (F7) e na assinatura do evento btnRecVoz_Click (veja a listagem a seguir), inclua a palavra chave async para que este evento permita incluir códigos assíncronos. Em seguida, instancie a classe SpeechRecognizerUI (o using é o using Windows.Phone.Speech.Recognition) e configure as propriedades ListenText que é o título do texto mostrado na caixa de reconhecimento de voz. A ExampleText é um exemplo de retorno ao usuário. A ReadoutEnabled = true faz com que o telefone fale pra você a frase. A showConfirmation exibe o texto de confirmação.

Em seguida há um IF para verificar se foi bem sucedido ou não. Caso verdadeiro, o texto ditado é mostrado no txtStatus e o txtResultado mostra em que nível de entendimento foi identificado.

Um item que vale a pena destacar é a chamada da API de forma assíncrona através do RecognizeWithUIAsync. Note que há a palavra chave await que aguarda o retorna da API de Speech para validar ou não o reconhecimento.

private async void btnRecVoz_Click(object sender, RoutedEventArgs e)
{
    SpeechRecognizerUI sr = new SpeechRecognizerUI();
    sr.Settings.ListenText = "Hello Windows Phone 8";
    sr.Settings.ExampleText = "Diga o texto por favor";
    sr.Settings.ReadoutEnabled = true; 
    sr.Settings.ShowConfirmation = true; 
    SpeechRecognitionUIResult result = await sr.RecognizeWithUIAsync();
    if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
    {
        txtStatus.Text = result.RecognitionResult.Text;
        txtResultado.Text = result.RecognitionResult.TextConfidence.ToString();
    }
}

Pressione F5 para executar o código diretamente no emulador. Note na figura 4 que quando a aplicação é aberta, você deve clicar no botão criado entitulado “Hello Windows Phone 8”. O emulador mostra uma janela solicitando o aceite para acesso a API, então, clique em accept.

Figura 4 – Execução da aplicação e o aceite da API de Speech
Figura 4 – Execução da aplicação e o aceite da API de Speech

Em seguida, diga algo em inglês para submeter o reconhecimento e aguarde o resultado. Note na figura 5 que o texto é submetido para reconhecimento e o resultado é mostrado a seguir.

Figura 5 – Reconhecimento da voz
Figura 5 – Reconhecimento da voz

Existem várias possibilidades de reconhecimento, por exemplo, reconhecer o dia da semana. Neste caso, na tela do XAML, adicione os seguintes controles abaixo dos existentes, dentro do StackPanel.

<Button x:Name="btnDia" Content="Que dia é hoje?" Click="btnDia_Click"/>
<TextBlock x:Name="txtDia"/>
<TextBlock x:Name="txtResultadoDia"/>

Veja a seguir o código para o evento btnDia_Click, que é similar a anterior. Neste há um array de strings contendo todos os dias da semana em inglês, o qual é nesta lista que o reconhecimento ocorrerá. Veja que está comentado três dias da semana em português que fiz para testes, e acreditem, funciona. Na verdade ele reconhece porque está no array, óbvio que se você ditar Quarta, sendo que não está no array, não será reconhecida.

private async void btnDia_Click(object sender, RoutedEventArgs e)
{
    SpeechRecognizerUI sr = new SpeechRecognizerUI();
    sr.Settings.ListenText = "Que dia é hoje?";
    sr.Settings.ExampleText = "Sunday";
    sr.Settings.ReadoutEnabled = true;
    sr.Settings.ShowConfirmation = true;
    sr.Recognizer.Grammars.AddGrammarFromList("answer", new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" });
    // veja um exemplo com textos em portugues
    //sr.Recognizer.Grammars.AddGrammarFromList("answer", new string[] { "Domingo", "Segunda", "Sabado" });
    SpeechRecognitionUIResult result = await sr.RecognizeWithUIAsync();
    if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
    {
        txtDia.Text = result.RecognitionResult.Text;
        txtResultadoDia.Text = result.RecognitionResult.TextConfidence.ToString();
    }
}
O próximo passo é adicionar um arquivo xml com tags específicas. Para isto, no Solution Explorer, clique com o botão direito no nome do projeto e selecione Add / New Item. Na lista de templates, selecione Voice Command Definition (VCD) e o nome do arquivo será VoiceCommandDefinition1.xml, conforme a figura 6.
Figura 6 – XML com as definições dos comandos
Figura 6 – XML com as definições dos comandos

Este arquivo é xml e a tag VoiceCommands é que informará ao sistema operacional que a aplicação tem comandos de voz. Isto servirá para registrar somente na primeira vez a aplicação no SO.

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">
Em seguida, teremos os comandos em si. Para facilitar, veja o código completo na listagem a seguir. A tag CommandSet informa a linguagem. O CommandPrefix é o que você irá ditar antes do comando em si a ser executado para que ele saiba a que se refere. Veja que há o exemplo na tag Example com a frase “play a new game”. Em seguida, teremos os blocos de comandos em si, por exemplo, o bloco chamado PlayGame contém as frases que são os comandos em si contidos nas tags ListenFor. Aqui observe que temos o [and] e o [a], os quais representam uma forma chamada de opcional, ou seja, caso o comando de voz tiver ou não as palavras contidas entre colchetes [], o programa entenderá como o mesmo comando. Sendo assim, uma pessoa pode dizer “play a new game” ou “play new game”. A tag Feedback é o texto da resposta a ser exibido ao usuário. Agora, prepare-se para o melhor de tudo, que é a tag Navigate. Imagine que você quer que a aplicação abra outra página com o comando de voz, então, na tag Navigate você define qual é o alvo (Target=) e informe o nome da página XAML. Neste caso, temos a Page1.xaml que criaremos futuramente.
<?xml version="1.0" encoding="utf-8"?>

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">
  <CommandSet xml:lang="en-US">
    <CommandPrefix>Contoso Game</CommandPrefix>
    <Example> play a new game </Example>

    <Command Name="PlayGame">
      <Example> play a new game </Example>
      <ListenFor> [and] play [a] new game </ListenFor>
      <ListenFor> [and] start [a] new game </ListenFor>
      <Feedback> Starting a new game... </Feedback>
      <Navigate Target="Page1.xaml"/>
    </Command>

    <Command Name="PlayLevel">
      <Example> replay level two </Example>
      <ListenFor> replay level {number} </ListenFor>
      <Feedback> Going to level {number}... </Feedback>
      <Navigate />
    </Command>
  </CommandSet>
</VoiceCommands>

Até aqui já temos o arquivo VCD com os comandos. Como definimos que o comando “play new game” abrirá a Page1.xaml, então precisamos criá-la. No Solution Explore, clique com o botão direito, selecione Add / New Item com o template Windows Phone Portrait Page chamada Page1.xaml e clique em Add. No XAML da página, faça as devidas alterações conforme o código a seguir tendo novos textos e uma cor de fundo para o Grid. Não é necessário adicionar nenhum controle porque o objetivo aqui é apenas abrir a página.

XAML

<StackPanel Grid.Row="0" Margin="12,17,0,28">
    <TextBlock Text="Novo Game" Style="{StaticResource PhoneTextNormalStyle}"/>
    <TextBlock Text="Pagina 1" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Background="Chocolate" Grid.Row="1" Margin="12,0,12,0">

</Grid>

A seguir abra o código da MainPage.xaml.cs para registrar no sistema operacional que a aplicação contém comandos de voz. No construtor da classe, após o InitializeComponent, adicione a chamada para o método InicializaVCD(); e o respectivo método.

public MainPage()
{
    InitializeComponent();

    InicializaVCD();
}

private async System.Threading.Tasks.Task InicializaVCD()
{
    await VoiceCommandService.InstallCommandSetsFromFileAsync(new Uri("ms-appx:///VoiceCommandDefinition1.xml"));
}
O using a ser referenciado é:
using Windows.Phone.Speech.VoiceCommands;

Este método informa qual é o arquivo xml que contém os comandos de voz, neste caso é o VoiceCommandDefinition1.xml que criamos anteriormente. Pronto, pode compilar e fazer o deploy no emulador.

Para enviar comandos de voz, você precisa apertar por alguns segundos o botão do Windows do emulador. Será aberta uma janela mostrando o “Listening…” e você pode dizer “What can I say”, ou então, clicar no botão de interrogação, conforme a figura 7. Veja que em common são mostrados alguns exemplos. Não fale nada ainda porque o que nos interessa é a nossa aplicação.

Figura 7 – Iniciar o comando de voz
Figura 7 – Iniciar o comando de voz

Navegue com o mouse para a direita do controle Pivot, ou seja, clique na tela, segure e arraste o mouse para a esquerda, assim mostrará outras opções, como a de apps (aplicações) que nos interessa. Veja que a app ReconhecimentoVoz aparece na lista, conforme figura 8, afinal fizemos o código para informar ao sistema operacional que esta app tem comandos de voz. Clique na app ReconhecimentoVoz e aparecerão o comando (Contoso Game) e todas as possibilidades aceitas.

Figura 8 – Lista de comandos de voz aceita
Figura 8 – Lista de comandos de voz aceita

Agora prepare-se, clique no botão speak e fale: Contoso Game, e em seguida, play new game. Veja na figura 9 as três etapas, a primeira quando o telefone está ouvindo, a segunda reconhecendo o comando e a terceira com a Page1.xaml aberta pelo comando “start new game”. Isto é fantástico, imaginem a quantidade de soluçoes que podemos ter para diversos públicos como deficientes visuais, pessoas que tem dificuldade de enxergar letras pequenas, jogos, etc.

Figura 9 – Etapas do reconhecimento do comando de voz
Figura 9 – Etapas do reconhecimento do comando de voz

Como posso reconhecer os comandos que chegam numa página? Abra o código da Page1.xaml.cs e adicione o seguinte código no OnNavigatedTo, conforme a listagem a seguir. Neste código verifico se o modo de navegação (NavigationMode) é novo, afinal ele vem do comando de voz. Em seguida verifico se há uma chave chamada voiceCommandName na QueryString passada pela navegação entre páginas. Se sim, então armazeno na variável cmd que é uma string.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    if (e.NavigationMode == NavigationMode.New)
        if (NavigationContext.QueryString.ContainsKey("voiceCommandName"))
        {
            string cmd = NavigationContext.QueryString["voiceCommandName"];
        }
}

Veja na figura 10 o bloco do debug com o breakpoint no if. Note que a chave voiceCommandName contém um array com todos os comandos falados. Desta forma, caso necessário, você pode direcionar o comando para uma ação da aplicação.

Figura 10 – Breakpoint para verificar os comandos
Figura 10 – Breakpoint para verificar os comandos

Pense no público alvo da sua aplicação, nas diversas soluções que podemos ajudar a melhorar o entendimento e o acesso à informação, de forma fácil, simples e atraente. Creio que neste artigo despertei o interesse por esta nova tecnologia no Windows Phone 8.