1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * @fileoverview Base class for Text-to-Speech engines that actually transform
11 goog.provide('cvox.AbstractTts');
13 goog.require('cvox.TtsInterface');
14 goog.require('goog.i18n.MessageFormat');
17 * Creates a new instance.
19 * @implements {cvox.TtsInterface}
21 cvox.AbstractTts = function() {
22 this.ttsProperties = new Object();
25 * Default value for TTS properties.
26 * Note that these as well as the subsequent properties might be different
27 * on different host platforms (like Chrome, Android, etc.).
28 * @type {{pitch : number,
33 this.propertyDefault = {
40 * Min value for TTS properties.
41 * @type {{pitch : number,
53 * Max value for TTS properties.
54 * @type {{pitch : number,
66 * Step value for TTS properties.
67 * @type {{pitch : number,
81 if (cvox.AbstractTts.pronunciationDictionaryRegexp_ == undefined) {
82 // Create an expression that matches all words in the pronunciation
83 // dictionary on word boundaries, ignoring case.
85 for (var word in cvox.AbstractTts.PRONUNCIATION_DICTIONARY) {
88 var expr = '\\b(' + words.join('|') + ')\\b';
89 cvox.AbstractTts.pronunciationDictionaryRegexp_ = new RegExp(expr, 'ig');
92 if (cvox.AbstractTts.substitutionDictionaryRegexp_ == undefined) {
93 // Create an expression that matches all words in the substitution
96 for (var symbol in cvox.AbstractTts.SUBSTITUTION_DICTIONARY) {
99 var expr = '(' + symbols.join('|') + ')';
100 cvox.AbstractTts.substitutionDictionaryRegexp_ = new RegExp(expr, 'ig');
106 * Default TTS properties for this TTS engine.
110 cvox.AbstractTts.prototype.ttsProperties;
114 cvox.AbstractTts.prototype.speak = function(textString, queueMode, properties) {
120 cvox.AbstractTts.prototype.isSpeaking = function() {
126 cvox.AbstractTts.prototype.stop = function() {
131 cvox.AbstractTts.prototype.addCapturingEventListener = function(listener) { };
135 cvox.AbstractTts.prototype.increaseOrDecreaseProperty =
136 function(propertyName, increase) {
137 var min = this.propertyMin[propertyName];
138 var max = this.propertyMax[propertyName];
139 var step = this.propertyStep[propertyName];
140 var current = this.ttsProperties[propertyName];
141 current = increase ? current + step : current - step;
142 this.ttsProperties[propertyName] = Math.max(Math.min(current, max), min);
147 * Merges the given properties with the default ones. Always returns a
148 * new object, so that you can safely modify the result of mergeProperties
149 * without worrying that you're modifying an object used elsewhere.
150 * @param {Object=} properties The properties to merge with the current ones.
151 * @return {Object} The merged properties.
154 cvox.AbstractTts.prototype.mergeProperties = function(properties) {
155 var mergedProperties = new Object();
157 if (this.ttsProperties) {
158 for (p in this.ttsProperties) {
159 mergedProperties[p] = this.ttsProperties[p];
163 var tts = cvox.AbstractTts;
164 if (typeof(properties[tts.VOLUME]) == 'number') {
165 mergedProperties[tts.VOLUME] = properties[tts.VOLUME];
167 if (typeof(properties[tts.PITCH]) == 'number') {
168 mergedProperties[tts.PITCH] = properties[tts.PITCH];
170 if (typeof(properties[tts.RATE]) == 'number') {
171 mergedProperties[tts.RATE] = properties[tts.RATE];
173 if (typeof(properties[tts.LANG]) == 'string') {
174 mergedProperties[tts.LANG] = properties[tts.LANG];
178 var mergeRelativeProperty = function(abs, rel) {
179 if (typeof(properties[rel]) == 'number' &&
180 typeof(mergedProperties[abs]) == 'number') {
181 mergedProperties[abs] += properties[rel];
182 var min = context.propertyMin[abs];
183 var max = context.propertyMax[abs];
184 if (mergedProperties[abs] > max) {
185 mergedProperties[abs] = max;
186 } else if (mergedProperties[abs] < min) {
187 mergedProperties[abs] = min;
192 mergeRelativeProperty(tts.VOLUME, tts.RELATIVE_VOLUME);
193 mergeRelativeProperty(tts.PITCH, tts.RELATIVE_PITCH);
194 mergeRelativeProperty(tts.RATE, tts.RELATIVE_RATE);
197 for (p in properties) {
198 if (!mergedProperties.hasOwnProperty(p)) {
199 mergedProperties[p] = properties[p];
203 return mergedProperties;
208 * Method to preprocess text to be spoken properly by a speech
211 * 1. Replace any single character with a description of that character.
213 * 2. Convert all-caps words to lowercase if they don't look like an
214 * acronym / abbreviation.
216 * @param {string} text A text string to be spoken.
217 * @param {Object= } properties Out parameter populated with how to speak the
219 * @return {string} The text formatted in a way that will sound better by
220 * most speech engines.
223 cvox.AbstractTts.prototype.preprocess = function(text, properties) {
224 if (text.length == 1 && text >= 'A' && text <= 'Z') {
225 for (var prop in cvox.AbstractTts.PERSONALITY_CAPITAL)
226 properties[prop] = cvox.AbstractTts.PERSONALITY_CAPITAL[prop];
229 // Substitute all symbols in the substitution dictionary. This is pretty
230 // efficient because we use a single regexp that matches all symbols
233 cvox.AbstractTts.substitutionDictionaryRegexp_,
235 return ' ' + cvox.AbstractTts.SUBSTITUTION_DICTIONARY[symbol] + ' ';
238 // Handle single characters that we want to make sure we pronounce.
239 if (text.length == 1) {
240 return cvox.AbstractTts.CHARACTER_DICTIONARY[text] ?
241 (new goog.i18n.MessageFormat(cvox.ChromeVox.msgs.getMsg(
242 cvox.AbstractTts.CHARACTER_DICTIONARY[text])))
243 .format({'COUNT': 1}) :
247 // Substitute all words in the pronunciation dictionary. This is pretty
248 // efficient because we use a single regexp that matches all words
249 // simultaneously, and it calls a function with each match, which we can
250 // use to look up the replacement in our dictionary.
252 cvox.AbstractTts.pronunciationDictionaryRegexp_,
254 return cvox.AbstractTts.PRONUNCIATION_DICTIONARY[word.toLowerCase()];
257 // Expand all repeated characters.
259 cvox.AbstractTts.repetitionRegexp_, cvox.AbstractTts.repetitionReplace_);
265 /** TTS rate property. @type {string} */
266 cvox.AbstractTts.RATE = 'rate';
267 /** TTS pitch property. @type {string} */
268 cvox.AbstractTts.PITCH = 'pitch';
269 /** TTS volume property. @type {string} */
270 cvox.AbstractTts.VOLUME = 'volume';
271 /** TTS language property. @type {string} */
272 cvox.AbstractTts.LANG = 'lang';
274 /** TTS relative rate property. @type {string} */
275 cvox.AbstractTts.RELATIVE_RATE = 'relativeRate';
276 /** TTS relative pitch property. @type {string} */
277 cvox.AbstractTts.RELATIVE_PITCH = 'relativePitch';
278 /** TTS relative volume property. @type {string} */
279 cvox.AbstractTts.RELATIVE_VOLUME = 'relativeVolume';
281 /** TTS color property (for the lens display). @type {string} */
282 cvox.AbstractTts.COLOR = 'color';
283 /** TTS CSS font-weight property (for the lens display). @type {string} */
284 cvox.AbstractTts.FONT_WEIGHT = 'fontWeight';
286 /** TTS punctuation-echo property. @type {string} */
287 cvox.AbstractTts.PUNCTUATION_ECHO = 'punctuationEcho';
289 /** TTS pause property. @type {string} */
290 cvox.AbstractTts.PAUSE = 'pause';
293 * TTS personality for annotations - text spoken by ChromeVox that
294 * elaborates on a user interface element but isn't displayed on-screen.
297 cvox.AbstractTts.PERSONALITY_ANNOTATION = {
298 'relativePitch': -0.25,
299 // TODO:(rshearer) Added this color change for I/O presentation.
301 'punctuationEcho': 'none'
306 * TTS personality for announcements - text spoken by ChromeVox that
307 * isn't tied to any user interface elements.
310 cvox.AbstractTts.PERSONALITY_ANNOUNCEMENT = {
311 'punctuationEcho': 'none'
315 * TTS personality for alerts from the system, such as battery level
319 cvox.AbstractTts.PERSONALITY_SYSTEM_ALERT = {
320 'punctuationEcho': 'none',
321 'doNotInterrupt': true
325 * TTS personality for an aside - text in parentheses.
328 cvox.AbstractTts.PERSONALITY_ASIDE = {
329 'relativePitch': -0.1,
335 * TTS personality for capital letters.
338 cvox.AbstractTts.PERSONALITY_CAPITAL = {
344 * TTS personality for deleted text.
347 cvox.AbstractTts.PERSONALITY_DELETED = {
348 'punctuationEcho': 'none',
349 'relativePitch': -0.6
354 * TTS personality for quoted text.
357 cvox.AbstractTts.PERSONALITY_QUOTE = {
358 'relativePitch': 0.1,
365 * TTS personality for strong or bold text.
368 cvox.AbstractTts.PERSONALITY_STRONG = {
369 'relativePitch': 0.1,
376 * TTS personality for emphasis or italicized text.
379 cvox.AbstractTts.PERSONALITY_EMPHASIS = {
380 'relativeVolume': 0.1,
381 'relativeRate': -0.1,
388 * Flag indicating if the TTS is being debugged.
391 cvox.AbstractTts.DEBUG = true;
395 * Character dictionary. These symbols are replaced with their human readable
396 * equivalents. This replacement only occurs for single character utterances.
397 * @type {Object<string>}
399 cvox.AbstractTts.CHARACTER_DICTIONARY = {
418 ']': 'right_bracket',
429 '?': 'question_mark',
440 * Pronunciation dictionary. Each key must be lowercase, its replacement
441 * should be spelled out the way most TTS engines will pronounce it
442 * correctly. This particular dictionary only handles letters and numbers,
444 * @type {Object<string>}
446 cvox.AbstractTts.PRONUNCIATION_DICTIONARY = {
448 'adsense': 'ad-sense',
449 'adwords': 'ad-words',
450 'angularjs': 'angular j s',
453 'chromevox': 'chrome vox',
456 'doubleclick': 'double-click',
460 'https' : 'H T T P S',
461 'igoogle': 'eye google',
462 'pagerank': 'page-rank',
463 'username': 'user-name',
465 'youtube': 'you tube'
470 * Pronunciation dictionary regexp.
474 cvox.AbstractTts.pronunciationDictionaryRegexp_;
478 * Substitution dictionary. These symbols or patterns are ALWAYS substituted
479 * whenever they occur, so this should be reserved only for unicode characters
480 * and characters that never have any different meaning in context.
482 * For example, do not include '$' here because $2 should be read as
484 * @type {Object<string>}
486 cvox.AbstractTts.SUBSTITUTION_DICTIONARY = {
487 '://': 'colon slash slash',
488 '\u00bc': 'one fourth',
489 '\u00bd': 'one half',
490 '\u2190': 'left arrow',
491 '\u2191': 'up arrow',
492 '\u2192': 'right arrow',
493 '\u2193': 'down arrow',
494 '\u21d0': 'left double arrow',
495 '\u21d1': 'up double arrow',
496 '\u21d2': 'right double arrow',
497 '\u21d3': 'down double arrow',
498 '\u21e6': 'left arrow',
499 '\u21e7': 'up arrow',
500 '\u21e8': 'right arrow',
501 '\u21e9': 'down arrow',
505 '\u25b2': 'up triangle',
506 '\u25b3': 'up triangle',
507 '\u25b4': 'up triangle',
508 '\u25b5': 'up triangle',
509 '\u25b6': 'right triangle',
510 '\u25b7': 'right triangle',
511 '\u25b8': 'right triangle',
512 '\u25b9': 'right triangle',
513 '\u25ba': 'right pointer',
514 '\u25bb': 'right pointer',
515 '\u25bc': 'down triangle',
516 '\u25bd': 'down triangle',
517 '\u25be': 'down triangle',
518 '\u25bf': 'down triangle',
519 '\u25c0': 'left triangle',
520 '\u25c1': 'left triangle',
521 '\u25c2': 'left triangle',
522 '\u25c3': 'left triangle',
523 '\u25c4': 'left pointer',
524 '\u25c5': 'left pointer',
530 * Substitution dictionary regexp.
534 cvox.AbstractTts.substitutionDictionaryRegexp_;
538 * repetition filter regexp.
542 cvox.AbstractTts.repetitionRegexp_ =
543 /([-\/\\|!@#$%^&*\(\)=_+\[\]\{\}.?;'":<>])\1{2,}/g;
547 * Constructs a description of a repeated character. Use as a param to
549 * @param {string} match The matching string.
550 * @return {string} The description.
553 cvox.AbstractTts.repetitionReplace_ = function(match) {
554 var count = match.length;
555 return ' ' + (new goog.i18n.MessageFormat(cvox.ChromeVox.msgs.getMsg(
556 cvox.AbstractTts.CHARACTER_DICTIONARY[match[0]])))
557 .format({'COUNT': count}) + ' ';
564 cvox.AbstractTts.prototype.getDefaultProperty = function(property) {
565 return this.propertyDefault[property];