制作一个从外部文件读取命令的 Jarvis
制作一个从外部文件读取命令的 Jarvis
本文解决了语音识别程序员在处理包含数十个单词或短语的语法时面临的问题;它加载两个外部文本文件:一个用于命令列表,另一个用于根据命令执行的操作。
背景
本文理想地集成了任何语音识别文章(例如 MSDN 目录或 C.P. 中存在的文章)。
使用代码
本文使用了自 Windows 7 Enterprise(某些版本)以来提供的 System.Speech 功能,并在 Windows 8.x 中进行了改进;SpeechRecognitionEngine 用于根据基于外部文件构建的语法识别口语。
本文使用 VB.NET 处理该问题(该项目是使用 Visual Basic 2010 XE 创建的)。
程序中的第一步是导入我们在编程过程中需要的功能。
Imports System.Speech
Imports System.Speech.Recognition
Imports System.Runtime.InteropServices
Imports System.IO
Imports System.Net
请注意,System.Speech 也应添加为引用。
主类包含几乎所有代码,分为两个部分:'_Load
' 和 '_SpeechRecognized
' 事件。
在编写这两个主要事件之前,我们需要通过全局变量完成一些设置,并且需要实现识别引擎。
Public Class Vera
Dim WithEvents reco As New Recognition.SpeechRecognitionEngine
Dim commandset() As String
Dim cmdList As New GrammarBuilder
在 Load
事件中,我们添加了处理命令集、语法以及对命令的识别/响应的所有代码。
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim npath As String = Application.StartupPath & "\commandlist.txt"
Dim nsr As StreamReader = File.OpenText(npath)
Dim i As Integer
Dim ls As String
For Each ls In File.ReadLines(npath)
ReDim Preserve commandset(i)
commandset(i) = ls
i += 1
ListBox1.Items.Add(ls)
Application.DoEvents()
Next
reco.SetInputToDefaultAudioDevice()
cmdList.Append(New Choices(commandset))
reco.LoadGrammar(New Recognition.Grammar(cmdList))
reco.RecognizeAsync()
End Sub
如代码所示,表单必须包含一个 ListBox,其中将加载所有命令以供参考;这避免了将所有命令保存在内存中或打开命令列表文件。
commandlist.txt 文件仅包含命令,每行一个,没有空格或空行,例如:
hello computer what is the timer? What is your name? Open chrome Go full screen […]
下一步是设置计算机在识别出包含在 *commandlist.txt* 文件中的命令时必须执行的操作;这在 Reco 对象的 _SpeechRecognized
事件中实现。
实际上,在本示例中,我们使用基于两个事件的方法:RecognizeComplete
和 SpeechRecognized
。
第一个只是告诉计算机异步启动另一个识别。
Private Sub reco_RecognizeCompleted(ByVal sender As Object, ByVal e As System.Speech.Recognition.RecognizeCompletedEventArgs) Handles reco.RecognizeCompleted
reco.RecognizeAsync()
End Sub
第二个将处理所有命令/操作,并将使用 Reco
对象的 SpeechRecognized
事件。这段长代码是程序的“核心”。
在本示例中,我们提供了一种简单但非常有用的方法,该方法基于以下列方式构造的外部文件。
command^action1^commandtype^action2
例程生成的随机数将用于随机允许计算机执行 action1 或 action2;这对于社交命令特别有用,我们可能希望将更多答案与同一命令关联。
这是文件的一个示例(命名为:commandactionlist.txt)
hello vera^Hello creator, how are you?^social^Good morning master, I am ready to operate honey are you there?^of course I am... where else should i be?^social^Here and running where are you vera?^come find me, creator!^social^That is a very stupid question! I am trapped in this goddamn computer open my computer^explorer.exe^comando^noaction open chrome^chrome.exe^comando^noaction close chrome^chrome.exe^comando^noaction open wordpad^wordpad.exe^comando^noaction navigate to facebook^www.facebook.com^website^noaction navigate to hotmail^www.hotmail.com^website^noaction navigate to twitter^http://twitter.com^website^noaction show commands^noaction^internal^noaction hide commands^noaction^internal^noaction go full screen^noaction^internal^noaction goodbye^It's been a pleasure,^internal^noaction
从代码可以看出,在这种方法中,我们在字符串(commandtype)的第 3rd 个位置使用了四种可能的选择:social、comando、website、internal。
“social”命令类型将仅用于与计算机聊天,计算机不执行任何实际操作,因此 action1 和 action2 将是计算机回答口语命令的 2 个可能的句子;“comando”命令类型将用于处理 windows 已安装程序上的操作,例如在示例中打开写字板等,在这种情况下,我们只需要 action1,因此 action2 设置为中性值;“website”命令类型在形式上与“comando”类型相同,只不过我们传递的是网站地址而不是 exe 文件;“internal”命令类型用于对程序本身进行操作,在上面的例子中,我们使用它来显示和隐藏命令列表框,并将表单设置为全屏。
现在让我们首先看看随机生成。
Public Function getrandom(ByVal min As Integer, ByVal max As Integer) As Integer
Static generator As System.Random = New System.Random()
Return generator.Next(min, max)
End Function
现在我们可以处理四种命令类型。请注意,由于该程序具有社交功能,因此它使用语音合成引擎与用户交互。
Private Sub reco_SpeechRecognized(ByVal sender As Object, ByVal e As System.Speech.Recognition.RecognitionEventArgs) Handles reco.SpeechRecognized
Dim response As String = ""
Dim synth As New Synthesis.SpeechSynthesizer
Dim npath As String = Application.StartupPath & "\commandactionlist.txt"
Dim nsr As StreamReader = File.OpenText(npath)
Dim ls As String
以上部分使用与命令列表相同的过程来加载命令/操作文件;这将使用 ^ 作为分隔符将其组件拆分为一个数组进行解析,并且将应用随机数生成来决定必须执行什么操作(这仅对“社交”命令有效)。
Dim params(3) As String
Dim execute = e.Result.Text.ToLower
Dim answer As String = ""
For Each ls In File.ReadLines(npath)
Dim value As Integer = getrandom(0, 6)
params = ls.Split("^"c)
Dim Command As String = params(0).ToLower
Dim comtype As String = params(2)
If comtype = "social" Then
If value <= 3 Then
response = params(1)
ElseIf value > 3 Then
response = params(3)
End If
Else
response = params(1)
End If
Dim Action As String = response
If execute.Contains(Command) And comtype = "social" Then
Dim robotvoice = CreateObject("sapi.spvoice")
answer = Action
robotvoice.Speak(answer)
ElseIf execute.Contains(Command) And comtype = "comando" Then
Dim robotvoice = CreateObject("sapi.spvoice")
If execute.Contains("open") Then
answer = "Opening application"
robotvoice.Speak(answer)
Process.Start(Action)
ElseIf execute.Contains("close") Then
answer = "Closing application"
robotvoice.Speak(answer)
For Each myprocess As Process In Process.GetProcessesByName(Action)
myprocess.CloseMainWindow()
Next
ElseIf execute.Contains(Command) And comtype = "website" Then
Try
If My.Computer.Network.IsAvailable = True Then
answer = "Website is opening in a while"
robotvoice.Speak(answer)
Process.Start(Action)
Else
answer = "It looks like there is no Internet connection available"
robotvoice.Speak(answer)
End If
Catch ex As Exception
End Try
End If
ElseIf execute.Contains(Command) And comtype = "internal" Then
Dim robotvoice = CreateObject("sapi.spvoice")
answer = "Ok Creator"
robotvoice.Speak(answer)
If Command = "show commands" Then
ListBox1.Visible = True
ElseIf execute.Contains("goodbye") Then
answer = Action
robotvoice.Speak(answer)
reco.UnloadAllGrammars()
reco.RecognizeAsyncStop()
reco.Dispose()
Me.Close()
ElseIf Command = "hide commands" Then
ListBox1.Visible = False
ElseIf Command = "go full screen" Then
Me.WindowState = FormWindowState.Maximized
End If
Application.DoEvents()
End If
Next
End Sub
最后一步当然是关闭类。
End Class
关注点
编写此代码的主要兴趣点是当我尝试使用不同的表单使程序处理更复杂的操作(例如处理媒体文件)时。困扰我的一个功能是,当程序说长句子(15 个或更多单词)时,程序会停止所有其他操作。例如,我的程序还包含一个每秒刷新一次的时钟,以及一个“演示”内部命令,使 PC 说“我的名字是 Vera,我是一个语音识别软件,可以处理不同类型的命令并进行社交互动”。当你要求电脑自我介绍时,时钟就会停止。
我将研究这个问题,并希望找到解决方案。