VoiceGap

Filling the space between You and Your Apps

Adding Text To Speech (TTS) to VoiceGap|Android

Now that we can listen to  what is being said, it might be nice to use verbal prompts to queue the dialogs. But first, we really need to do some cleaning.  Let’s start with the CSS.

<style type="text/css">
#micButton, #searchField {
padding: 2px;
align: center;
}
input[type=button] {
width: 100%;
}
img {
display:block;
margin-left:auto;
margin-right:auto;
}
p {
display:block;
margin-left:auto;
margin-right:auto;
width: 80%;
}
</style>

Expendable.css

Let’s Call it Expendable.css
Then we can put all the control code including init() into a file called Expendable.js
That leaves us with a fairly clean index.html file.

<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=320; user-scalable=no" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>PhoneGap</title>
<link rel="stylesheet" href="jquery.mobile-1.0.1.min.css" />
<link rel="stylesheet" href="Expendable.css" />
<script type="text/javascript" charset="utf-8" src="js/phonegap-1.1.0.js"></script>
<script type="text/javascript" charset="utf=8" src="js/jquery-1.6.4.js"></script>
<script type="text/javascript" charset="utf-8" src="js/SpeechRecognizer.js"></script>
<script type="text/javascript" charset="utf=8" src="js/jquery.mobile-1.0.1.min.js"></script>
<script type="text/javascript" charset="utf=8" src="Expendable.js"></script>
</head>
<body onload="init()">

	<div data-role="page" class="type-interior">
		<div data-role="header" data-theme="a" data-position="fixed">
		<h1>Capt. Expendable</h1>
		<a href="../../" data-icon="home" data-iconpos="notext" data-direction="reverse" class="ui-btn-right
jqm-home">Home</a>
	</div>

<input type="search" id="searchField" value="" width="40">
<a href="#" id="micButton"><img src="Expendable.png" width="200" height="150" /></a>

<div id="container">

    <div id="tab-1">
      <h3>Please tap on Capt. Expendable and ask him to search for something</h3>
      <p> </p>
    </div>
    <div id="tab-2">
      <h3>Tab 2</h3>
      <p> </p>
    </div>
    <div id="tab-3">
      <h3>Search Results</h3>
    </div>
  </div>

<div data-role="footer" data-id="foo1" data-position="fixed">
			<div data-role="navbar" id="tabs">
				<ul id="foot">

					<li><a href="#tab-1" id="bar-1" >Think</a></li>
					<li><a href="#tab-2" id="bar-2">Say</a></li>
					<li><a href="#tab-3" id="bar-3" >Act</a></li>
				</ul>
			</div><!-- /navbar -->
		</div><!-- /footer -->
		</div><!-- /page -->
   </body>
</html>

There is a great How-To for the TTS, that is definitely worth reading. You can find it here. http://www.raymondcamden.com/index.cfm/2011/10/12/First-test-of-PhoneGap-Plugins

If TTS would speak a prompt, would you be able to hear it in a moving vehicle or noisy location? To help counter that the middle tab was supposed to collect all things said by Capt. Expendable. That is why it is labeled “Say,” but that will have to be delayed. First you will need the TTS plugin though!

<script type="text/javascript" charset="utf-8" src="js/tts.js"></script>

Then we need to make a lot of changes to Expendable.js where all the control code it.


function deviceready() {
    console.log('loaded');

    window.plugins.speechrecognizer.init(speechInitOk, speechInitFail);
    window.plugins.tts.startup(doSpeak, errHandler2);

    
    function doSpeak() {
       window.plugins.tts.speak("The TTS service is ready", nothing , errHandler);
    }
    function nothing() {}
    function errHandler2(result){
        alert('Unable to Start Speech');
    } 
    function errHandler(result){
        alert('Speech Error: ' + result);
    } 
        function speechInitOk() {
        $("#micButton").removeAttr("disabled");
    }
    
    function speechInitFail(e) {
        //Since this isn't critical, we don't care...
    }
    
    $("#micButton").bind("touchstart", function() { 
        var promptString = "Where do you want to go?"; 
        window.plugins.tts.speak(promptString, startListen , errHandler);      
    });

    function startListen() {
        var requestCode = 4815162342;
        var maxMatches = 1;
        var promptString = "Where do you want to go?";

        window.plugins.speechrecognizer.startRecognize(speechOk, speechFail, requestCode, maxMatches, promptString)
    }
    function speechOk(result) {
        var match, respObj;
        if (result) {
            respObj = JSON.parse(result);
            if (respObj) {
                var response = respObj.speechMatches.speechMatch[0];
                $("#searchField").val(response);
                work();
            }        
        }
    }

    function speechFail(m) {
        navigator.notification.alert("Sorry, I couldn't recognize you.", function() {}, "Speech Fail");
    }
}

function work() {
        var s = $.trim($("#searchField").val());
        $.getJSON("http://api.search.live.net/json.aspx?Appid="+appid+"&query="+escape(s)+"&sources=image&image.count=20", {}, function(res) {
            var results = res.SearchResponse.Image.Results;
            if(results.length == 0) {
                $("#tab-3").html("No results!");
                return;
            }
            var s = "<hr/><h3>Search Results</h3>";
for(var i=0; i<results.length; i++) {
     s+= "<p><img src='"+results[i].Thumbnail.Url+"'><br/>";
     s+= "<a href='"+results[i].Url+"'>";
     s+=  results[i].DisplayUrl+"</a></p>";
            }
            $("#tab-3").html(s);
            $("#bar-3").click();
            $("#foot").listview('refresh');
        });
}
 
function init() {
    document.addEventListener("deviceready", deviceready, true);
 
  $.ajaxSetup({"error":function(XMLHttpRequest,textStatus, errorThrown) {
      alert(textStatus);
      alert(errorThrown);
      alert(XMLHttpRequest.responseText);
  }});
 
jQuery.support.cors = true;
 
}

$(document).ready(function(){
$('#container div').hide();
$('#container div:first').show();
$('#tabs ul li:first').addClass('active');
$('#tabs ul li a:first').addClass('ui-btn-active');
$('#tabs ul li a').click(function(){
$('#tabs ul li').removeClass('active');
$('#tabs ul li a').removeClass('ui-btn-active');
$(this).parent().addClass('active');
$(this).addClass('ui-btn-active');
var currentTab = $(this).attr('href');
$('#container div').hide();
$(currentTab).show();
return false;
});
});

Expendable.js

Well, That about does it. Except on my system I get “The TTS service is ready” “The TTS service is ready” spoken twice. I will look into that another day.

Since tts.js was not in the original batch of items I am including it here. Don’t worry, the TTS Java Class was already included and in the plugin.xml file was updated. So you just need the missing tts.js contents below.

/*
 * PhoneGap is available under *either* the terms of the modified BSD license *or* the
 * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
 * 
 * Copyright (c) 2011, IBM Corporation
 */

/**
 * Constructor
 */
function TTS() {
}

TTS.STOPPED = 0;
TTS.INITIALIZING = 1;
TTS.STARTED = 2;

/**
 * Play the passed in text as synthasized speech
 * 
 * @param {DOMString} text
 * @param {Object} successCallback
 * @param {Object} errorCallback
 */
TTS.prototype.speak = function(text, successCallback, errorCallback) {
     return PhoneGap.exec(successCallback, errorCallback, "TTS", "speak", 1);
};

/** 
 * Play silence for the number of ms passed in as duration
 * 
 * @param {long} duration
 * @param {Object} successCallback
 * @param {Object} errorCallback
 */
TTS.prototype.silence = function(duration, successCallback, errorCallback) {
     return PhoneGap.exec(successCallback, errorCallback, "TTS", "silence", [duration]);
};

/**
 * Starts up the TTS Service
 * 
 * @param {Object} successCallback
 * @param {Object} errorCallback
 */
TTS.prototype.startup = function(successCallback, errorCallback) {
     return PhoneGap.exec(successCallback, errorCallback, "TTS", "startup", []);
};

/**
 * Shuts down the TTS Service if you no longer need it.
 * 
 * @param {Object} successCallback
 * @param {Object} errorCallback
 */
TTS.prototype.shutdown = function(successCallback, errorCallback) {
     return PhoneGap.exec(successCallback, errorCallback, "TTS", "shutdown", []);
};

/**
 * Finds out if the language is currently supported by the TTS service.
 * 
 * @param {DOMSting} lang
 * @param {Object} successCallback
 * @param {Object} errorCallback
 */
TTS.prototype.isLanguageAvailable = function(lang, successCallback, errorCallback) {
     return PhoneGap.exec(successCallback, errorCallback, "TTS", "isLanguageAvailable", [lang]);
};

/**
 * Finds out the current language of the TTS service.
 * 
 * @param {Object} successCallback
 * @param {Object} errorCallback
 */
TTS.prototype.getLanguage = function(successCallback, errorCallback) {
     return PhoneGap.exec(successCallback, errorCallback, "TTS", "getLanguage", []);
};

/**
 * Sets the language of the TTS service.
 * 
 * @param {DOMString} lang
 * @param {Object} successCallback
 * @param {Object} errorCallback
 */
TTS.prototype.setLanguage = function(lang, successCallback, errorCallback) {
     return PhoneGap.exec(successCallback, errorCallback, "TTS", "setLanguage", [lang]);
};

/**
 * Load TTS
 */ 
PhoneGap.addConstructor(function() {
    PhoneGap.addPlugin("tts", new TTS());
// @deprecated: No longer needed in PhoneGap 1.0. Uncomment the addService code for earlier 
// PhoneGap releases.
//     PluginManager.addService("TTS", "com.phonegap.plugins.speech.TTS");
});

js/tts.js

Comments are currently closed.