summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser/resources/chromeos/chromevox/host/interface/abstract_tts.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/resources/chromeos/chromevox/host/interface/abstract_tts.js')
-rw-r--r--chromium/chrome/browser/resources/chromeos/chromevox/host/interface/abstract_tts.js616
1 files changed, 616 insertions, 0 deletions
diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/host/interface/abstract_tts.js b/chromium/chrome/browser/resources/chromeos/chromevox/host/interface/abstract_tts.js
new file mode 100644
index 00000000000..d17f1cc88c5
--- /dev/null
+++ b/chromium/chrome/browser/resources/chromeos/chromevox/host/interface/abstract_tts.js
@@ -0,0 +1,616 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Base class for Text-to-Speech engines that actually transform
+ * text to speech.
+ *
+ */
+
+goog.provide('cvox.AbstractTts');
+
+goog.require('cvox.TtsInterface');
+goog.require('goog.i18n.MessageFormat');
+
+/**
+ * Creates a new instance.
+ * @constructor
+ * @implements {cvox.TtsInterface}
+ */
+cvox.AbstractTts = function() {
+ this.ttsProperties = new Object();
+
+ /**
+ * Default value for TTS properties.
+ * Note that these as well as the subsequent properties might be different
+ * on different host platforms (like Chrome, Android, etc.).
+ * @type {{pitch : number,
+ * rate: number,
+ * volume: number}}
+ * @protected
+ */
+ this.propertyDefault = {
+ 'rate': 0.5,
+ 'pitch': 0.5,
+ 'volume': 0.5
+ };
+
+ /**
+ * Min value for TTS properties.
+ * @type {{pitch : number,
+ * rate: number,
+ * volume: number}}
+ * @protected
+ */
+ this.propertyMin = {
+ 'rate': 0.0,
+ 'pitch': 0.0,
+ 'volume': 0.0
+ };
+
+ /**
+ * Max value for TTS properties.
+ * @type {{pitch : number,
+ * rate: number,
+ * volume: number}}
+ * @protected
+ */
+ this.propertyMax = {
+ 'rate': 1.0,
+ 'pitch': 1.0,
+ 'volume': 1.0
+ };
+
+ /**
+ * Step value for TTS properties.
+ * @type {{pitch : number,
+ * rate: number,
+ * volume: number}}
+ * @protected
+ */
+ this.propertyStep = {
+ 'rate': 0.1,
+ 'pitch': 0.1,
+ 'volume': 0.1
+ };
+
+
+ /** @private */
+
+ if (cvox.AbstractTts.pronunciationDictionaryRegexp_ == undefined) {
+ // Create an expression that matches all words in the pronunciation
+ // dictionary on word boundaries, ignoring case.
+ var words = [];
+ for (var word in cvox.AbstractTts.PRONUNCIATION_DICTIONARY) {
+ words.push(word);
+ }
+ var expr = '\\b(' + words.join('|') + ')\\b';
+ cvox.AbstractTts.pronunciationDictionaryRegexp_ = new RegExp(expr, 'ig');
+ }
+
+ if (cvox.AbstractTts.substitutionDictionaryRegexp_ == undefined) {
+ // Create an expression that matches all words in the substitution
+ // dictionary.
+ var symbols = [];
+ for (var symbol in cvox.AbstractTts.SUBSTITUTION_DICTIONARY) {
+ symbols.push(symbol);
+ }
+ var expr = '(' + symbols.join('|') + ')';
+ cvox.AbstractTts.substitutionDictionaryRegexp_ = new RegExp(expr, 'ig');
+ }
+};
+
+
+/**
+ * Default TTS properties for this TTS engine.
+ * @type {Object}
+ * @protected
+ */
+cvox.AbstractTts.prototype.ttsProperties;
+
+
+/** @override */
+cvox.AbstractTts.prototype.speak = function(textString, queueMode, properties) {
+ return this;
+};
+
+
+/** @override */
+cvox.AbstractTts.prototype.isSpeaking = function() {
+ return false;
+};
+
+
+/** @override */
+cvox.AbstractTts.prototype.stop = function() {
+};
+
+
+/** @override */
+cvox.AbstractTts.prototype.addCapturingEventListener = function(listener) { };
+
+
+/** @override */
+cvox.AbstractTts.prototype.increaseOrDecreaseProperty =
+ function(propertyName, increase) {
+ var min = this.propertyMin[propertyName];
+ var max = this.propertyMax[propertyName];
+ var step = this.propertyStep[propertyName];
+ var current = this.ttsProperties[propertyName];
+ current = increase ? current + step : current - step;
+ this.ttsProperties[propertyName] = Math.max(Math.min(current, max), min);
+ };
+
+
+/**
+ * Merges the given properties with the default ones. Always returns a
+ * new object, so that you can safely modify the result of mergeProperties
+ * without worrying that you're modifying an object used elsewhere.
+ * @param {Object=} properties The properties to merge with the current ones.
+ * @return {Object} The merged properties.
+ * @protected
+ */
+cvox.AbstractTts.prototype.mergeProperties = function(properties) {
+ var mergedProperties = new Object();
+ var p;
+ if (this.ttsProperties) {
+ for (p in this.ttsProperties) {
+ mergedProperties[p] = this.ttsProperties[p];
+ }
+ }
+ if (properties) {
+ var tts = cvox.AbstractTts;
+ if (typeof(properties[tts.VOLUME]) == 'number') {
+ mergedProperties[tts.VOLUME] = properties[tts.VOLUME];
+ }
+ if (typeof(properties[tts.PITCH]) == 'number') {
+ mergedProperties[tts.PITCH] = properties[tts.PITCH];
+ }
+ if (typeof(properties[tts.RATE]) == 'number') {
+ mergedProperties[tts.RATE] = properties[tts.RATE];
+ }
+ if (typeof(properties[tts.LANG]) == 'string') {
+ mergedProperties[tts.LANG] = properties[tts.LANG];
+ }
+
+ var context = this;
+ var mergeRelativeProperty = function(abs, rel) {
+ if (typeof(properties[rel]) == 'number' &&
+ typeof(mergedProperties[abs]) == 'number') {
+ mergedProperties[abs] += properties[rel];
+ var min = context.propertyMin[abs];
+ var max = context.propertyMax[abs];
+ if (mergedProperties[abs] > max) {
+ mergedProperties[abs] = max;
+ } else if (mergedProperties[abs] < min) {
+ mergedProperties[abs] = min;
+ }
+ }
+ };
+
+ mergeRelativeProperty(tts.VOLUME, tts.RELATIVE_VOLUME);
+ mergeRelativeProperty(tts.PITCH, tts.RELATIVE_PITCH);
+ mergeRelativeProperty(tts.RATE, tts.RELATIVE_RATE);
+ }
+
+ for (p in properties) {
+ if (!mergedProperties.hasOwnProperty(p)) {
+ mergedProperties[p] = properties[p];
+ }
+ }
+
+ return mergedProperties;
+};
+
+
+/**
+ * Method to preprocess text to be spoken properly by a speech
+ * engine.
+ *
+ * 1. Replace any single character with a description of that character.
+ *
+ * 2. Convert all-caps words to lowercase if they don't look like an
+ * acronym / abbreviation.
+ *
+ * @param {string} text A text string to be spoken.
+ * @param {Object= } properties Out parameter populated with how to speak the
+ * string.
+ * @return {string} The text formatted in a way that will sound better by
+ * most speech engines.
+ * @protected
+ */
+cvox.AbstractTts.prototype.preprocess = function(text, properties) {
+ if (text.length == 1 && text >= 'A' && text <= 'Z') {
+ for (var prop in cvox.AbstractTts.PERSONALITY_CAPITAL)
+ properties[prop] = cvox.AbstractTts.PERSONALITY_CAPITAL[prop];
+ }
+
+ // Substitute all symbols in the substitution dictionary. This is pretty
+ // efficient because we use a single regexp that matches all symbols
+ // simultaneously.
+ text = text.replace(
+ cvox.AbstractTts.substitutionDictionaryRegexp_,
+ function(symbol) {
+ return ' ' + cvox.AbstractTts.SUBSTITUTION_DICTIONARY[symbol] + ' ';
+ });
+
+ // Handle single characters that we want to make sure we pronounce.
+ if (text.length == 1) {
+ return cvox.AbstractTts.CHARACTER_DICTIONARY[text] ?
+ (new goog.i18n.MessageFormat(cvox.ChromeVox.msgs.getMsg(
+ cvox.AbstractTts.CHARACTER_DICTIONARY[text])))
+ .format({'COUNT': 1}) :
+ text.toUpperCase();
+ }
+
+ // Substitute all words in the pronunciation dictionary. This is pretty
+ // efficient because we use a single regexp that matches all words
+ // simultaneously, and it calls a function with each match, which we can
+ // use to look up the replacement in our dictionary.
+ text = text.replace(
+ cvox.AbstractTts.pronunciationDictionaryRegexp_,
+ function(word) {
+ return cvox.AbstractTts.PRONUNCIATION_DICTIONARY[word.toLowerCase()];
+ });
+
+ // Special case for google+, where the punctuation must be pronounced.
+ text = text.replace(/google\+/ig, 'google plus');
+
+ // Expand all repeated characters.
+ text = text.replace(
+ cvox.AbstractTts.repetitionRegexp_, cvox.AbstractTts.repetitionReplace_);
+
+ // If there's no lower case letters, and at least two spaces, skip spacing
+ // text.
+ var skipSpacing = false;
+ if (!text.match(/[a-z]+/) && text.indexOf(' ') != text.lastIndexOf(' ')) {
+ skipSpacing = true;
+ }
+
+ // Convert all-caps words to lowercase if they don't look like acronyms,
+ // otherwise add a space before all-caps words so that all-caps words in
+ // the middle of camelCase will be separated.
+ text = text.replace(/[A-Z]+/g, function(word) {
+ // If a word contains vowels and is more than 3 letters long, it is
+ // probably a real word and not just an abbreviation. Convert it to lower
+ // case and speak it normally.
+ if ((word.length > 3) && word.match(/([AEIOUY])/g)) {
+ return word.toLowerCase();
+ } else if (!skipSpacing) {
+ // Builds spaced-out camelCased/all CAPS words so they sound better when
+ // spoken by TTS engines.
+ return ' ' + word.split('').join(' ');
+ } else {
+ return word;
+ }
+ });
+
+ return text;
+};
+
+
+/** TTS rate property. @type {string} */
+cvox.AbstractTts.RATE = 'rate';
+/** TTS pitch property. @type {string} */
+cvox.AbstractTts.PITCH = 'pitch';
+/** TTS volume property. @type {string} */
+cvox.AbstractTts.VOLUME = 'volume';
+/** TTS language property. @type {string} */
+cvox.AbstractTts.LANG = 'lang';
+
+/** TTS relative rate property. @type {string} */
+cvox.AbstractTts.RELATIVE_RATE = 'relativeRate';
+/** TTS relative pitch property. @type {string} */
+cvox.AbstractTts.RELATIVE_PITCH = 'relativePitch';
+/** TTS relative volume property. @type {string} */
+cvox.AbstractTts.RELATIVE_VOLUME = 'relativeVolume';
+
+/** TTS color property (for the lens display). @type {string} */
+cvox.AbstractTts.COLOR = 'color';
+/** TTS CSS font-weight property (for the lens display). @type {string} */
+cvox.AbstractTts.FONT_WEIGHT = 'fontWeight';
+
+/** TTS punctuation-echo property. @type {string} */
+cvox.AbstractTts.PUNCTUATION_ECHO = 'punctuationEcho';
+
+/** TTS pause property. @type {string} */
+cvox.AbstractTts.PAUSE = 'pause';
+
+/**
+ * TTS personality for annotations - text spoken by ChromeVox that
+ * elaborates on a user interface element but isn't displayed on-screen.
+ * @type {Object}
+ */
+cvox.AbstractTts.PERSONALITY_ANNOTATION = {
+ 'relativePitch': -0.25,
+ // TODO:(rshearer) Added this color change for I/O presentation.
+ 'color': 'yellow',
+ 'punctuationEcho': 'none'
+};
+
+
+/**
+ * TTS personality for announcements - text spoken by ChromeVox that
+ * isn't tied to any user interface elements.
+ * @type {Object}
+ */
+cvox.AbstractTts.PERSONALITY_ANNOUNCEMENT = {
+ 'punctuationEcho': 'none'
+};
+
+/**
+ * TTS personality for alerts from the system, such as battery level
+ * warnings.
+ * @type {Object}
+ */
+cvox.AbstractTts.PERSONALITY_SYSTEM_ALERT = {
+ 'punctuationEcho': 'none',
+ 'doNotInterrupt': true
+};
+
+/**
+ * TTS personality for an aside - text in parentheses.
+ * @type {Object}
+ */
+cvox.AbstractTts.PERSONALITY_ASIDE = {
+ 'relativePitch': -0.1,
+ 'color': '#669'
+};
+
+
+/**
+ * TTS personality for capital letters.
+ * @type {Object}
+ */
+cvox.AbstractTts.PERSONALITY_CAPITAL = {
+ 'relativePitch': 0.6
+};
+
+
+/**
+ * TTS personality for deleted text.
+ * @type {Object}
+ */
+cvox.AbstractTts.PERSONALITY_DELETED = {
+ 'punctuationEcho': 'none',
+ 'relativePitch': -0.6
+};
+
+
+/**
+ * TTS personality for quoted text.
+ * @type {Object}
+ */
+cvox.AbstractTts.PERSONALITY_QUOTE = {
+ 'relativePitch': 0.1,
+ 'color': '#b6b',
+ 'fontWeight': 'bold'
+};
+
+
+/**
+ * TTS personality for strong or bold text.
+ * @type {Object}
+ */
+cvox.AbstractTts.PERSONALITY_STRONG = {
+ 'relativePitch': 0.1,
+ 'color': '#b66',
+ 'fontWeight': 'bold'
+};
+
+
+/**
+ * TTS personality for emphasis or italicized text.
+ * @type {Object}
+ */
+cvox.AbstractTts.PERSONALITY_EMPHASIS = {
+ 'relativeVolume': 0.1,
+ 'relativeRate': -0.1,
+ 'color': '#6bb',
+ 'fontWeight': 'bold'
+};
+
+
+/**
+ * Flag indicating if the TTS is being debugged.
+ * @type {boolean}
+ */
+cvox.AbstractTts.DEBUG = true;
+
+
+/**
+ * Speech queue mode that interrupts the current utterance.
+ * @type {number}
+ */
+cvox.AbstractTts.QUEUE_MODE_FLUSH = 0;
+
+
+/**
+ * Speech queue mode that does not interrupt the current utterance.
+ * @type {number}
+ */
+cvox.AbstractTts.QUEUE_MODE_QUEUE = 1;
+
+
+/**
+ * Speech queue mode that flushes all utterances of the same category
+ * (as set by properties['category']).
+ * @type {number}
+ */
+cvox.AbstractTts.QUEUE_MODE_CATEGORY_FLUSH = 2;
+
+
+/**
+ * Character dictionary. These symbols are replaced with their human readable
+ * equivalents. This replacement only occurs for single character utterances.
+ * @type {Object.<string, string>}
+ */
+cvox.AbstractTts.CHARACTER_DICTIONARY = {
+ ' ': 'space',
+ '`': 'backtick',
+ '~': 'tilde',
+ '!': 'exclamation',
+ '@': 'at',
+ '#': 'pound',
+ '$': 'dollar',
+ '%': 'percent',
+ '^': 'caret',
+ '&': 'ampersand',
+ '*': 'asterisk',
+ '(': 'open_paren',
+ ')': 'close_paren',
+ '-': 'dash',
+ '_': 'underscore',
+ '=': 'equals',
+ '+': 'plus',
+ '[': 'left_bracket',
+ ']': 'right_bracket',
+ '{': 'left_brace',
+ '}': 'right_brace',
+ '|': 'pipe',
+ ';': 'semicolon',
+ ':': 'colon',
+ ',': 'comma',
+ '.': 'dot',
+ '<': 'less_than',
+ '>': 'greater_than',
+ '/': 'slash',
+ '?': 'question_mark',
+ '"': 'quote',
+ '\'': 'apostrophe',
+ '\t': 'tab',
+ '\r': 'return',
+ '\n': 'new_line',
+ '\\': 'backslash'
+};
+
+
+/**
+ * Pronunciation dictionary. Each key must be lowercase, its replacement
+ * should be spelled out the way most TTS engines will pronounce it
+ * correctly. This particular dictionary only handles letters and numbers,
+ * no symbols.
+ * @type {Object.<string, string>}
+ */
+cvox.AbstractTts.PRONUNCIATION_DICTIONARY = {
+ 'admob': 'ad-mob',
+ 'adsense': 'ad-sense',
+ 'adwords': 'ad-words',
+ 'angularjs': 'angular j s',
+ 'bcc': 'B C C',
+ 'cc': 'C C',
+ 'chromevox': 'chrome vox',
+ 'cr48': 'C R 48',
+ 'ctrl': 'control',
+ 'doubleclick': 'double-click',
+ 'gmail': 'gee mail',
+ 'gtalk': 'gee talk',
+ 'http': 'H T T P',
+ 'https' : 'H T T P S',
+ 'igoogle': 'eye google',
+ 'pagerank': 'page-rank',
+ 'username': 'user-name',
+ 'www': 'W W W',
+ 'youtube': 'you tube'
+};
+
+
+/**
+ * Pronunciation dictionary regexp.
+ * @type {RegExp};
+ * @private
+ */
+cvox.AbstractTts.pronunciationDictionaryRegexp_;
+
+
+/**
+ * Substitution dictionary. These symbols or patterns are ALWAYS substituted
+ * whenever they occur, so this should be reserved only for unicode characters
+ * and characters that never have any different meaning in context.
+ *
+ * For example, do not include '$' here because $2 should be read as
+ * "two dollars".
+ * @type {Object.<string, string>}
+ */
+cvox.AbstractTts.SUBSTITUTION_DICTIONARY = {
+ '://': 'colon slash slash',
+ '\u00bc': 'one fourth',
+ '\u00bd': 'one half',
+ '\u2190': 'left arrow',
+ '\u2191': 'up arrow',
+ '\u2192': 'right arrow',
+ '\u2193': 'down arrow',
+ '\u21d0': 'left double arrow',
+ '\u21d1': 'up double arrow',
+ '\u21d2': 'right double arrow',
+ '\u21d3': 'down double arrow',
+ '\u21e6': 'left arrow',
+ '\u21e7': 'up arrow',
+ '\u21e8': 'right arrow',
+ '\u21e9': 'down arrow',
+ '\u2303': 'control',
+ '\u2318': 'command',
+ '\u2325': 'option',
+ '\u25b2': 'up triangle',
+ '\u25b3': 'up triangle',
+ '\u25b4': 'up triangle',
+ '\u25b5': 'up triangle',
+ '\u25b6': 'right triangle',
+ '\u25b7': 'right triangle',
+ '\u25b8': 'right triangle',
+ '\u25b9': 'right triangle',
+ '\u25ba': 'right pointer',
+ '\u25bb': 'right pointer',
+ '\u25bc': 'down triangle',
+ '\u25bd': 'down triangle',
+ '\u25be': 'down triangle',
+ '\u25bf': 'down triangle',
+ '\u25c0': 'left triangle',
+ '\u25c1': 'left triangle',
+ '\u25c2': 'left triangle',
+ '\u25c3': 'left triangle',
+ '\u25c4': 'left pointer',
+ '\u25c5': 'left pointer',
+ '\uf8ff': 'apple'
+};
+
+
+/**
+ * Substitution dictionary regexp.
+ * @type {RegExp};
+ * @private
+ */
+cvox.AbstractTts.substitutionDictionaryRegexp_;
+
+
+/**
+ * repetition filter regexp.
+ * @type {RegExp}
+ * @private
+ */
+cvox.AbstractTts.repetitionRegexp_ =
+ /([-\/\\|!@#$%^&*\(\)=_+\[\]\{\}.?;'":<>])\1{2,}/g;
+
+
+/**
+ * Constructs a description of a repeated character. Use as a param to
+ * string.replace.
+ * @param {string} match The matching string.
+ * @return {string} The description.
+ * @private
+ */
+cvox.AbstractTts.repetitionReplace_ = function(match) {
+ var count = match.length;
+ return ' ' + (new goog.i18n.MessageFormat(cvox.ChromeVox.msgs.getMsg(
+ cvox.AbstractTts.CHARACTER_DICTIONARY[match[0]])))
+ .format({'COUNT': count}) + ' ';
+};
+
+
+/**
+ * @override
+ */
+cvox.AbstractTts.prototype.getDefaultProperty = function(property) {
+ return this.propertyDefault[property];
+};