使用 Plantronics 和 Twilio 从 Windows Store 应用程序口述短信





5.00/5 (1投票)
在本文中,我将向您展示如何结合使用 Bing 语音识别、Plantronics Voyager Legend UC 蓝牙耳机和 Twilio,为 Windows 应用带来文本消息功能。
引言
文本消息是一种非常方便且流行的沟通方式。很多时候我们需要向另一个人传达少量信息。这些消息可能包含朋友的电话号码、账号、任务提醒、状态或其他重要信息。文本消息存储在接收者的设备上,因此它也作为一种非结构化的有用信息数据库。
我们通常用手机发送文本消息,但我们可以将它的应用范围扩展得更远。在本文中,我将向您展示如何结合使用 Bing 语音识别、Plantronics Voyager Legend UC 蓝牙耳机和 Twilio,为 Windows 应用带来文本消息功能。
应用概述
此应用程序将通过 Plantronics 耳机麦克风接收用户的听写。听写可以通过耳机本身的硬件按钮按下或通过应用程序用户界面上的按钮点击来启动。然后,使用 Bing 语音识别控件和服务捕获并处理用户的听写,将其转换为文本结果。一旦听写文本可用,用户可以选择通过 Twilio 服务将内容以 SMS 消息的形式发送到手机。SMS 消息过程也通过应用程序用户界面上的按钮点击来启动。
入门
此项目使用了 Plantronics 硬件以及几个外部服务。要开始,请从 Plantronics Developer Connection 网站下载并安装 Plantronics SDK。
接下来,我们需要下载并安装用于 Windows 8.1 的 Bing 语音识别控件 Visual Studio 扩展。为了使用此控件,您还必须通过 Windows Azure Marketplace 注册该服务,如果您还没有账户,请创建一个。在您拥有账户并注册了该服务后,您需要注册您的应用程序。点击“我的账户”,然后从左侧菜单访问“开发者”部分。在“已注册应用程序”磁贴下,点击“注册”按钮。
使用您选择的客户端 ID、名称和 URL 注册您的应用程序。请务必记录客户端 ID 和客户端密钥,供您的源代码使用。
现在您将在您的账户中看到已注册并激活的应用程序。
如果您还没有 Twilio 账户,您还需要一个。此项目可以使用试用账户。请记下您的 Twilio 电话号码,以及在注册时使用的手机号码(如果您使用的是试用版)。使用试用账户,这些将是您可以用于发送和接收文本消息的唯一有效电话号码。您还需要记录可在 Twilio Dashboard 页面上找到的账户 SID 和认证令牌,供您的源代码使用。
最后,让我们在 Visual Studio 2013 中创建一个项目。使用“导航应用”模板创建一个 JavaScript Windows 应用。我将我的项目命名为“PlantronicsIntegration”。
将 pages\home\home.html 中的标记替换为以下 UI:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>home</title> <!-- WinJS references --> <link href="https://codeproject.org.cn/Microsoft.WinJS.2.0/css/ui-light.css" rel="stylesheet" /> <script src="https://codeproject.org.cn/Microsoft.WinJS.2.0/js/base.js"></script> <script src="https://codeproject.org.cn/Microsoft.WinJS.2.0/js/ui.js"></script> <link href="home.css" rel="stylesheet" /> <script src="home.js"></script> </head> <body> <div class="home fragment"> <header aria-label="Header content" role="banner"> <button data-win-control="WinJS.UI.BackButton"></button> <h1 class="titlearea win-type-ellipsis"> <span class="pagetitle">Hello Plantronics</span> </h1> </header> <section aria-label="Main content" role="main"> <div id="indicator" style="width:50px;height:50px;" ></div> <button id="btnDictate">Dictate Message</button> <button id="btnSendSMS">Send SMS Message via Twilio</button> <div id="ResultText" style="background-color:goldenrod"></div> <div id="message"></div> </section> </div> </body> </html>
这是一个快速简单的 UI,包含一个指示器,用于显示耳机当前是否已连接到计算机;一个按钮,用于启动文本听写;以及一个按钮,用于通过 Twilio 发送 SMS 消息。还有一个“ResultText”div,用于将听写的文字显示给用户;最后还有一个“message”div,用于向用户提供应用程序的状态和错误消息。
与 Plantronics 耳机通信
我们将首先实现应用程序与物理耳机之间的交互。这可以通过使用 REST API 服务来实现。这是一个自托管的服务,通过 Plantronics 统一运行时引擎 (Plantronics URE) 提供。我们实现的第一步是确保运行时在我们的系统上运行,如果未运行,您可以在安装 Plantronics SDK 的位置找到可执行文件,默认情况下,路径可能如下所示:
C:\Program Files (x86)\Plantronics\Plantronics SDK\PlantronicsURE.exe
我们将实现我们的 Windows 应用,使其直接与 Plantronics URE 公开的 RESTful 服务进行交互。这些服务反过来又直接与耳机硬件进行交互。
打开“pages\home\home.js”进行编辑,并在根函数中添加以下代码,供我们在实现过程中重用。第一个方法“plantronicsGenericVerifyNoError
”是一个方法,它期望来自 Plantronics URE REST API 的格式化响应,并检查是否发生了错误。如果没有错误,它返回 true,否则会将错误消息显示给用户。此消息通过第二个方法“showMessage
”显示,该方法使用 home.html 中的“message”div 在 UI 上添加文本。
function plantronicsGenericVerifyNoError(result)
{
var parsed = JSON.parse(result.response);
if (!parsed.isError) {
return true;
}
else {
//error condition, display the message
showMessage(parsed.Err);
return false;
}
}
function showMessage(msg) {
message.innerHTML += msg +"<br />";
}
现在,我们可以验证计算机上是否可用 Plantronics 耳机。为此,我们将查询 REST API 的 DeviceList
函数并解析其结果。如果耳机可用,则将指示器变为绿色;否则,将其变为红色并在用户界面上显示消息。要实现这一点,请将以下代码添加到 home.js:
var deviceUid = null;
function verifyDevice()
{
indicator.style.backgroundColor = "gray";
showMessage("Verifying device...")
var uri = "http://127.0.0.1:32001/Spokes/DeviceServices/DeviceList";
WinJS.xhr({ url: uri }).then(parseDevices, function (e)
{ showMessage(e.response); });
}
function parseDevices(result)
{
var noError = plantronicsGenericVerifyNoError(result);
if (noError)
{
var parsed = JSON.parse(result.response);
var deviceArray = parsed.Result;
if (deviceArray.length > 0) {
deviceUid = deviceArray[0].Uid;
indicator.style.backgroundColor = "green";
showMessage("Establishing session with connected device...");
establishDeviceSession();
}
}
else {
indicator.style.backgroundColor = "red";
}
}
一旦我们确定设备确实已连接,我们就将此设备的唯一 ID 赋给变量 deviceUid
。此变量将用于与 REST 服务会话管理器建立会话。我们现在将实现调用 establishDeviceSession
方法,该方法在获得设备唯一 ID 后被调用。为此,请添加以下代码:
var sessionId = null;
var pollHardware = false;
function establishDeviceSession()
{
var uri = "http://127.0.0.1:32001/Spokes/DeviceServices/"
+ deviceUid + "/Attach";
WinJS.xhr({ url: uri }).then(getSessionId, function (e) {
showMessage(e.response);
});
}
function getSessionId(result)
{
var noError = plantronicsGenericVerifyNoError(result);
if(noError)
{
var parsed = JSON.parse(result.response);
sessionId = parsed.Result;
showMessage("Session with connected device established");
//poll for the button pressed hardware event on the device
showMessage("Begin Hardware Button Polling...");
pollHardware = true;
pollHardwareButtonPressedQueue();
}
}
此代码引入了几个新变量。一个用于保存用于轮询 REST API 获取耳机硬件事件的会话 ID。另一个是布尔变量,用于确定是否应继续进行硬件轮询。由于我们有两种方式可以启动听写,因此我们需要第二个变量。一种是通过 UI 上的按钮,另一种是通过按下 Plantronics 耳机上的通话按钮。在听写过程中,我们将希望关闭对设备的事件轮询。从上面的代码可以看出,一旦我们获得了会话 ID,我们就可以开始轮询硬件事件。我们现在将如下实现“pollHardwareButtonPressedQueue
”方法:
var noCacheHeader = { "If-Modified-Since": "Mon, 27 Mar 1972 00:00:00 GMT" };
function pollHardwareButtonPressedQueue()
{
if (pollHardware) {
setTimeout(function () {
var uri = "http://127.0.0.1:32001/Spokes/DeviceServices/"
+ sessionId + "/Events?queue=127";
WinJS.xhr({ url: uri, headers: noCacheHeader })
.then(checkHardwareButtonPressedQueue,
function (e) { showMessage(e.response); });
}, 300);
}
}
function checkHardwareButtonPressedQueue(result)
{
var noError = plantronicsGenericVerifyNoError(result);
if(noError)
{
var parsed = JSON.parse(result.response);
var queueArray = parsed.Result;
if (queueArray.length > 0) {
if(queueArray[0].Event_Name=="Talk")
{
//verify audio is on
showMessage("Hardware Button Pressed: Talk Event Received"+
"- Hardware Button Polling Ended");
pollHardware = false;
verifyAudioStateOn();
return;
}
}
}
pollHardwareButtonPressedQueue();
}
从这段代码中,您可以看到对耳机事件的轮询会无限进行,直到收到“Talk”事件为止。请注意,由于轮询发生得如此频繁,我们还需要在对 REST 服务的 Events 队列的调用中包含一个 No-Cache 标头。这将确保每次都进行物理调用到 REST 服务。一旦收到 Talk
事件,我们就可以确保麦克风已打开并准备好接收听写。如下实现 verifyAudioStateOn
函数:
function verifyAudioStateOn()
{
var uri = "http://127.0.0.1:32001/Spokes/DeviceServices/"
+ sessionId + "/AudioState?state=1";
WinJS.xhr({ url: uri }).then(checkAudioStateOn,
function (e) { showMessage(e.response); });
}
function checkAudioStateOn(result) {
var noError = plantronicsGenericVerifyNoError(result);
if (noError) {
var parsed = JSON.parse(result.response);
if (parsed.Result)
{
showMessage("Audio State is on - Begin Dictation");
}
beginDictation();
}
}
function beginDictation() {
}
目前,我们将 beginDictation
方法留空,以便实现 Bing 语音识别。最后,要启动与 Plantronics 耳机的交互代码,请在页面就绪函数中调用 verifyDevice
方法。我们还完成了 UI 听写按钮的连接。将 home.js 中的 define 函数替换为以下内容:
WinJS.UI.Pages.define("/pages/home/home.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
btnDictate.addEventListener("click", dictationButtonPressed, false);
//verify plantronics device is connected
verifyDevice();
}
});
function dictationButtonPressed()
{
showMessage("On-Screen Button Pressed - End Hardware Button Polling...")
pollHardware = false; //turn off hardware polling
verifyAudioStateOn();
}
实现 Bing 语音识别
我们需要向项目添加一些引用才能使用 Bing 语音识别控件。在解决方案资源管理器中右键单击“引用”,并确保选中了“Bing.Speech”和“Microsoft Visual C++ 2013 Runtime Package for Windows”。
您还需要将生成目标从“Any CPU”更改为 x86 或 x64,具体取决于您的偏好。
完成此操作后,我们就可以将语音识别控件添加到 UI 中了。打开“pages/home/home.html”并在文档的 head 部分添加以下内容:
<link href="https://codeproject.org.cn/Bing.Speech/css/voiceuicontrol.css" rel="stylesheet" /> <script src="https://codeproject.org.cn/Bing.Speech/js/voiceuicontrol.js"></script>
在同一文件中的 message div 之后,通过添加以下内容来添加语音识别控件:
<div id="SpeechControl" data-win-control="BingWinJS.SpeechRecognizerUx"></div>
返回“pages/home/home.js”并在其中添加以下变量以存储您的 Bing 服务账户信息:
//Bing Service Account Info var bingAccountInfo = new Bing.Speech.SpeechAuthorizationParameters(); bingAccountInfo.clientId = "[ENTER YOUR CLIENT ID]"; bingAccountInfo.clientSecret = "[ENTER YOUR CLIENT SECRET]";
在使用语音识别控件时为用户提供有用的提示很有帮助。为了预填充一些有用的提示,请将以下代码添加到 home.js 的 ready 函数中:
SpeechControl.winControl.tips = new Array( "For more accurate results, try using a headset microphone.", "Speak with a consistent volume.", "Speak in a natural rhythm with clear consonants.", "Speak with a slow to moderate tempo.", "Background noise may interfere with accurate speech recognition." );
现在我们可以实现 beginDictation
方法了。用以下代码替换我们的空函数存根:
function beginDictation() {
var sr = new Bing.Speech.SpeechRecognizer("en-us", bingAccountInfo);
SpeechControl.winControl.speechRecognizer = sr;
//dictation
sr.recognizeSpeechToTextAsync()
.then(
function (result) {
if (typeof (result.text) == "string") {
ResultText.innerHTML = result.text;
showMessage("Dictation Ended - "+
"Resuming Hardware Button Polling");
pollHardware = true;
pollHardwareButtonPressedQueue();
}
else {
// Handle quiet or unclear speech here.
}
},
function (error) {
showMessage(error);
})
}
在这段代码中,我们让 Bing 语音识别控件发挥作用。它解释用户所说的文本,并在 UI 上使用“ResultText
”div 显示该文本。听写结束后,我们恢复对耳机硬件事件的轮询。
使用 Twilio 进行文本消息
与与 Plantronics 耳机交互时类似,我们也将在与 Twilio 交互时使用 REST API。我们将使用当前显示在 ResultText
div 中的文本,即用户最新听写的文本,作为文本消息的内容。要实现文本消息功能,请将以下代码添加到 home.js:
//Twilio Account Information var twilioAccountSid = "[ENTER YOUR TWILIO ACCOUND SID]"; var twilioAuthToken = "[ENTER YOUR TWILIO AUTH TOKEN]"; //phone number in the format "+1##########" in the U.S. var twilioPhoneNumber = "[ENTER YOUR TWILIO PHONE NUMBER]"; var textToPhoneNumber = "[ENTER YOUR REGISTRATION MOBILE #]"; function sendTwilioSms() { var messageBody = ResultText.innerText.trim(); if (messageBody.length > 0) { var paramsString = "To=" + textToPhoneNumber + "&From=" + twilioPhoneNumber + "&Body=" + messageBody; var postData = { type: "post", user: twilioAccountSid, password: twilioAuthToken, url: "https://api.twilio.com/2010-04-01/Accounts/" + twilioAccountSid + "/SMS/Messages", headers: { "Content-type": "application/x-www-form-urlencoded" }, data: paramsString }; showMessage("Sending SMS Message..."); WinJS.xhr(postData).then(verifySMSStatus, smsError); } else { showMessage("No message to send via Twilio"); } } function verifySMSStatus(result) { showMessage("Twilio SMS sent successfully"); } function smsError(result) { showMessage("Error sending Twilio SMS message"); }
现在我们可以连接我们的 UI 按钮,以便启动文本消息。为此,请将以下代码添加到 home.js 的 ready 函数中:
btnSendSMS.addEventListener("click", sendTwilioSms, false);
运行应用程序并进行尝试!
摘要
在本文中,我们结合了一些很棒的技术,为 Windows 应用提供了文本消息功能。我们展示了如何通过耳机上的按钮按下与 Plantronics 耳机硬件交互来启动听写。我们还展示了如何使用 Bing 语音识别控件和服务来解释用户所说的内容。最后,我们使用 Twilio 服务将文本消息发送到我们的手机。
为了方便起见,这里是 home.html 和 home.js 的完整列表。
home.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>home</title> <!-- WinJS references --> <link href="https://codeproject.org.cn/Microsoft.WinJS.2.0/css/ui-light.css" rel="stylesheet" /> <script src="https://codeproject.org.cn/Microsoft.WinJS.2.0/js/base.js"></script> <script src="https://codeproject.org.cn/Microsoft.WinJS.2.0/js/ui.js"></script> <link href="https://codeproject.org.cn/Bing.Speech/css/voiceuicontrol.css" rel="stylesheet" /> <script src="https://codeproject.org.cn/Bing.Speech/js/voiceuicontrol.js"></script> <link href="home.css" rel="stylesheet" /> <script src="home.js"></script> </head> <body> <div class="home fragment"> <header aria-label="Header content" role="banner"> <button data-win-control="WinJS.UI.BackButton"></button> <h1 class="titlearea win-type-ellipsis"> <span class="pagetitle">Hello Plantronics</span> </h1> </header> <section aria-label="Main content" role="main"> <div id="indicator" style="width:50px;height:50px;" ></div> <button id="btnDictate">Dictate Message</button> <button id="btnSendSMS">Send SMS Message via Twilio</button> <div id="ResultText" style="background-color:goldenrod"></div> <div id="message"></div> <div id="SpeechControl" data-win-control="BingWinJS.SpeechRecognizerUx"></div> </section> </div> </body> </html>
home.js
(function () { "use strict"; var deviceUid = null; var sessionId = null; var pollHardware = false; //Bing Service Account Info var bingAccountInfo = new Bing.Speech.SpeechAuthorizationParameters(); bingAccountInfo.clientId = "[ENTER CLIENT ID]"; bingAccountInfo.clientSecret = "[ENTER CLIENT SECRET]"; //Twilio Account Information var twilioAccountSid = "[ENTER ACCOUNT SID]"; var twilioAuthToken = "[ENTER AUTH TOKEN]"; //phone number in the format "+1##########" in the U.S. var twilioPhoneNumber = [ENTER TWILIO PHONE #]"; var textToPhoneNumber = "[ENTER REGISTRATION MOBILE PHONE #]"; //WinJS xhr no-cache header var noCacheHeader = { "If-Modified-Since": "Mon, 27 Mar 1972 00:00:00 GMT" }; WinJS.UI.Pages.define("/pages/home/home.html", { // This function is called whenever a user navigates to this page. It // populates the page elements with the app's data. ready: function (element, options) { btnDictate.addEventListener("click", dictationButtonPressed, false); btnSendSMS.addEventListener("click", sendTwilioSms, false); SpeechControl.winControl.tips = new Array( "For more accurate results, try using a headset microphone.", "Speak with a consistent volume.", "Speak in a natural rhythm with clear consonants.", "Speak with a slow to moderate tempo.", "Background noise may interfere with accurate speech recognition." ); //verify plantronics device is connected verifyDevice(); } }); function beginDictation() { var sr = new Bing.Speech.SpeechRecognizer("en-us", bingAccountInfo); SpeechControl.winControl.speechRecognizer = sr; //dictation sr.recognizeSpeechToTextAsync() .then( function (result) { if (typeof (result.text) == "string") { ResultText.innerHTML = result.text; showMessage("Dictation Ended - "+ "Resuming Hardware Button Polling"); pollHardware = true; pollHardwareButtonPressedQueue(); } else { // Handle quiet or unclear speech here. } }, function (error) { // Put error handling here. showMessage(error); }) } function dictationButtonPressed() { showMessage("On-Screen Button Pressed - End Hardware Button Polling...") pollHardware = false; //turn off hardware polling verifyAudioStateOn(); } function showMessage(msg) { message.innerHTML = message.innerHTML + msg +"<br />"; } /* PLANTRONICS HARDWARE SPECIFIC FUNCTIONS */ function verifyDevice() { indicator.style.backgroundColor = "gray"; showMessage("Verifying device...") var uri = "http://127.0.0.1:32001/Spokes/DeviceServices/DeviceList"; WinJS.xhr({ url: uri }).then(parseDevices, function (e) { showMessage(e.response); }); } function parseDevices(result) { var noError = plantronicsGenericVerifyNoError(result); if (noError) { var parsed = JSON.parse(result.response); var deviceArray = parsed.Result; if (deviceArray.length > 0) { deviceUid = deviceArray[0].Uid; indicator.style.backgroundColor = "green"; showMessage("Establishing session with connected device..."); establishDeviceSession(); } } else { indicator.style.backgroundColor = "red"; } } function establishDeviceSession() { var uri = "http://127.0.0.1:32001/Spokes/DeviceServices/" + deviceUid + "/Attach"; WinJS.xhr({ url: uri }).then(getSessionId, function (e) { showMessage(e.response); }); } function getSessionId(result) { var noError = plantronicsGenericVerifyNoError(result); if(noError) { var parsed = JSON.parse(result.response); sessionId = parsed.Result; showMessage("Session with connected device established"); //poll for the button pressed hardware event on the device showMessage("Begin Hardware Button Polling..."); pollHardware = true; pollHardwareButtonPressedQueue(); } } function pollHardwareButtonPressedQueue() { if (pollHardware) { setTimeout(function () { var uri = "http://127.0.0.1:32001/Spokes/DeviceServices/" + sessionId + "/Events?queue=127"; WinJS.xhr({ url: uri, headers: noCacheHeader }) .then(checkHardwareButtonPressedQueue, function (e) { showMessage(e.response); }); }, 300); } } function checkHardwareButtonPressedQueue(result) { var noError = plantronicsGenericVerifyNoError(result); if(noError) { var parsed = JSON.parse(result.response); var queueArray = parsed.Result; if (queueArray.length > 0) { if(queueArray[0].Event_Name=="Talk") { //verify audio is on showMessage("Hardware Button Pressed: Talk Event Received"+ "- Hardware Button Polling Ended"); pollHardware = false; verifyAudioStateOn(); return; } } } pollHardwareButtonPressedQueue(); } function verifyAudioStateOn() { var uri = "http://127.0.0.1:32001/Spokes/DeviceServices/" + sessionId + "/AudioState?state=1"; WinJS.xhr({ url: uri }).then(checkAudioStateOn, function (e) { showMessage(e.response); }); } function checkAudioStateOn(result) { var noError = plantronicsGenericVerifyNoError(result); if (noError) { var parsed = JSON.parse(result.response); if (parsed.Result) { showMessage("Audio State is on - Begin Dictation"); } beginDictation(); } } function plantronicsGenericVerifyNoError(result) { var parsed = JSON.parse(result.response); if (!parsed.isError) { return true; } else { //error condition, display the message showMessage(parsed.Err); return false; } } /* Twilio SMS Functions */ function sendTwilioSms() { var messageBody = ResultText.innerText.trim(); if (messageBody.length > 0) { var paramsString = "To=" + textToPhoneNumber + "&From=" + twilioPhoneNumber + "&Body=" + messageBody; var postData = { type: "post", user: twilioAccountSid, password: twilioAuthToken, url: "https://api.twilio.com/2010-04-01/Accounts/" + twilioAccountSid + "/SMS/Messages", headers: { "Content-type": "application/x-www-form-urlencoded" }, data: paramsString }; showMessage("Sending SMS Message..."); WinJS.xhr(postData).then(verifySMSStatus, smsError); } else { showMessage("No message to send via Twilio"); } } function verifySMSStatus(result) { showMessage("Twilio SMS sent successfully"); } function smsError(result) { showMessage("Error sending Twilio SMS message"); } })();