【Win10 應用開發】集成語音命令


記得老周以前在寫WP8應用開發的文章時,曾經寫過語音命令集成的文章,后來8.1的時候“小娜”問世,但考慮到其變化不大,故老周沒有補寫相應的文章。

今天,老周打算補一下Win 10通用應用開發中,有關語音命令集成相關的內容。雖然還是一脈相承,大的變化沒有,不過Win10 sdk在語音命令定義文件中添加了新內容,而且現在不僅能在手機應用中加入語音集成,在面向PC和板子的應用中也能如願,因為應用程序已經通用。

同理,在開始之前,老周仍然先給大家講個故事。

話說10166的SDK已經發布,當然如果你網速飛快並有興趣的話可以下來裝裝,不下也無妨,畢竟是可選的。上回老周告訴大家如何通過修改VS的項目模板來匹配SDK版本號,要是大家裝了10166的SDK,也可以去改改,方法我就不重復了。

這一次再給大家介紹一個技巧。或許細心的各位已經發現,UAP項目的引用列表中包含了兩套程序集,分別是:

1、用於遙測的ApplicationInsights類庫。

2、用於特珠數值類型的庫,比如矩陣,一般是在DX繪圖中用到,程序集為System.Numerics.Vectors。

這兩個玩意兒屬於NuGet包,引用它們會增大項目的體積。而且我們可能用不上它們,但在創建項目時它們會被默認引用。一種方法你可以在創建項目后手動刪除它們,然后把項目導出為自定義的項目模板,這樣以后你用自定義的應用項目模板來創建項目,就會帶有這些引用了。

如果你想一勞永逸,又不想導出自定義模板,其實也可以和上次一樣,直接在VS目錄中修改UAP項目模板來實現。打開C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\ProjectTemplates\CSharp\Windows Root\Windows UAP\1033目錄,我們只需修改常用的幾個項目就行了。

a、先改BlankApplication項目(空白應用),打開\BlankApplication目錄,找到BlankApplication.vstemplate文件,用文本編輯器打開(記事本就行了,右擊,從上下文菜單中選擇[編輯]),打開文件后,一直滾動到XML文檔的最后,你會看到有這么幾段:

  <WizardExtension>
    <Assembly>Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
    <FullClassName>Microsoft.VisualStudio.WinRT.TemplateWizards.CreateProjectCertificate.Wizard</FullClassName>
  </WizardExtension>
  <WizardExtension>
    <Assembly>Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
    <FullClassName>Microsoft.VisualStudio.WinRT.TemplateWizards.ApplicationInsights.Wizard</FullClassName>
  </WizardExtension>
  <WizardExtension>
    <Assembly>NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
    <FullClassName>NuGet.VisualStudio.TemplateWizard</FullClassName>
  </WizardExtension>
  <WizardData>
    <packages repository="registry" keyName="UAPFrameworkDependenciesLocationVS14" isPreunzipped="true">
      <package id="System.Numerics.Vectors" version="4.0.0" skipAssemblyReferences="false" />
    </packages>
  </WizardData>

其中,有兩段就是和遙測庫、Numerics.Vetors相關,即以下兩個節點:

  <WizardExtension>
    <Assembly>Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
    <FullClassName>Microsoft.VisualStudio.WinRT.TemplateWizards.ApplicationInsights.Wizard</FullClassName>
  </WizardExtension>

  <WizardData>
    <packages repository="registry" keyName="UAPFrameworkDependenciesLocationVS14" isPreunzipped="true">
      <package id="System.Numerics.Vectors" version="4.0.0" skipAssemblyReferences="false" />
    </packages>
  </WizardData>

 

在XML文檔中找到以上兩個節點,然后把它們注釋掉即可,不建議直接刪除。因為一旦發現不正常或者你以后想使用這些擴展庫時,就可以取消注釋來還原。

  <WizardExtension>
    <Assembly>Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
    <FullClassName>Microsoft.VisualStudio.WinRT.TemplateWizards.CreateProjectCertificate.Wizard</FullClassName>
  </WizardExtension>
<!--
  <WizardExtension>
    <Assembly>Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
    <FullClassName>Microsoft.VisualStudio.WinRT.TemplateWizards.ApplicationInsights.Wizard</FullClassName>
  </WizardExtension>
-->
  <WizardExtension>
    <Assembly>NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
    <FullClassName>NuGet.VisualStudio.TemplateWizard</FullClassName>
  </WizardExtension>
<!-- 
  <WizardData>
    <packages repository="registry" keyName="UAPFrameworkDependenciesLocationVS14" isPreunzipped="true">
      <package id="System.Numerics.Vectors" version="4.0.0" skipAssemblyReferences="false" />
    </packages>
  </WizardData>
-->

這樣就可以了,然后保存。注意權限,你可以在父目錄的權限上加上你當前的登錄用戶並完全控制,等修改完后再把父目錄上的當前用戶權限刪掉即可,因為容器上的權限會自動應用到子對象上。修改權限有不少人的壞慣是直接把所有者改掉,這是不合理的,對於需要保護的系統文件或程序文件,不要動不動就改掉人家的所有者帳戶。

b、類庫項目。打開\ClassLibrary目錄,然后用文本編輯器打開ClassLibrary.vstemplate文件,把下面內容注釋掉,然后保存。

<!--
  <WizardData>
    <packages repository="registry" keyName="UAPFrameworkDependenciesLocationVS14" isPreunzipped="true">
      <package id="System.Numerics.Vectors" version="4.0.0" skipAssemblyReferences="false" />
    </packages>
  </WizardData>
-->


c、Runtime組件項目。打開\RuntimeComponent目錄,再打開RuntimeComponent.vstemplate文件,把下面內容注釋掉。

<!--
  <WizardData>
    <packages repository="registry" keyName="UAPFrameworkDependenciesLocationVS14" isPreunzipped="true">
      <package id="System.Numerics.Vectors" version="4.0.0" skipAssemblyReferences="false" />
    </packages>
  </WizardData>
-->


d、單元測試項目。打開\UnitTestApp目錄,打開UnitTestApp.vstemplate文件,把下面內容注釋掉,然后保存。

<!--
  <WizardData>
    <packages repository="registry" keyName="UAPFrameworkDependenciesLocationVS14" isPreunzipped="true">
      <package id="System.Numerics.Vectors" version="4.0.0" skipAssemblyReferences="false" />
    </packages>
  </WizardData>
-->

 

========================================================

 好了,故事講完了,下面我們開始干正事。其實,實現語音命令主要的難度在於定義語音命令,所以,下面老周就從頭到尾給大家演示一下如何編寫VCD文件。

往項目中添加一個xml文件。默認在新的XML文件中會生成以下行:

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

不用管他,這是XML文件通用的文檔標記,首先,我們定義文檔的根VoiceCommands。

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

每類XML文檔都有相應的規范,這些規范一般以.xsd文件定義,我們在編寫XML文檔時,通常是引入相應的命名空間來進行驗證,就好像我們在C#中要使用某個類型可以先using其所在命名空間(VB中為Import)一樣。在VCD(語音命令定義)文件中我們要引入http://schemas.microsoft.com/voicecommands/1.2命名空間。

這跟以前的VCD文件結構一樣,只是注意后的版本號要改為1.2,WP8.1的時候是1.1,現在是1.2。VoiceCommands是整個文檔的根節點,它下面可以包含多個CommandSet節點,最少1個,最多15個,至少目前來說xsd文件中是這樣定義。通常,CommandSet節點將作為一個命令集合存在,以語言為划分,比如中文的命令歸到一個CommandSet中,安哥拉語歸一個CommandSet,鳥語也歸到一個CommandSet中。

這里我只定義zh-cn語言的命令集:

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
  <CommandSet xml:lang="zh-cn" Name="set">
    
  </CommandSet>
</VoiceCommands>

CommandSet既然是命令集了,就說明它下面可以包含N條語音命令。是的,它可以包含最多100條語音命令,用Command元素表示。但是,CommandSet的子元素在設置時必須按順序進行,你不能亂來。

首先必須放一個CommandPrefix元素或者AppName元素,CommandPrefix元素是以前就有的,AppName元素是現在新增的,根據xsd文件的定義,這兩個元素都是一樣的,所以你不能同時,只能任選其一。大概是為兼容早期版本的VCD文件,所以保留CommandPrefix元素。CommandPrefix和AppName元素的作用是給你的程序起一個別名,這個名字一定要方便用戶用嘴巴去說的,比如你的應用叫“高大上_v2.0.1”,這個你讓用戶怎么念呢,所以這時候你可以為程序起一個好名字。

  <CommandSet xml:lang="zh-cn" Name="set">
    <AppName>高大上</AppName>
    
  </CommandSet>

這樣一來,用戶在使用語音命令時,只要說出“高大上”就能識別出是你的高大上應用了。

AppName之后,要定義一個Example元素,用來告訴用戶如何使用你的程序的語音命令,比如“微博 發微博。”。

  <CommandSet xml:lang="zh-cn" Name="set">
    <AppName>高大上</AppName>
    <Example>“高大上 紅色”,或者“高大上 左對齊”</Example>
    
  </CommandSet>

接下來就是定義語音命令了,本例就定義兩個命令,第一個命令叫color,通過它可以改變界面上文本的顏色;第二個命令叫align,通過它可以修改文本在水平方向上的對齊方式(左、中、右)。

    <Command Name="color">
      <Example>“紅色”或者“改為紅色”</Example>
      <ListenFor>[改為]{coloritem}</ListenFor>
      <Feedback>正在修改顏色……</Feedback>
      <Navigate />
    </Command>

Example與上面的Example功能一樣,但意義不同,上面的Example元素是面向整個命令集的,而Command上的Example元素是針對當前命令的。

ListenFor表示語音識別系統要聆聽的內容,“改為”被中括號包圍,表示可選,即就算你沒有說出“改為”兩字也能進行識別,后面的coloritem放在一對大括號中,表示它引用一個短語列表,內容可以是短語列表中的任何一項,比如:

    <PhraseList Label="coloritem">
      <Item>紅色</Item>
      <Item>藍色</Item>
      <Item>綠色</Item>
      <Item>紫色</Item>
    </PhraseList>

PhraseList元素定義可以被識別的列表候選項,Label屬性是必須的,它的名字要與前面Command中ListenFor元素中的引用對應,不然ListenFor無法找到相應的列表項。PhraseList元素必須放在Command元素后面。

下面我們來定義第二條命令,用於設置文本的水平對齊方式。

    <Command Name="align">
      <Example>“左對齊”、“居中”、“右對齊”</Example>
      <ListenFor>{alignitem}[對齊]</ListenFor>
      <Feedback>正在設置對齊方式……</Feedback>
      <Navigate/>
    </Command>
    <PhraseList Label="alignitem">
      <Item></Item>
      <Item>居中</Item>
      <Item></Item>
    </PhraseList>


Navigate元素雖然是必須的,但在RuntimeApp中用不上,所以Target屬性不必設置。FeedBack是當識別成功后,在小娜面板上顯示的內容(小娜會讀出它),以向用戶提供操作反饋。

整個VCD文件的內容如下:

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

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
  <CommandSet xml:lang="zh-cn" Name="set">
    <AppName>高大上</AppName>
    <Example>“高大上 紅色”,或者“高大上 左對齊”</Example>
    <Command Name="color">
      <Example>“紅色”或者“改為紅色”</Example>
      <ListenFor>[改為]{coloritem}</ListenFor>
      <Feedback>正在修改顏色……</Feedback>
      <Navigate />
    </Command>
    <Command Name="align">
      <Example>“左對齊”、“居中”、“右對齊”</Example>
      <ListenFor>{alignitem}[對齊]</ListenFor>
      <Feedback>正在設置對齊方式……</Feedback>
      <Navigate/>
    </Command>
    <PhraseList Label="coloritem">
      <Item>紅色</Item>
      <Item>藍色</Item>
      <Item>綠色</Item>
      <Item>紫色</Item>
    </PhraseList>
    <PhraseList Label="alignitem">
      <Item></Item>
      <Item>居中</Item>
      <Item></Item>
    </PhraseList>
  </CommandSet>
</VoiceCommands>

確保該XML文件的生成操作為“內容”,不復制到輸出目錄

 

在應用程序運行時,應當安裝語音命令文件。在App類中重寫的OnLaunched方法中加入安裝VCD文件的代碼。

            // 獲取安裝包中的VCD文件
            StorageFile vcd = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///vcd.xml"));
            // 安裝VCD文件
            await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcd);

注意using以下命名空間:
 Windows.Storage

 Windows.ApplicationModel.VoiceCommands - 操作VCD文件的API現已移到這里

 

當小娜成功識別語音命令后,會激活我們的高大上應用程序,這時候Application類的OnActived方法會被調用,我們在App類中應當重寫該方法,並處理語音命令識別。

        protected override void OnActivated(IActivatedEventArgs e)
        {
            // 如果程序不是因為語音命令而激活的,就不處理
            if (e.Kind != ActivationKind.VoiceCommand) return;

            VoiceCommandActivatedEventArgs vcargs = (VoiceCommandActivatedEventArgs)e;
            // 分析被識別的命令
            var res = vcargs.Result;

            /*
            輸出調試信息
            System.Text.StringBuilder strbd = new System.Text.StringBuilder();
            strbd.AppendLine("觸發識別的規則:");
            foreach (string ru in res.RulePath)
            {
                strbd.AppendFormat("\t{0}\n", ru);
            }
            strbd.AppendLine("\n語義屬性列表:");
            foreach (var kp in res.SemanticInterpretation.Properties)
            {
                strbd.AppendFormat("{0} - {1}\n", kp.Key, string.Join(", ", kp.Value.ToArray()));
            }
            System.Diagnostics.Debug.WriteLine("\n===============================\n" + strbd.ToString() + "/n==============================================");
            */

            // 獲取被識別的命令的名字
            string cmdName = res.RulePath[0];

                 ……

            if (cmdName == "color") //設置顏色
            {
                // 獲取被識別出來的短語項
                string coloritem = res.SemanticInterpretation.Properties["coloritem"][0];
                Color color = Colors.Black;
                switch (coloritem)
                {
                    case "紅色":
                        color = Colors.Red;
                        break;
                    case "藍色":
                        color = Colors.Blue;
                        break;
                    case "綠色":
                        color = Colors.Green;
                        break;
                    case "紫色":
                        color = Colors.Purple;
                        break;
                }
                uc.SetColor(color);
            }
            else if (cmdName == "align") //設置對齊方式
            {
                // 獲取被識別的短語
                string alignitem = res.SemanticInterpretation.Properties["alignitem"][0];
                HorizontalAlignment align = HorizontalAlignment.Left;
                switch (alignitem)
                {
                    case "":
                        align = HorizontalAlignment.Left;
                        break;
                    case "居中":
                        align = HorizontalAlignment.Center;
                        break;
                    case "":
                        align = HorizontalAlignment.Right;
                        break;
                }
                uc.SetAlignment(align);
            }

            Window.Current.Activate();
        }

識別結果由SpeechRecognitionResult類封裝,其中,被識別的語音命令的名字會存放到RulePath屬性中,它是一個只讀的字符串列表,一般來說,應用程序每次僅處理一條語音命令,所以只要訪問RulePath屬性的第一個元素就可以知道被識別的語音命令的名字。該命令名字是在VCD文件的Command元素的Name屬性上定義的。

要知道用戶說出了ListenFor元素所引用的PhraseList列表中的某個短語,可以訪問SpeechRecognitionResult對象的SemanticInterpretation.Properties 屬性值,它是一個字典集合,通過key的名字可以找出被識別的項。

這個key和VCD文件中PhraseList元素的Label屬性對應。比如用戶對着小娜說:“高大上 左對齊”,那么在VCD文件中定義對齊命令的PhraseList的Label值為alignitem,所以要訪問.SemanticInterpretation.Properties["alignitem"]來獲取,因為用戶說了“左對齊”,“對齊”是可選的,而“左”是在短語列表中的,所以.Properties["alignitem"]字典所返回的字符串列表中就包含一個“左”值。

對已識別的語音命令進行分析后,程序就要做出相應的處理了。就像本例中,用於修改文本顏色或設置文本的對齊方式。

 

接下來,可以運行一下“高大上”應用,當應用順利運行后,表明語音命令文件已經注冊。這時候可以對着小娜說話了,現在小娜是無處不在的,所以你不必要在手機上測試,只要你有話筒,在桌面上就可以開工。

比如,對着小娜講“高大上 紫色”,這時候小娜會響應,並且會把應用界面上的文本改為紫色。

 

接着,你可以試着對小娜說:“高大上 右對齊”,然后文本會設置為右對齊。

 

OK,今天的牛皮暫時吹到這里,明天如果有空,老周繼續吹語音命令相關的,下一篇爛文會說一說如何讓語音命令集成結合到App Service中使用。

示例源碼下載:http://files.cnblogs.com/files/tcjiaan/VoicecommandApp.zip

 

 

 

 

 

 

 

 

 

 

 


 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM