WP8 - Voice Command,讓App懂你的話 - 2

Windows Phone 8–Voice Command,讓App懂你的話–2

在撰寫<Windows Phone 8–Voice Command,讓App懂你的話–1>時,我發現在往下看範例時,

應該先了解一下VCD File裡所有的標籤與屬性的意義,有助於了解VCD File的定義與操作方式。

因此,針對Voice command element與attribute加以深入說明。

 

〉Voice Command Definition (VCD) file

    以下簡略列出一個範例,了解其VCD File的內容與定義的格式:

   1: <?xml version="1.0" encoding="utf-8"?>
   2:  
   3: <VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">
   4:   <!-- CommandSet:一種語系一個 -->
   5:   <CommandSet xml:lang="" Name="">
   6:     <CommandPrefix></CommandPrefix>
   7:     <Example></Example>
   8:  
   9:     <Command Name="">
  10:       <Example></Example>
  11:       <ListenFor></ListenFor>
  12:       <Feedback></Feedback>
  13:       <Navigate Target="" />
  14:     </Command>
  15:  
  16:     <!-- PhraseList依照{}的類型增長 -->
  17:     <PhraseList Label="">
  18:       <Item></Item>
  19:       <Item></Item>
  20:     </PhraseList>
  21:  
  22:   </CommandSet>
  23: </VoiceCommands>

 

往下針對每一個elements 與attributes 加以說明其定義與組合的方式:

(A) elements與attributes說明表

Element Description
VoiceCommands voice command manifest的root element (必要element)。
xmlns:屬性其值為固定:http://schemas.microsoft.com/voicecommands/1.0
該element可以包括一至多個<CommandSet />,而每一個<CommandSet />代表一種語系的voice command。
CommandSet <VoiceCommand />下的child element (必要element)。
一個<CommandSet />代表一種語系於應用程式中所支援的所有voice command,支援二種屬性,如下:
xmlns:lang:定義<CommandSet />屬於何種語系,在<VoiceCommand />中必須為唯一值。
Name:選擇用的屬性,可為任何字串,用途在於程式化參考與更新該<CommandSet />的<PhraseList />

該<CommandSet />包括以下的child elements,需注意這些child是有順序性,不可以更換出現位置:
‧<CommandPrefix /> (0 or 1);
‧<Example /> (1 only);
‧<Command /> (1 to 100);
‧<PhraseList /> (0 to more);
CommandPrefix <CommandSet />中選擇性的child element,如果該element出現代表它需出現在所有<CommandSet />中child element的第一個。建議定義一個user-friendly name,讓用戶在下達voice command時可以唸的出來,有助於當應用程式名稱非常難唸時很好用。
Command <CommandSet />中必要element。具有Name屬性。定義一個應用程序操作,用戶可以啟動的演講和什麼樣的用戶可以說,開始行動。每一個<Command />關聯至應用程式中指定的一個Page。

該<Command />包括以下的child elements:
‧<Example  /> (1 only);
‧<ListenFor /> (up to 10);
‧<Facebook /> (1 only);
‧<Navigate /> (1 only);
Example <Command />中必要的element<CommandSet />中選擇性的element。 提供一個範例告知用戶如何說出指令
定義的Example會出現至下列的Screen:
1. 在what can I say screenapps頁籤中,各個應用程式名稱下出現一句範例;
2. 在what can I say screenopen頁籤中,呈現二個額外的範例說明:應用程式名稱 + 範例字段,可連結至apps頁籤
3. 在some error screens。
4. 在Listening screens。
5. 在Did you know? help screen中列出該應用程式的指令清單。

上述幾個點,透過下圖說明:
vm-04 vm-05 vm-06
ListenFor <Command />中必要的element。該內容包括word或phrase讓應用程式識別與組織成一個指令。它可能include或reference到<PhraseList />中的Label屬性值,並且在<ListenFor />中用「{Label value}」給包裝起來。
一個<Command />可最多擁有10組<ListenFor />,而其中每一個<LisenFor />均可以被組織成一個active the command。
使用「[]」方框包住的word代表是選擇性使用的話,在口訴指令時可以使用或省略其word。
另外,設定一組 <ListenFor>Find {*}</ListenFor>:指定星字符號是任何一種word或phrase,口訴指令只要說:Find。
Feedback <Command />中必要的element。定義當指令被正確識別後,出現的回饋字段。
如果<Feedback />包含參考的<PhraseList />元素,代表其<Command />下的每一個<ListenFor />均要參考到該<PhraseList />,這樣組合字段才能正確匹配。
Navigate <Command />中必要的element。透過Target屬性指令當voice command被系統執行時要導向應用程式的那一個Page。
在Target屬性中除了可以指定Page名稱,也可以加上QueryString。如果沒有指定Target值,預設指定main page。

如下:
<Navigate Target="Page2.xaml?myParam=someValue"/>
PhraseList <CommandSet />中選擇使用的element,必要有Label屬性。該Label值必須與<ListenFor />或<Feedback />中使用{}包裝起來的值相同。選擇用的Disambiguate屬性(default為true),用於控制<PhraseList />有些易混淆的word或phrase,在組合voice command的參數至QueryString時詳細指出。

最多有2,000個<Item />,其限制受限於同一個<CommandSet />下的<PhraseList />總合。每一個<Item />可被組合成對應的active command。
Item <PhraseList />中選擇使用的element,內容可為多個words或phrases的組合。一個<CommandSet />可擁有最多2,000個<Item />,所有<PhraseList />的總合。

 

(B) []與{}符號的定義

Special character Description
{} 用於包括<PhraseList />的Label屬性值,例如:{myphraselist}。可使用於<ListenFor />或<Feedback />之中,而且二者使用的<PhraseList />必須相同。
[] 用於指定接近的單字或片語時,是選擇性使用的。接近的單字或片語,舉例來說:
<ListenFor>[start] [begin] new game</ListenFor>,用戶說"start new game"、"begin new game"或"new game",其實都是符合條件的。
每一個[]所包括的內容是獨立應用的,它們出現的順序需按照<ListenFor />中所定義的排列。例如:new game,可以說"start new game"是正確的,但說成"begin start new game"即是錯誤的。因[start]在[begin]前面。

要注意,不可以寫成 [[start] new game]或[{myphraselist}]這種巢狀式組合的字段

 

以上主要針對VCD File內容中的element與attribute做了一個詳細的說明,有助於了解voice command如何定義,並且在定義的時候,

也可以思考未來更新<PhraseList />中<Item />的機制。

 

接下來針對處理Voice Command時必要的元素加以說明。

Windows.Phone.Speech.VoiceCommands

    主要的voice command functionally,包括初始化Voice Command Definition (VCD)檔案與處理VCD檔案中的每一個元素。

該Namespace有二個主要的類別:

類別 說明
VoiceCommandService 靜態類別。負責初始化、安裝VCD檔案中的每一個Command sets,也支援取得被安裝過的Command sets。
VoiceCommandSet 代表一個被安裝過的Command Set。

往下細部來看這二個類別。

 

(A) VoiceCommandService Class

       此為靜態類別。負責初始化、安裝VCD檔案中的每一個Command sets,也支援取得被安裝過的Command sets。

以下是它可用的方法與屬性:

類型 名稱 說明
Method InstallCommandSetsFromFileAsync 安裝Voice Command Definition中定義的<CommandSet />元素。
Attribute InstalledCommandSets 屬於唯讀(Read-Only)。該dictionary中包括所有安裝的Command sets,可透過指定的Name來取得Voice Command Definition檔案中的Command sets

 

(B) VoiceCommandSet

       代表一個被安裝過的Command set。也就是透過VoiceCommandService.InstalledCommandSets("Name");方法取得的Command set物件

要針對<CommandSet />中引用的<PhraseList />透過程式化動態更新,即需要取得VoiceCommandSet物件,以下是該類別的方法與屬性:

名稱 說明
Method UpdatePhraseListAsync 填充<PhraseList />中每一個<Item />項目後,利用此方法更新VoiceCommandSet物件所參考的Phrase List。
Attribute Language 取得該VoiceCommandSet物件指定的語系。
Attribute Name 取得該VoiceCommandSet物件的名稱。

 

以下是針對Voice Command中重要的API與Voice command definition檔案的說明,往下介紹二個必要常用的範例。

 

[範例]

本篇範例延用<Windows Phone 8–Voice Command,讓App懂你的話–1>的公車語音範例,加以說明:

1. 動態更新<PhraseList />,依賴外部匯入的詞彙清單

   1: private async void Button_Click_1(object sender, RoutedEventArgs e)
   2: {
   3:     // 動態更新<PhraseList />中的<Item />
   4:     // 利用<CommandSet />中的Name屬性取得值
   5:     VoiceCommandSet tCmdSet = VoiceCommandService.InstalledCommandSets["myTWCommands"];
   6:     // 更新該<CommandSet />中指定Label屬性的<PhraseList />
   7:     await tCmdSet.UpdatePhraseListAsync("busnumber", new string[] { "100", "55688", "983", "189", "101" });
   8: }

 

2. 透過background agent更新<PhraseList />

    2-1. 在專案中先建立一個Windows Phone Schedule Task Agent的Background Agent專案,並且加上動態載入新Phrase List的邏輯

   1: protected override void OnInvoke(ScheduledTask task)
   2: {
   3:     // 啟動更新CommandSet
   4:     BgUpdatePhraseList();
   5:  
   6:     NotifyComplete();
   7: }
   8:  
   9: private async void BgUpdatePhraseList()
  10: {
  11:     // 建立一個更新完畢後要通知的Toast物件
  12:     Microsoft.Phone.Shell.ShellToast tToast = new Microsoft.Phone.Shell.ShellToast();
  13:     tToast.Title = "VCDBackgroundApp";
  14:  
  15:     try
  16:     {
  17:         // 取得該應用程式目前Speech System已安裝的CommandSets集合。
  18:         System.Collections.Generic.IReadOnlyDictionary<String, VoiceCommandSet> CommandSets = 
  19:             VoiceCommandService.InstalledCommandSets;
  20:         // 取得指定VCD File中定義的<CommandSet />Name。
  21:         VoiceCommandSet EnglishVCS = CommandSets["myTWCommands"];
  22:  
  23:         // 更新該<CommandSet />中引用的<PhraseList />Label。
  24:         String[] tBusNumber = { "909", "253", "476", "001", "1", "72", "33" };
  25:         String[] tStopName = { "大坪林", "士林", "圓山", "台北車站" };
  26:         await EnglishVCS.UpdatePhraseListAsync("stopname", tStopName);
  27:         await EnglishVCS.UpdatePhraseListAsync("busnumber", tBusNumber);
  28:         tToast.Content = "更新 Phraselists 成功!";
  29:         tToast.Show();
  30:     }
  31:     catch (Exception err)
  32:     {
  33:         Debug.WriteLine(err.ToString());
  34:         Debug.WriteLine(err.Message + " " + err.StackTrace);
  35:         tToast.Content = "更新 Phraselists 失敗!";
  36:         tToast.Show();
  37:     }
  38: }

 

    2-2. 在主程式中加入一個PeriodicTask物件,指定ScheduleActionService進行Schedule Task的任務

   1: private void Button_Click_2(object sender, RoutedEventArgs e)
   2: {
   3:     try
   4:     {
   5:         // 定義一個PeriodicTask物件,設定期限為1天
   6:         PeriodicTask task = new PeriodicTask("MyTask");
   7:         task.Description = "定期更新 voice command phrase lists.";
   8:         task.ExpirationTime = DateTime.Now.AddDays(1);
   9:  
  10:         // 識別目前是否有存在的PeriodicTask,有便移除
  11:         if (ScheduledActionService.Find(task.Name) != null)
  12:         {
  13:             ScheduledActionService.Remove(task.Name);
  14:         }
  15:  
  16:         //加入該PeriodicTask
  17:         ScheduledActionService.Add(task);
  18:  
  19: #if DEBUG
  20:         // 指定在5秒後,執行background task
  21:         // LaunchForTest() 只支援在debug模式下運作
  22:         ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(5));
  23: #endif
  24:     }
  25:     catch (Exception err)
  26:     {
  27:         Debug.WriteLine(err.ToString());
  28:         Debug.WriteLine(err.Message + " " + err.StackTrace);
  29:     }
  30: }

           需注意Shcedule Task Agent在正式版運作時,系統採用每30分鐘更新一次,以上範例是透過ScheduledActionService.LaunchForTest指定

           5秒內更新的效果,在正式版需注意。

   

[範例程式]

 

[補充]

‧註冊Voice command File遇到錯誤訊息

    ‧0x80045584:主要是<CommandSet />中xml:lang屬性值設定的語系錯誤。

======

連續撰寫二篇有關Voice Command操作的文章,希望能讓大家跟我一樣了解如何使用Voice Command使應用程式有更好的操作體驗。

相關其他Speech API的使用,請往下繼續收看。

 

References:

Voice command element and attribute reference for Windows Phone 8 (重要)

Handling errors in speech apps for Windows Phone (錯誤代碼查詢)

System voice commands for Windows Phone 8 (WP8各語系Command的說明)

Speech For Windows Phone: Refreshing a phrase list using a background agent (重要)

App capabilities and hardware requirements for Windows Phone

App policies for Windows Phone

Use Speech on my phone

Speech-Enabling a Windows Phone 8 App with Voice Commands (重要)

 

Dotblogs Tags: