niedziela, 20 stycznia 2013

Windows Phone 8 na żywo - odc 2 (komendy głosowe)

Dziś napiszę trochę o komendach głosowych w WP8.

W WP 7.x można było używać tylko standardowych poleceń typu “Otwórz kalendarz”. Teraz mamy możliwość definiowania dla naszych aplikacji własnych komend z frazami. Postanowiłem trochę się zabawić i zamienić aplikację z ostatniego posta w szafę grającą spełniającą ustnie wypowiadane życzenia. Pamiętam, że w dzieciństwie podziwiałem jakiś układ elektroniczny przełączający obwód na zadane hasło… Były też bajki o Sezamie… No czy jakoś odwrotnie -;) W każdym razie dziś mała namiastka tego.

Obsługa mowy jest bardzo prosta. Jest to część wysokopoziomowego WinPRT API (btw szkoda że nie ma takiego API w Windows 8).

Zacznijmy od zdefiniowania pliku XML z komendami (najlepiej użyć do tego predefiniowanego szablonu w Visual Studio).  W moim przypadku komendy w języku polskim przedstawiają się w tym pliku następująco:

<?xml version="1.0" encoding="utf-8"?>

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">

  <CommandSet xml:lang="pl-PL" Name="myPolishCommands">
    <CommandPrefix>Tuba</CommandPrefix>
    <Example>Graj ostatni</Example>

    <Command Name="playVideo">
      <Example>Graj ostatni</Example>
      <ListenFor>[Graj] {video}</ListenFor>
      <Feedback>Gram {video}</Feedback>
      <Navigate Target="/StartPage.xaml"/>
    </Command>

    <PhraseList Label="video">
      <Item>ostatni</Item>
      <Item>losowy</Item>
    </PhraseList>

  </CommandSet>

   …
 
</VoiceCommands>

Ogólnie zakładam mówienie w trzech postaciach: “Tuba, graj ostatni”, “Tuba, graj losowy” lub “Tuba, graj {nazwa utworu}” oraz krótsze wersje tych wypowiedzi bez słowa “graj” (zdefiniowane jest ono w []). Pierwsze dwa przypadki są zdefiniowane w samym pliku XML (fraza “video” o wartościach “ostatni” i “losowy”, wstawianie wartości z frazy oznaczone jest {}). O trzecim przypadku za chwilę. Warto wspomnieć, że w tym samym pliku możemy równocześnie zdefiniować inne wersje językowe komend poprzez inny CommandSet (u mnie “myEnglishCommands” z językiem en-US). Druga rzecz jaką warto dodać, to wygodne przełączanie języka mowy w Ustawienia –> Mowa. Jest to zupełnie niezależne od wszystkich innych ustawień językowych w telefonie.

Należy teraz zarejestrować w systemie zdefiniowane przed chwilą komendy. Czynimy to raz przy uruchomionej aplikacji (przeważnie w obsłudze zdarzenia Load strony startowej).

        async private void RegisterVoiceCommandsAtFirstRun()
        {
            var appSettings = IsolatedStorageSettings.ApplicationSettings;
            if (appSettings.Contains("firstRun")) return;

            Uri uri = new Uri("ms-appx:///VoiceCommands.xml", UriKind.Absolute);
            await VoiceCommandService.InstallCommandSetsFromFileAsync(uri);           

            appSettings["firstRun"] = true;
        }

Na tym etapie po jednokrotnym uruchomieniu aplikacji możemy wrócić do ekranu startowego i przytrzymać dłużej przycisk Start. Pojawi się okno dialogowe do słuchania komend (pierwszy screenshot z poniższych). Jeśli chcemy wiedzieć jakie aplikacje na naszym telefonie wspierają komendy naciskamy “?” i wyświetla się nam odpowiednie okno z pivotem (drugi screenshot). Przechodzimy wtedy do zakładki “aplikacje” (trzeci screenshot). Wróćmy jednak do chwili, gdy jesteśmy słuchani. Wypowiadamy nasze magiczne słowa i jeśli zostaniemy dobrze rozpoznani, to pojawi się okienko z ikoną naszej aplikacji i usłyszymy odpowiedź określającą podejmowaną czynność np. “Gram ostatni” (czwarty screenshot). Następnie aplikacja zostanie otworzona na wskazanej w definicji komendy stronie (u mnie jest to StartPage.xaml)

VC0_PL  VC1_PL  VC2_PL  VC3_PL

Jak aplikacja dowiaduje się, przez jaką komendę została uruchomiona? Komendy mogą mieć przecież różne parametry i zachowanie aplikacji może być nimi uwarunkowane. Twórcy WP8 nie szukali długo, zrealizowali to tak jak każdy inny mechanizm deep linking. Do adresu strony są doklejane parametry w określony sposób. W moim przypadku odczyt przychodzących parametrów został zrealizowany na stronie StartPage w następujący sposób:

        async protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            //…

            if (e.NavigationMode == System.Windows.Navigation.NavigationMode.New)
            {
                if (NavigationContext.QueryString.ContainsKey("voiceCommandName"))
                {
                    string voiceCommandName = NavigationContext.QueryString["voiceCommandName"];

                    switch (voiceCommandName)
                    {
                        case "playVideo":

                            string video = NavigationContext.QueryString["video"];

                            //…                          

                       if (video == "last")
                       {
                          //...
                       }
                       else if (video == "random")
                       {
                          //...
                       }
                      else
                      {
                         //...
                      }

                            //…

                            break;

                        default:
                            break;
                    }
                }
            }

            //…

      }

Jak dodać tytuły klipów zarządzanych przez aplikację do wartości frazy “video”? Zbiór wartości odświeżam korzystając z metody:

        async private Task UpdatePhrasesForVoiceCommands(string voiceCommandSet, string[] startPhrases)
        {
            var phrases = startPhrases.Concat(AllVideos.Select(f => GetTitle(f.Name.Replace(".mp4", "")))).ToList();

            var videoVcs = VoiceCommandService.InstalledCommandSets[voiceCommandSet];
            await videoVcs.UpdatePhraseListAsync("video", phrases);
        }

Tytuł nagrania z YouTube obcinam do słów, które powinny zawierać tytuł utworu (GetTitle). Całość wywołuję w następujący sposób:

await UpdatePhrasesForVoiceCommands("myPolishCommands", new string[] { "ostatni", "losowy" });

Przykładowe uruchomienie aplikacji za pomocą komendy zawierającej nazwę utworu pokazałem na poniższym nagraniu:

P.S  API dla mowy w WP8 obejmuje także zamianę tekstu na mowę oraz rozpoznawanie mowy za pomocą wbudowanych i własnych gramatyk, ale o tym może innym już razem.

Brak komentarzy: