1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package android.speech.tts; 17 18 import android.annotation.RawRes; 19 import android.annotation.SdkConstant; 20 import android.annotation.SdkConstant.SdkConstantType; 21 import android.content.ComponentName; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.ServiceConnection; 26 import android.media.AudioAttributes; 27 import android.media.AudioManager; 28 import android.net.Uri; 29 import android.os.AsyncTask; 30 import android.os.Bundle; 31 import android.os.IBinder; 32 import android.os.ParcelFileDescriptor; 33 import android.os.RemoteException; 34 import android.provider.Settings; 35 import android.text.TextUtils; 36 import android.util.Log; 37 38 import java.io.File; 39 import java.io.FileNotFoundException; 40 import java.io.IOException; 41 import java.util.Collections; 42 import java.util.HashMap; 43 import java.util.HashSet; 44 import java.util.List; 45 import java.util.Locale; 46 import java.util.Map; 47 import java.util.MissingResourceException; 48 import java.util.Set; 49 50 /** 51 * 52 * Synthesizes speech from text for immediate playback or to create a sound file. 53 * <p>A TextToSpeech instance can only be used to synthesize text once it has completed its 54 * initialization. Implement the {@link TextToSpeech.OnInitListener} to be 55 * notified of the completion of the initialization.<br> 56 * When you are done using the TextToSpeech instance, call the {@link #shutdown()} method 57 * to release the native resources used by the TextToSpeech engine. 58 */ 59 public class TextToSpeech { 60 61 private static final String TAG = "TextToSpeech"; 62 63 /** 64 * Denotes a successful operation. 65 */ 66 public static final int SUCCESS = 0; 67 /** 68 * Denotes a generic operation failure. 69 */ 70 public static final int ERROR = -1; 71 72 /** 73 * Denotes a stop requested by a client. It's used only on the service side of the API, 74 * client should never expect to see this result code. 75 */ 76 public static final int STOPPED = -2; 77 78 /** 79 * Denotes a failure of a TTS engine to synthesize the given input. 80 */ 81 public static final int ERROR_SYNTHESIS = -3; 82 83 /** 84 * Denotes a failure of a TTS service. 85 */ 86 public static final int ERROR_SERVICE = -4; 87 88 /** 89 * Denotes a failure related to the output (audio device or a file). 90 */ 91 public static final int ERROR_OUTPUT = -5; 92 93 /** 94 * Denotes a failure caused by a network connectivity problems. 95 */ 96 public static final int ERROR_NETWORK = -6; 97 98 /** 99 * Denotes a failure caused by network timeout. 100 */ 101 public static final int ERROR_NETWORK_TIMEOUT = -7; 102 103 /** 104 * Denotes a failure caused by an invalid request. 105 */ 106 public static final int ERROR_INVALID_REQUEST = -8; 107 108 /** 109 * Denotes a failure caused by an unfinished download of the voice data. 110 * @see Engine#KEY_FEATURE_NOT_INSTALLED 111 */ 112 public static final int ERROR_NOT_INSTALLED_YET = -9; 113 114 /** 115 * Queue mode where all entries in the playback queue (media to be played 116 * and text to be synthesized) are dropped and replaced by the new entry. 117 * Queues are flushed with respect to a given calling app. Entries in the queue 118 * from other callees are not discarded. 119 */ 120 public static final int QUEUE_FLUSH = 0; 121 /** 122 * Queue mode where the new entry is added at the end of the playback queue. 123 */ 124 public static final int QUEUE_ADD = 1; 125 /** 126 * Queue mode where the entire playback queue is purged. This is different 127 * from {@link #QUEUE_FLUSH} in that all entries are purged, not just entries 128 * from a given caller. 129 * 130 * @hide 131 */ 132 static final int QUEUE_DESTROY = 2; 133 134 /** 135 * Denotes the language is available exactly as specified by the locale. 136 */ 137 public static final int LANG_COUNTRY_VAR_AVAILABLE = 2; 138 139 /** 140 * Denotes the language is available for the language and country specified 141 * by the locale, but not the variant. 142 */ 143 public static final int LANG_COUNTRY_AVAILABLE = 1; 144 145 /** 146 * Denotes the language is available for the language by the locale, 147 * but not the country and variant. 148 */ 149 public static final int LANG_AVAILABLE = 0; 150 151 /** 152 * Denotes the language data is missing. 153 */ 154 public static final int LANG_MISSING_DATA = -1; 155 156 /** 157 * Denotes the language is not supported. 158 */ 159 public static final int LANG_NOT_SUPPORTED = -2; 160 161 /** 162 * Broadcast Action: The TextToSpeech synthesizer has completed processing 163 * of all the text in the speech queue. 164 * 165 * Note that this notifies callers when the <b>engine</b> has finished has 166 * processing text data. Audio playback might not have completed (or even started) 167 * at this point. If you wish to be notified when this happens, see 168 * {@link OnUtteranceCompletedListener}. 169 */ 170 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 171 public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = 172 "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED"; 173 174 /** 175 * Interface definition of a callback to be invoked indicating the completion of the 176 * TextToSpeech engine initialization. 177 */ 178 public interface OnInitListener { 179 /** 180 * Called to signal the completion of the TextToSpeech engine initialization. 181 * 182 * @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}. 183 */ onInit(int status)184 public void onInit(int status); 185 } 186 187 /** 188 * Listener that will be called when the TTS service has 189 * completed synthesizing an utterance. This is only called if the utterance 190 * has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}). 191 * 192 * @deprecated Use {@link UtteranceProgressListener} instead. 193 */ 194 @Deprecated 195 public interface OnUtteranceCompletedListener { 196 /** 197 * Called when an utterance has been synthesized. 198 * 199 * @param utteranceId the identifier of the utterance. 200 */ onUtteranceCompleted(String utteranceId)201 public void onUtteranceCompleted(String utteranceId); 202 } 203 204 /** 205 * Constants and parameter names for controlling text-to-speech. These include: 206 * 207 * <ul> 208 * <li> 209 * Intents to ask engine to install data or check its data and 210 * extras for a TTS engine's check data activity. 211 * </li> 212 * <li> 213 * Keys for the parameters passed with speak commands, e.g. 214 * {@link Engine#KEY_PARAM_UTTERANCE_ID}, {@link Engine#KEY_PARAM_STREAM}. 215 * </li> 216 * <li> 217 * A list of feature strings that engines might support, e.g 218 * {@link Engine#KEY_FEATURE_NETWORK_SYNTHESIS}. These values may be passed in to 219 * {@link TextToSpeech#speak} and {@link TextToSpeech#synthesizeToFile} to modify 220 * engine behaviour. The engine can be queried for the set of features it supports 221 * through {@link TextToSpeech#getFeatures(java.util.Locale)}. 222 * </li> 223 * </ul> 224 */ 225 public class Engine { 226 227 /** 228 * Default speech rate. 229 * @hide 230 */ 231 public static final int DEFAULT_RATE = 100; 232 233 /** 234 * Default pitch. 235 * @hide 236 */ 237 public static final int DEFAULT_PITCH = 100; 238 239 /** 240 * Default volume. 241 * @hide 242 */ 243 public static final float DEFAULT_VOLUME = 1.0f; 244 245 /** 246 * Default pan (centered). 247 * @hide 248 */ 249 public static final float DEFAULT_PAN = 0.0f; 250 251 /** 252 * Default value for {@link Settings.Secure#TTS_USE_DEFAULTS}. 253 * @hide 254 */ 255 public static final int USE_DEFAULTS = 0; // false 256 257 /** 258 * Package name of the default TTS engine. 259 * 260 * @hide 261 * @deprecated No longer in use, the default engine is determined by 262 * the sort order defined in {@link TtsEngines}. Note that 263 * this doesn't "break" anything because there is no guarantee that 264 * the engine specified below is installed on a given build, let 265 * alone be the default. 266 */ 267 @Deprecated 268 public static final String DEFAULT_ENGINE = "com.svox.pico"; 269 270 /** 271 * Default audio stream used when playing synthesized speech. 272 */ 273 public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC; 274 275 /** 276 * Indicates success when checking the installation status of the resources used by the 277 * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 278 */ 279 public static final int CHECK_VOICE_DATA_PASS = 1; 280 281 /** 282 * Indicates failure when checking the installation status of the resources used by the 283 * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 284 */ 285 public static final int CHECK_VOICE_DATA_FAIL = 0; 286 287 /** 288 * Indicates erroneous data when checking the installation status of the resources used by 289 * the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 290 * 291 * @deprecated Use CHECK_VOICE_DATA_FAIL instead. 292 */ 293 @Deprecated 294 public static final int CHECK_VOICE_DATA_BAD_DATA = -1; 295 296 /** 297 * Indicates missing resources when checking the installation status of the resources used 298 * by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 299 * 300 * @deprecated Use CHECK_VOICE_DATA_FAIL instead. 301 */ 302 @Deprecated 303 public static final int CHECK_VOICE_DATA_MISSING_DATA = -2; 304 305 /** 306 * Indicates missing storage volume when checking the installation status of the resources 307 * used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 308 * 309 * @deprecated Use CHECK_VOICE_DATA_FAIL instead. 310 */ 311 @Deprecated 312 public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3; 313 314 /** 315 * Intent for starting a TTS service. Services that handle this intent must 316 * extend {@link TextToSpeechService}. Normal applications should not use this intent 317 * directly, instead they should talk to the TTS service using the the methods in this 318 * class. 319 */ 320 @SdkConstant(SdkConstantType.SERVICE_ACTION) 321 public static final String INTENT_ACTION_TTS_SERVICE = 322 "android.intent.action.TTS_SERVICE"; 323 324 /** 325 * Name under which a text to speech engine publishes information about itself. 326 * This meta-data should reference an XML resource containing a 327 * <code><{@link android.R.styleable#TextToSpeechEngine tts-engine}></code> 328 * tag. 329 */ 330 public static final String SERVICE_META_DATA = "android.speech.tts"; 331 332 // intents to ask engine to install data or check its data 333 /** 334 * Activity Action: Triggers the platform TextToSpeech engine to 335 * start the activity that installs the resource files on the device 336 * that are required for TTS to be operational. Since the installation 337 * of the data can be interrupted or declined by the user, the application 338 * shouldn't expect successful installation upon return from that intent, 339 * and if need be, should check installation status with 340 * {@link #ACTION_CHECK_TTS_DATA}. 341 */ 342 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 343 public static final String ACTION_INSTALL_TTS_DATA = 344 "android.speech.tts.engine.INSTALL_TTS_DATA"; 345 346 /** 347 * Broadcast Action: broadcast to signal the change in the list of available 348 * languages or/and their features. 349 */ 350 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 351 public static final String ACTION_TTS_DATA_INSTALLED = 352 "android.speech.tts.engine.TTS_DATA_INSTALLED"; 353 354 /** 355 * Activity Action: Starts the activity from the platform TextToSpeech 356 * engine to verify the proper installation and availability of the 357 * resource files on the system. Upon completion, the activity will 358 * return one of the following codes: 359 * {@link #CHECK_VOICE_DATA_PASS}, 360 * {@link #CHECK_VOICE_DATA_FAIL}, 361 * <p> Moreover, the data received in the activity result will contain the following 362 * fields: 363 * <ul> 364 * <li>{@link #EXTRA_AVAILABLE_VOICES} which contains an ArrayList<String> of all the 365 * available voices. The format of each voice is: lang-COUNTRY-variant where COUNTRY and 366 * variant are optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").</li> 367 * <li>{@link #EXTRA_UNAVAILABLE_VOICES} which contains an ArrayList<String> of all the 368 * unavailable voices (ones that user can install). The format of each voice is: 369 * lang-COUNTRY-variant where COUNTRY and variant are optional (ie, "eng" or 370 * "eng-USA" or "eng-USA-FEMALE").</li> 371 * </ul> 372 */ 373 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 374 public static final String ACTION_CHECK_TTS_DATA = 375 "android.speech.tts.engine.CHECK_TTS_DATA"; 376 377 /** 378 * Activity intent for getting some sample text to use for demonstrating TTS. Specific 379 * locale have to be requested by passing following extra parameters: 380 * <ul> 381 * <li>language</li> 382 * <li>country</li> 383 * <li>variant</li> 384 * </ul> 385 * 386 * Upon completion, the activity result may contain the following fields: 387 * <ul> 388 * <li>{@link #EXTRA_SAMPLE_TEXT} which contains an String with sample text.</li> 389 * </ul> 390 */ 391 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 392 public static final String ACTION_GET_SAMPLE_TEXT = 393 "android.speech.tts.engine.GET_SAMPLE_TEXT"; 394 395 /** 396 * Extra information received with the {@link #ACTION_GET_SAMPLE_TEXT} intent result where 397 * the TextToSpeech engine returns an String with sample text for requested voice 398 */ 399 public static final String EXTRA_SAMPLE_TEXT = "sampleText"; 400 401 402 // extras for a TTS engine's check data activity 403 /** 404 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where 405 * the TextToSpeech engine returns an ArrayList<String> of all the available voices. 406 * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are 407 * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE"). 408 */ 409 public static final String EXTRA_AVAILABLE_VOICES = "availableVoices"; 410 411 /** 412 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where 413 * the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices. 414 * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are 415 * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE"). 416 */ 417 public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices"; 418 419 /** 420 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where 421 * the TextToSpeech engine specifies the path to its resources. 422 * 423 * It may be used by language packages to find out where to put their data. 424 * 425 * @deprecated TTS engine implementation detail, this information has no use for 426 * text-to-speech API client. 427 */ 428 @Deprecated 429 public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot"; 430 431 /** 432 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where 433 * the TextToSpeech engine specifies the file names of its resources under the 434 * resource path. 435 * 436 * @deprecated TTS engine implementation detail, this information has no use for 437 * text-to-speech API client. 438 */ 439 @Deprecated 440 public static final String EXTRA_VOICE_DATA_FILES = "dataFiles"; 441 442 /** 443 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where 444 * the TextToSpeech engine specifies the locale associated with each resource file. 445 * 446 * @deprecated TTS engine implementation detail, this information has no use for 447 * text-to-speech API client. 448 */ 449 @Deprecated 450 public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo"; 451 452 /** 453 * Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the 454 * caller indicates to the TextToSpeech engine which specific sets of voice data to 455 * check for by sending an ArrayList<String> of the voices that are of interest. 456 * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are 457 * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE"). 458 * 459 * @deprecated Redundant functionality, checking for existence of specific sets of voice 460 * data can be done on client side. 461 */ 462 @Deprecated 463 public static final String EXTRA_CHECK_VOICE_DATA_FOR = "checkVoiceDataFor"; 464 465 // extras for a TTS engine's data installation 466 /** 467 * Extra information received with the {@link #ACTION_TTS_DATA_INSTALLED} intent result. 468 * It indicates whether the data files for the synthesis engine were successfully 469 * installed. The installation was initiated with the {@link #ACTION_INSTALL_TTS_DATA} 470 * intent. The possible values for this extra are 471 * {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}. 472 * 473 * @deprecated No longer in use. If client ise interested in information about what 474 * changed, is should send ACTION_CHECK_TTS_DATA intent to discover available voices. 475 */ 476 @Deprecated 477 public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled"; 478 479 // keys for the parameters passed with speak commands. Hidden keys are used internally 480 // to maintain engine state for each TextToSpeech instance. 481 /** 482 * @hide 483 */ 484 public static final String KEY_PARAM_RATE = "rate"; 485 486 /** 487 * @hide 488 */ 489 public static final String KEY_PARAM_VOICE_NAME = "voiceName"; 490 491 /** 492 * @hide 493 */ 494 public static final String KEY_PARAM_LANGUAGE = "language"; 495 496 /** 497 * @hide 498 */ 499 public static final String KEY_PARAM_COUNTRY = "country"; 500 501 /** 502 * @hide 503 */ 504 public static final String KEY_PARAM_VARIANT = "variant"; 505 506 /** 507 * @hide 508 */ 509 public static final String KEY_PARAM_ENGINE = "engine"; 510 511 /** 512 * @hide 513 */ 514 public static final String KEY_PARAM_PITCH = "pitch"; 515 516 /** 517 * Parameter key to specify the audio stream type to be used when speaking text 518 * or playing back a file. The value should be one of the STREAM_ constants 519 * defined in {@link AudioManager}. 520 * 521 * @see TextToSpeech#speak(String, int, HashMap) 522 * @see TextToSpeech#playEarcon(String, int, HashMap) 523 */ 524 public static final String KEY_PARAM_STREAM = "streamType"; 525 526 /** 527 * Parameter key to specify the audio attributes to be used when 528 * speaking text or playing back a file. The value should be set 529 * using {@link TextToSpeech#setAudioAttributes(AudioAttributes)}. 530 * 531 * @see TextToSpeech#speak(String, int, HashMap) 532 * @see TextToSpeech#playEarcon(String, int, HashMap) 533 * @hide 534 */ 535 public static final String KEY_PARAM_AUDIO_ATTRIBUTES = "audioAttributes"; 536 537 /** 538 * Parameter key to identify an utterance in the 539 * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been 540 * spoken, a file has been played back or a silence duration has elapsed. 541 * 542 * @see TextToSpeech#speak(String, int, HashMap) 543 * @see TextToSpeech#playEarcon(String, int, HashMap) 544 * @see TextToSpeech#synthesizeToFile(String, HashMap, String) 545 */ 546 public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId"; 547 548 /** 549 * Parameter key to specify the speech volume relative to the current stream type 550 * volume used when speaking text. Volume is specified as a float ranging from 0 to 1 551 * where 0 is silence, and 1 is the maximum volume (the default behavior). 552 * 553 * @see TextToSpeech#speak(String, int, HashMap) 554 * @see TextToSpeech#playEarcon(String, int, HashMap) 555 */ 556 public static final String KEY_PARAM_VOLUME = "volume"; 557 558 /** 559 * Parameter key to specify how the speech is panned from left to right when speaking text. 560 * Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan, 561 * 0 to center (the default behavior), and +1 to hard-right. 562 * 563 * @see TextToSpeech#speak(String, int, HashMap) 564 * @see TextToSpeech#playEarcon(String, int, HashMap) 565 */ 566 public static final String KEY_PARAM_PAN = "pan"; 567 568 /** 569 * Feature key for network synthesis. See {@link TextToSpeech#getFeatures(Locale)} 570 * for a description of how feature keys work. If set (and supported by the engine 571 * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must 572 * use network based synthesis. 573 * 574 * @see TextToSpeech#speak(String, int, java.util.HashMap) 575 * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String) 576 * @see TextToSpeech#getFeatures(java.util.Locale) 577 * 578 * @deprecated Starting from API level 21, to select network synthesis, call 579 * {@link TextToSpeech#getVoices()}, find a suitable network voice 580 * ({@link Voice#isNetworkConnectionRequired()}) and pass it 581 * to {@link TextToSpeech#setVoice(Voice)}. 582 */ 583 @Deprecated 584 public static final String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts"; 585 586 /** 587 * Feature key for embedded synthesis. See {@link TextToSpeech#getFeatures(Locale)} 588 * for a description of how feature keys work. If set and supported by the engine 589 * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must synthesize 590 * text on-device (without making network requests). 591 * 592 * @see TextToSpeech#speak(String, int, java.util.HashMap) 593 * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String) 594 * @see TextToSpeech#getFeatures(java.util.Locale) 595 596 * @deprecated Starting from API level 21, to select embedded synthesis, call 597 * ({@link TextToSpeech#getVoices()}, find a suitable embedded voice 598 * ({@link Voice#isNetworkConnectionRequired()}) and pass it 599 * to {@link TextToSpeech#setVoice(Voice)}). 600 */ 601 @Deprecated 602 public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts"; 603 604 /** 605 * Parameter key to specify an audio session identifier (obtained from 606 * {@link AudioManager#generateAudioSessionId()}) that will be used by the request audio 607 * output. It can be used to associate one of the {@link android.media.audiofx.AudioEffect} 608 * objects with the synthesis (or earcon) output. 609 * 610 * @see TextToSpeech#speak(String, int, HashMap) 611 * @see TextToSpeech#playEarcon(String, int, HashMap) 612 */ 613 public static final String KEY_PARAM_SESSION_ID = "sessionId"; 614 615 /** 616 * Feature key that indicates that the voice may need to download additional data to be fully 617 * functional. The download will be triggered by calling 618 * {@link TextToSpeech#setVoice(Voice)} or {@link TextToSpeech#setLanguage(Locale)}. 619 * Until download is complete, each synthesis request will either report 620 * {@link TextToSpeech#ERROR_NOT_INSTALLED_YET} error, or use a different voice to synthesize 621 * the request. This feature should NOT be used as a key of a request parameter. 622 * 623 * @see TextToSpeech#getFeatures(java.util.Locale) 624 * @see Voice#getFeatures() 625 */ 626 public static final String KEY_FEATURE_NOT_INSTALLED = "notInstalled"; 627 628 /** 629 * Feature key that indicate that a network timeout can be set for the request. If set and 630 * supported as per {@link TextToSpeech#getFeatures(Locale)} or {@link Voice#getFeatures()}, 631 * it can be used as request parameter to set the maximum allowed time for a single 632 * request attempt, in milliseconds, before synthesis fails. When used as a key of 633 * a request parameter, its value should be a string with an integer value. 634 * 635 * @see TextToSpeech#getFeatures(java.util.Locale) 636 * @see Voice#getFeatures() 637 */ 638 public static final String KEY_FEATURE_NETWORK_TIMEOUT_MS = "networkTimeoutMs"; 639 640 /** 641 * Feature key that indicates that network request retries count can be set for the request. 642 * If set and supported as per {@link TextToSpeech#getFeatures(Locale)} or 643 * {@link Voice#getFeatures()}, it can be used as a request parameter to set the 644 * number of network request retries that are attempted in case of failure. When used as 645 * a key of a request parameter, its value should be a string with an integer value. 646 * 647 * @see TextToSpeech#getFeatures(java.util.Locale) 648 * @see Voice#getFeatures() 649 */ 650 public static final String KEY_FEATURE_NETWORK_RETRIES_COUNT = "networkRetriesCount"; 651 } 652 653 private final Context mContext; 654 private Connection mConnectingServiceConnection; 655 private Connection mServiceConnection; 656 private OnInitListener mInitListener; 657 // Written from an unspecified application thread, read from 658 // a binder thread. 659 private volatile UtteranceProgressListener mUtteranceProgressListener; 660 private final Object mStartLock = new Object(); 661 662 private String mRequestedEngine; 663 // Whether to initialize this TTS object with the default engine, 664 // if the requested engine is not available. Valid only if mRequestedEngine 665 // is not null. Used only for testing, though potentially useful API wise 666 // too. 667 private final boolean mUseFallback; 668 private final Map<String, Uri> mEarcons; 669 private final Map<CharSequence, Uri> mUtterances; 670 private final Bundle mParams = new Bundle(); 671 private final TtsEngines mEnginesHelper; 672 private volatile String mCurrentEngine = null; 673 674 /** 675 * The constructor for the TextToSpeech class, using the default TTS engine. 676 * This will also initialize the associated TextToSpeech engine if it isn't already running. 677 * 678 * @param context 679 * The context this instance is running in. 680 * @param listener 681 * The {@link TextToSpeech.OnInitListener} that will be called when the 682 * TextToSpeech engine has initialized. In a case of a failure the listener 683 * may be called immediately, before TextToSpeech instance is fully constructed. 684 */ TextToSpeech(Context context, OnInitListener listener)685 public TextToSpeech(Context context, OnInitListener listener) { 686 this(context, listener, null); 687 } 688 689 /** 690 * The constructor for the TextToSpeech class, using the given TTS engine. 691 * This will also initialize the associated TextToSpeech engine if it isn't already running. 692 * 693 * @param context 694 * The context this instance is running in. 695 * @param listener 696 * The {@link TextToSpeech.OnInitListener} that will be called when the 697 * TextToSpeech engine has initialized. In a case of a failure the listener 698 * may be called immediately, before TextToSpeech instance is fully constructed. 699 * @param engine Package name of the TTS engine to use. 700 */ TextToSpeech(Context context, OnInitListener listener, String engine)701 public TextToSpeech(Context context, OnInitListener listener, String engine) { 702 this(context, listener, engine, null, true); 703 } 704 705 /** 706 * Used by the framework to instantiate TextToSpeech objects with a supplied 707 * package name, instead of using {@link android.content.Context#getPackageName()} 708 * 709 * @hide 710 */ TextToSpeech(Context context, OnInitListener listener, String engine, String packageName, boolean useFallback)711 public TextToSpeech(Context context, OnInitListener listener, String engine, 712 String packageName, boolean useFallback) { 713 mContext = context; 714 mInitListener = listener; 715 mRequestedEngine = engine; 716 mUseFallback = useFallback; 717 718 mEarcons = new HashMap<String, Uri>(); 719 mUtterances = new HashMap<CharSequence, Uri>(); 720 mUtteranceProgressListener = null; 721 722 mEnginesHelper = new TtsEngines(mContext); 723 initTts(); 724 } 725 runActionNoReconnect(Action<R> action, R errorResult, String method, boolean onlyEstablishedConnection)726 private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method, 727 boolean onlyEstablishedConnection) { 728 return runAction(action, errorResult, method, false, onlyEstablishedConnection); 729 } 730 runAction(Action<R> action, R errorResult, String method)731 private <R> R runAction(Action<R> action, R errorResult, String method) { 732 return runAction(action, errorResult, method, true, true); 733 } 734 runAction(Action<R> action, R errorResult, String method, boolean reconnect, boolean onlyEstablishedConnection)735 private <R> R runAction(Action<R> action, R errorResult, String method, 736 boolean reconnect, boolean onlyEstablishedConnection) { 737 synchronized (mStartLock) { 738 if (mServiceConnection == null) { 739 Log.w(TAG, method + " failed: not bound to TTS engine"); 740 return errorResult; 741 } 742 return mServiceConnection.runAction(action, errorResult, method, reconnect, 743 onlyEstablishedConnection); 744 } 745 } 746 initTts()747 private int initTts() { 748 // Step 1: Try connecting to the engine that was requested. 749 if (mRequestedEngine != null) { 750 if (mEnginesHelper.isEngineInstalled(mRequestedEngine)) { 751 if (connectToEngine(mRequestedEngine)) { 752 mCurrentEngine = mRequestedEngine; 753 return SUCCESS; 754 } else if (!mUseFallback) { 755 mCurrentEngine = null; 756 dispatchOnInit(ERROR); 757 return ERROR; 758 } 759 } else if (!mUseFallback) { 760 Log.i(TAG, "Requested engine not installed: " + mRequestedEngine); 761 mCurrentEngine = null; 762 dispatchOnInit(ERROR); 763 return ERROR; 764 } 765 } 766 767 // Step 2: Try connecting to the user's default engine. 768 final String defaultEngine = getDefaultEngine(); 769 if (defaultEngine != null && !defaultEngine.equals(mRequestedEngine)) { 770 if (connectToEngine(defaultEngine)) { 771 mCurrentEngine = defaultEngine; 772 return SUCCESS; 773 } 774 } 775 776 // Step 3: Try connecting to the highest ranked engine in the 777 // system. 778 final String highestRanked = mEnginesHelper.getHighestRankedEngineName(); 779 if (highestRanked != null && !highestRanked.equals(mRequestedEngine) && 780 !highestRanked.equals(defaultEngine)) { 781 if (connectToEngine(highestRanked)) { 782 mCurrentEngine = highestRanked; 783 return SUCCESS; 784 } 785 } 786 787 // NOTE: The API currently does not allow the caller to query whether 788 // they are actually connected to any engine. This might fail for various 789 // reasons like if the user disables all her TTS engines. 790 791 mCurrentEngine = null; 792 dispatchOnInit(ERROR); 793 return ERROR; 794 } 795 connectToEngine(String engine)796 private boolean connectToEngine(String engine) { 797 Connection connection = new Connection(); 798 Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 799 intent.setPackage(engine); 800 boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 801 if (!bound) { 802 Log.e(TAG, "Failed to bind to " + engine); 803 return false; 804 } else { 805 Log.i(TAG, "Sucessfully bound to " + engine); 806 mConnectingServiceConnection = connection; 807 return true; 808 } 809 } 810 dispatchOnInit(int result)811 private void dispatchOnInit(int result) { 812 synchronized (mStartLock) { 813 if (mInitListener != null) { 814 mInitListener.onInit(result); 815 mInitListener = null; 816 } 817 } 818 } 819 getCallerIdentity()820 private IBinder getCallerIdentity() { 821 return mServiceConnection.getCallerIdentity(); 822 } 823 824 /** 825 * Releases the resources used by the TextToSpeech engine. 826 * It is good practice for instance to call this method in the onDestroy() method of an Activity 827 * so the TextToSpeech engine can be cleanly stopped. 828 */ shutdown()829 public void shutdown() { 830 // Special case, we are asked to shutdown connection that did finalize its connection. 831 synchronized (mStartLock) { 832 if (mConnectingServiceConnection != null) { 833 mContext.unbindService(mConnectingServiceConnection); 834 mConnectingServiceConnection = null; 835 return; 836 } 837 } 838 839 // Post connection case 840 runActionNoReconnect(new Action<Void>() { 841 @Override 842 public Void run(ITextToSpeechService service) throws RemoteException { 843 service.setCallback(getCallerIdentity(), null); 844 service.stop(getCallerIdentity()); 845 mServiceConnection.disconnect(); 846 // Context#unbindService does not result in a call to 847 // ServiceConnection#onServiceDisconnected. As a result, the 848 // service ends up being destroyed (if there are no other open 849 // connections to it) but the process lives on and the 850 // ServiceConnection continues to refer to the destroyed service. 851 // 852 // This leads to tons of log spam about SynthThread being dead. 853 mServiceConnection = null; 854 mCurrentEngine = null; 855 return null; 856 } 857 }, null, "shutdown", false); 858 } 859 860 /** 861 * Adds a mapping between a string of text and a sound resource in a 862 * package. After a call to this method, subsequent calls to 863 * {@link #speak(String, int, HashMap)} will play the specified sound resource 864 * if it is available, or synthesize the text it is missing. 865 * 866 * @param text 867 * The string of text. Example: <code>"south_south_east"</code> 868 * 869 * @param packagename 870 * Pass the packagename of the application that contains the 871 * resource. If the resource is in your own application (this is 872 * the most common case), then put the packagename of your 873 * application here.<br/> 874 * Example: <b>"com.google.marvin.compass"</b><br/> 875 * The packagename can be found in the AndroidManifest.xml of 876 * your application. 877 * <p> 878 * <code><manifest xmlns:android="..." 879 * package="<b>com.google.marvin.compass</b>"></code> 880 * </p> 881 * 882 * @param resourceId 883 * Example: <code>R.raw.south_south_east</code> 884 * 885 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 886 */ addSpeech(String text, String packagename, @RawRes int resourceId)887 public int addSpeech(String text, String packagename, @RawRes int resourceId) { 888 synchronized (mStartLock) { 889 mUtterances.put(text, makeResourceUri(packagename, resourceId)); 890 return SUCCESS; 891 } 892 } 893 894 /** 895 * Adds a mapping between a CharSequence (may be spanned with TtsSpans) of text 896 * and a sound resource in a package. After a call to this method, subsequent calls to 897 * {@link #speak(String, int, HashMap)} will play the specified sound resource 898 * if it is available, or synthesize the text it is missing. 899 * 900 * @param text 901 * The string of text. Example: <code>"south_south_east"</code> 902 * 903 * @param packagename 904 * Pass the packagename of the application that contains the 905 * resource. If the resource is in your own application (this is 906 * the most common case), then put the packagename of your 907 * application here.<br/> 908 * Example: <b>"com.google.marvin.compass"</b><br/> 909 * The packagename can be found in the AndroidManifest.xml of 910 * your application. 911 * <p> 912 * <code><manifest xmlns:android="..." 913 * package="<b>com.google.marvin.compass</b>"></code> 914 * </p> 915 * 916 * @param resourceId 917 * Example: <code>R.raw.south_south_east</code> 918 * 919 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 920 */ addSpeech(CharSequence text, String packagename, @RawRes int resourceId)921 public int addSpeech(CharSequence text, String packagename, @RawRes int resourceId) { 922 synchronized (mStartLock) { 923 mUtterances.put(text, makeResourceUri(packagename, resourceId)); 924 return SUCCESS; 925 } 926 } 927 928 /** 929 * Adds a mapping between a string of text and a sound file. Using this, it 930 * is possible to add custom pronounciations for a string of text. 931 * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)} 932 * will play the specified sound resource if it is available, or synthesize the text it is 933 * missing. 934 * 935 * @param text 936 * The string of text. Example: <code>"south_south_east"</code> 937 * @param filename 938 * The full path to the sound file (for example: 939 * "/sdcard/mysounds/hello.wav") 940 * 941 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 942 */ addSpeech(String text, String filename)943 public int addSpeech(String text, String filename) { 944 synchronized (mStartLock) { 945 mUtterances.put(text, Uri.parse(filename)); 946 return SUCCESS; 947 } 948 } 949 950 /** 951 * Adds a mapping between a CharSequence (may be spanned with TtsSpans and a sound file. 952 * Using this, it is possible to add custom pronounciations for a string of text. 953 * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)} 954 * will play the specified sound resource if it is available, or synthesize the text it is 955 * missing. 956 * 957 * @param text 958 * The string of text. Example: <code>"south_south_east"</code> 959 * @param file 960 * File object pointing to the sound file. 961 * 962 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 963 */ addSpeech(CharSequence text, File file)964 public int addSpeech(CharSequence text, File file) { 965 synchronized (mStartLock) { 966 mUtterances.put(text, Uri.fromFile(file)); 967 return SUCCESS; 968 } 969 } 970 971 /** 972 * Adds a mapping between a string of text and a sound resource in a 973 * package. Use this to add custom earcons. 974 * 975 * @see #playEarcon(String, int, HashMap) 976 * 977 * @param earcon The name of the earcon. 978 * Example: <code>"[tick]"</code><br/> 979 * 980 * @param packagename 981 * the package name of the application that contains the 982 * resource. This can for instance be the package name of your own application. 983 * Example: <b>"com.google.marvin.compass"</b><br/> 984 * The package name can be found in the AndroidManifest.xml of 985 * the application containing the resource. 986 * <p> 987 * <code><manifest xmlns:android="..." 988 * package="<b>com.google.marvin.compass</b>"></code> 989 * </p> 990 * 991 * @param resourceId 992 * Example: <code>R.raw.tick_snd</code> 993 * 994 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 995 */ addEarcon(String earcon, String packagename, @RawRes int resourceId)996 public int addEarcon(String earcon, String packagename, @RawRes int resourceId) { 997 synchronized(mStartLock) { 998 mEarcons.put(earcon, makeResourceUri(packagename, resourceId)); 999 return SUCCESS; 1000 } 1001 } 1002 1003 /** 1004 * Adds a mapping between a string of text and a sound file. 1005 * Use this to add custom earcons. 1006 * 1007 * @see #playEarcon(String, int, HashMap) 1008 * 1009 * @param earcon 1010 * The name of the earcon. 1011 * Example: <code>"[tick]"</code> 1012 * @param filename 1013 * The full path to the sound file (for example: 1014 * "/sdcard/mysounds/tick.wav") 1015 * 1016 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 1017 * 1018 * @deprecated As of API level 21, replaced by 1019 * {@link #addEarcon(String, File)}. 1020 */ 1021 @Deprecated addEarcon(String earcon, String filename)1022 public int addEarcon(String earcon, String filename) { 1023 synchronized(mStartLock) { 1024 mEarcons.put(earcon, Uri.parse(filename)); 1025 return SUCCESS; 1026 } 1027 } 1028 1029 /** 1030 * Adds a mapping between a string of text and a sound file. 1031 * Use this to add custom earcons. 1032 * 1033 * @see #playEarcon(String, int, HashMap) 1034 * 1035 * @param earcon 1036 * The name of the earcon. 1037 * Example: <code>"[tick]"</code> 1038 * @param file 1039 * File object pointing to the sound file. 1040 * 1041 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 1042 */ addEarcon(String earcon, File file)1043 public int addEarcon(String earcon, File file) { 1044 synchronized(mStartLock) { 1045 mEarcons.put(earcon, Uri.fromFile(file)); 1046 return SUCCESS; 1047 } 1048 } 1049 makeResourceUri(String packageName, int resourceId)1050 private Uri makeResourceUri(String packageName, int resourceId) { 1051 return new Uri.Builder() 1052 .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 1053 .encodedAuthority(packageName) 1054 .appendEncodedPath(String.valueOf(resourceId)) 1055 .build(); 1056 } 1057 1058 /** 1059 * Speaks the text using the specified queuing strategy and speech parameters, the text may 1060 * be spanned with TtsSpans. 1061 * This method is asynchronous, i.e. the method just adds the request to the queue of TTS 1062 * requests and then returns. The synthesis might not have finished (or even started!) at the 1063 * time when this method returns. In order to reliably detect errors during synthesis, 1064 * we recommend setting an utterance progress listener (see 1065 * {@link #setOnUtteranceProgressListener}) and using the 1066 * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. 1067 * 1068 * @param text The string of text to be spoken. No longer than 1069 * {@link #getMaxSpeechInputLength()} characters. 1070 * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 1071 * @param params Parameters for the request. Can be null. 1072 * Supported parameter names: 1073 * {@link Engine#KEY_PARAM_STREAM}, 1074 * {@link Engine#KEY_PARAM_VOLUME}, 1075 * {@link Engine#KEY_PARAM_PAN}. 1076 * Engine specific parameters may be passed in but the parameter keys 1077 * must be prefixed by the name of the engine they are intended for. For example 1078 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 1079 * engine named "com.svox.pico" if it is being used. 1080 * @param utteranceId An unique identifier for this request. 1081 * 1082 * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation. 1083 */ speak(final CharSequence text, final int queueMode, final Bundle params, final String utteranceId)1084 public int speak(final CharSequence text, 1085 final int queueMode, 1086 final Bundle params, 1087 final String utteranceId) { 1088 return runAction(new Action<Integer>() { 1089 @Override 1090 public Integer run(ITextToSpeechService service) throws RemoteException { 1091 Uri utteranceUri = mUtterances.get(text); 1092 if (utteranceUri != null) { 1093 return service.playAudio(getCallerIdentity(), utteranceUri, queueMode, 1094 getParams(params), utteranceId); 1095 } else { 1096 return service.speak(getCallerIdentity(), text, queueMode, getParams(params), 1097 utteranceId); 1098 } 1099 } 1100 }, ERROR, "speak"); 1101 } 1102 1103 /** 1104 * Speaks the string using the specified queuing strategy and speech parameters. 1105 * This method is asynchronous, i.e. the method just adds the request to the queue of TTS 1106 * requests and then returns. The synthesis might not have finished (or even started!) at the 1107 * time when this method returns. In order to reliably detect errors during synthesis, 1108 * we recommend setting an utterance progress listener (see 1109 * {@link #setOnUtteranceProgressListener}) and using the 1110 * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. 1111 * 1112 * @param text The string of text to be spoken. No longer than 1113 * {@link #getMaxSpeechInputLength()} characters. 1114 * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 1115 * @param params Parameters for the request. Can be null. 1116 * Supported parameter names: 1117 * {@link Engine#KEY_PARAM_STREAM}, 1118 * {@link Engine#KEY_PARAM_UTTERANCE_ID}, 1119 * {@link Engine#KEY_PARAM_VOLUME}, 1120 * {@link Engine#KEY_PARAM_PAN}. 1121 * Engine specific parameters may be passed in but the parameter keys 1122 * must be prefixed by the name of the engine they are intended for. For example 1123 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 1124 * engine named "com.svox.pico" if it is being used. 1125 * 1126 * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation. 1127 * @deprecated As of API level 21, replaced by 1128 * {@link #speak(CharSequence, int, Bundle, String)}. 1129 */ 1130 @Deprecated 1131 public int speak(final String text, final int queueMode, final HashMap<String, String> params) { 1132 return speak(text, queueMode, convertParamsHashMaptoBundle(params), 1133 params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); 1134 } 1135 1136 /** 1137 * Plays the earcon using the specified queueing mode and parameters. 1138 * The earcon must already have been added with {@link #addEarcon(String, String)} or 1139 * {@link #addEarcon(String, String, int)}. 1140 * This method is asynchronous, i.e. the method just adds the request to the queue of TTS 1141 * requests and then returns. The synthesis might not have finished (or even started!) at the 1142 * time when this method returns. In order to reliably detect errors during synthesis, 1143 * we recommend setting an utterance progress listener (see 1144 * {@link #setOnUtteranceProgressListener}) and using the 1145 * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. 1146 * 1147 * @param earcon The earcon that should be played 1148 * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 1149 * @param params Parameters for the request. Can be null. 1150 * Supported parameter names: 1151 * {@link Engine#KEY_PARAM_STREAM}, 1152 * Engine specific parameters may be passed in but the parameter keys 1153 * must be prefixed by the name of the engine they are intended for. For example 1154 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 1155 * engine named "com.svox.pico" if it is being used. 1156 * 1157 * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation. 1158 */ 1159 public int playEarcon(final String earcon, final int queueMode, 1160 final Bundle params, final String utteranceId) { 1161 return runAction(new Action<Integer>() { 1162 @Override 1163 public Integer run(ITextToSpeechService service) throws RemoteException { 1164 Uri earconUri = mEarcons.get(earcon); 1165 if (earconUri == null) { 1166 return ERROR; 1167 } 1168 return service.playAudio(getCallerIdentity(), earconUri, queueMode, 1169 getParams(params), utteranceId); 1170 } 1171 }, ERROR, "playEarcon"); 1172 } 1173 1174 /** 1175 * Plays the earcon using the specified queueing mode and parameters. 1176 * The earcon must already have been added with {@link #addEarcon(String, String)} or 1177 * {@link #addEarcon(String, String, int)}. 1178 * This method is asynchronous, i.e. the method just adds the request to the queue of TTS 1179 * requests and then returns. The synthesis might not have finished (or even started!) at the 1180 * time when this method returns. In order to reliably detect errors during synthesis, 1181 * we recommend setting an utterance progress listener (see 1182 * {@link #setOnUtteranceProgressListener}) and using the 1183 * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. 1184 * 1185 * @param earcon The earcon that should be played 1186 * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 1187 * @param params Parameters for the request. Can be null. 1188 * Supported parameter names: 1189 * {@link Engine#KEY_PARAM_STREAM}, 1190 * {@link Engine#KEY_PARAM_UTTERANCE_ID}. 1191 * Engine specific parameters may be passed in but the parameter keys 1192 * must be prefixed by the name of the engine they are intended for. For example 1193 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 1194 * engine named "com.svox.pico" if it is being used. 1195 * 1196 * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation. 1197 * @deprecated As of API level 21, replaced by 1198 * {@link #playEarcon(String, int, Bundle, String)}. 1199 */ 1200 @Deprecated 1201 public int playEarcon(final String earcon, final int queueMode, 1202 final HashMap<String, String> params) { 1203 return playEarcon(earcon, queueMode, convertParamsHashMaptoBundle(params), 1204 params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); 1205 } 1206 1207 /** 1208 * Plays silence for the specified amount of time using the specified 1209 * queue mode. 1210 * This method is asynchronous, i.e. the method just adds the request to the queue of TTS 1211 * requests and then returns. The synthesis might not have finished (or even started!) at the 1212 * time when this method returns. In order to reliably detect errors during synthesis, 1213 * we recommend setting an utterance progress listener (see 1214 * {@link #setOnUtteranceProgressListener}) and using the 1215 * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. 1216 * 1217 * @param durationInMs The duration of the silence. 1218 * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 1219 * @param utteranceId An unique identifier for this request. 1220 * 1221 * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilentUtterance operation. 1222 */ 1223 public int playSilentUtterance(final long durationInMs, final int queueMode, 1224 final String utteranceId) { 1225 return runAction(new Action<Integer>() { 1226 @Override 1227 public Integer run(ITextToSpeechService service) throws RemoteException { 1228 return service.playSilence(getCallerIdentity(), durationInMs, 1229 queueMode, utteranceId); 1230 } 1231 }, ERROR, "playSilentUtterance"); 1232 } 1233 1234 /** 1235 * Plays silence for the specified amount of time using the specified 1236 * queue mode. 1237 * This method is asynchronous, i.e. the method just adds the request to the queue of TTS 1238 * requests and then returns. The synthesis might not have finished (or even started!) at the 1239 * time when this method returns. In order to reliably detect errors during synthesis, 1240 * we recommend setting an utterance progress listener (see 1241 * {@link #setOnUtteranceProgressListener}) and using the 1242 * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. 1243 * 1244 * @param durationInMs The duration of the silence. 1245 * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 1246 * @param params Parameters for the request. Can be null. 1247 * Supported parameter names: 1248 * {@link Engine#KEY_PARAM_UTTERANCE_ID}. 1249 * Engine specific parameters may be passed in but the parameter keys 1250 * must be prefixed by the name of the engine they are intended for. For example 1251 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 1252 * engine named "com.svox.pico" if it is being used. 1253 * 1254 * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation. 1255 * @deprecated As of API level 21, replaced by 1256 * {@link #playSilentUtterance(long, int, String)}. 1257 */ 1258 @Deprecated 1259 public int playSilence(final long durationInMs, final int queueMode, 1260 final HashMap<String, String> params) { 1261 return playSilentUtterance(durationInMs, queueMode, 1262 params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); 1263 } 1264 1265 /** 1266 * Queries the engine for the set of features it supports for a given locale. 1267 * Features can either be framework defined, e.g. 1268 * {@link TextToSpeech.Engine#KEY_FEATURE_NETWORK_SYNTHESIS} or engine specific. 1269 * Engine specific keys must be prefixed by the name of the engine they 1270 * are intended for. These keys can be used as parameters to 1271 * {@link TextToSpeech#speak(String, int, java.util.HashMap)} and 1272 * {@link TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)}. 1273 * 1274 * Features values are strings and their values must meet restrictions described in their 1275 * documentation. 1276 * 1277 * @param locale The locale to query features for. 1278 * @return Set instance. May return {@code null} on error. 1279 * @deprecated As of API level 21, please use voices. In order to query features of the voice, 1280 * call {@link #getVoices()} to retrieve the list of available voices and 1281 * {@link Voice#getFeatures()} to retrieve the set of features. 1282 */ 1283 @Deprecated 1284 public Set<String> getFeatures(final Locale locale) { 1285 return runAction(new Action<Set<String>>() { 1286 @Override 1287 public Set<String> run(ITextToSpeechService service) throws RemoteException { 1288 String[] features = null; 1289 try { 1290 features = service.getFeaturesForLanguage( 1291 locale.getISO3Language(), locale.getISO3Country(), locale.getVariant()); 1292 } catch(MissingResourceException e) { 1293 Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 " + 1294 "country code for locale: " + locale, e); 1295 return null; 1296 } 1297 1298 if (features != null) { 1299 final Set<String> featureSet = new HashSet<String>(); 1300 Collections.addAll(featureSet, features); 1301 return featureSet; 1302 } 1303 return null; 1304 } 1305 }, null, "getFeatures"); 1306 } 1307 1308 /** 1309 * Checks whether the TTS engine is busy speaking. Note that a speech item is 1310 * considered complete once it's audio data has been sent to the audio mixer, or 1311 * written to a file. There might be a finite lag between this point, and when 1312 * the audio hardware completes playback. 1313 * 1314 * @return {@code true} if the TTS engine is speaking. 1315 */ 1316 public boolean isSpeaking() { 1317 return runAction(new Action<Boolean>() { 1318 @Override 1319 public Boolean run(ITextToSpeechService service) throws RemoteException { 1320 return service.isSpeaking(); 1321 } 1322 }, false, "isSpeaking"); 1323 } 1324 1325 /** 1326 * Interrupts the current utterance (whether played or rendered to file) and discards other 1327 * utterances in the queue. 1328 * 1329 * @return {@link #ERROR} or {@link #SUCCESS}. 1330 */ 1331 public int stop() { 1332 return runAction(new Action<Integer>() { 1333 @Override 1334 public Integer run(ITextToSpeechService service) throws RemoteException { 1335 return service.stop(getCallerIdentity()); 1336 } 1337 }, ERROR, "stop"); 1338 } 1339 1340 /** 1341 * Sets the speech rate. 1342 * 1343 * This has no effect on any pre-recorded speech. 1344 * 1345 * @param speechRate Speech rate. {@code 1.0} is the normal speech rate, 1346 * lower values slow down the speech ({@code 0.5} is half the normal speech rate), 1347 * greater values accelerate it ({@code 2.0} is twice the normal speech rate). 1348 * 1349 * @return {@link #ERROR} or {@link #SUCCESS}. 1350 */ 1351 public int setSpeechRate(float speechRate) { 1352 if (speechRate > 0.0f) { 1353 int intRate = (int)(speechRate * 100); 1354 if (intRate > 0) { 1355 synchronized (mStartLock) { 1356 mParams.putInt(Engine.KEY_PARAM_RATE, intRate); 1357 } 1358 return SUCCESS; 1359 } 1360 } 1361 return ERROR; 1362 } 1363 1364 /** 1365 * Sets the speech pitch for the TextToSpeech engine. 1366 * 1367 * This has no effect on any pre-recorded speech. 1368 * 1369 * @param pitch Speech pitch. {@code 1.0} is the normal pitch, 1370 * lower values lower the tone of the synthesized voice, 1371 * greater values increase it. 1372 * 1373 * @return {@link #ERROR} or {@link #SUCCESS}. 1374 */ 1375 public int setPitch(float pitch) { 1376 if (pitch > 0.0f) { 1377 int intPitch = (int)(pitch * 100); 1378 if (intPitch > 0) { 1379 synchronized (mStartLock) { 1380 mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch); 1381 } 1382 return SUCCESS; 1383 } 1384 } 1385 return ERROR; 1386 } 1387 1388 /** 1389 * Sets the audio attributes to be used when speaking text or playing 1390 * back a file. 1391 * 1392 * @param audioAttributes Valid AudioAttributes instance. 1393 * 1394 * @return {@link #ERROR} or {@link #SUCCESS}. 1395 */ 1396 public int setAudioAttributes(AudioAttributes audioAttributes) { 1397 if (audioAttributes != null) { 1398 synchronized (mStartLock) { 1399 mParams.putParcelable(Engine.KEY_PARAM_AUDIO_ATTRIBUTES, 1400 audioAttributes); 1401 } 1402 return SUCCESS; 1403 } 1404 return ERROR; 1405 } 1406 1407 /** 1408 * @return the engine currently in use by this TextToSpeech instance. 1409 * @hide 1410 */ 1411 public String getCurrentEngine() { 1412 return mCurrentEngine; 1413 } 1414 1415 /** 1416 * Returns a Locale instance describing the language currently being used as the default 1417 * Text-to-speech language. 1418 * 1419 * The locale object returned by this method is NOT a valid one. It has identical form to the 1420 * one in {@link #getLanguage()}. Please refer to {@link #getLanguage()} for more information. 1421 * 1422 * @return language, country (if any) and variant (if any) used by the client stored in a 1423 * Locale instance, or {@code null} on error. 1424 * @deprecated As of API level 21, use <code>getDefaultVoice().getLocale()</code> ({@link 1425 * #getDefaultVoice()}) 1426 */ 1427 @Deprecated 1428 public Locale getDefaultLanguage() { 1429 return runAction(new Action<Locale>() { 1430 @Override 1431 public Locale run(ITextToSpeechService service) throws RemoteException { 1432 String[] defaultLanguage = service.getClientDefaultLanguage(); 1433 1434 return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]); 1435 } 1436 }, null, "getDefaultLanguage"); 1437 } 1438 1439 /** 1440 * Sets the text-to-speech language. 1441 * The TTS engine will try to use the closest match to the specified 1442 * language as represented by the Locale, but there is no guarantee that the exact same Locale 1443 * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support 1444 * before choosing the language to use for the next utterances. 1445 * 1446 * This method sets the current voice to the default one for the given Locale; 1447 * {@link #getVoice()} can be used to retrieve it. 1448 * 1449 * @param loc The locale describing the language to be used. 1450 * 1451 * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE}, 1452 * {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE}, 1453 * {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}. 1454 */ 1455 public int setLanguage(final Locale loc) { 1456 return runAction(new Action<Integer>() { 1457 @Override 1458 public Integer run(ITextToSpeechService service) throws RemoteException { 1459 if (loc == null) { 1460 return LANG_NOT_SUPPORTED; 1461 } 1462 String language = null, country = null; 1463 try { 1464 language = loc.getISO3Language(); 1465 } catch (MissingResourceException e) { 1466 Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e); 1467 return LANG_NOT_SUPPORTED; 1468 } 1469 1470 try { 1471 country = loc.getISO3Country(); 1472 } catch (MissingResourceException e) { 1473 Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e); 1474 return LANG_NOT_SUPPORTED; 1475 } 1476 1477 String variant = loc.getVariant(); 1478 1479 // As of API level 21, setLanguage is implemented using setVoice. 1480 // (which, in the default implementation, will call loadLanguage on the service 1481 // interface). 1482 1483 // Sanitize locale using isLanguageAvailable. 1484 int result = service.isLanguageAvailable(language, country, variant); 1485 if (result >= LANG_AVAILABLE) { 1486 // Get the default voice for the locale. 1487 String voiceName = service.getDefaultVoiceNameFor(language, country, variant); 1488 if (TextUtils.isEmpty(voiceName)) { 1489 Log.w(TAG, "Couldn't find the default voice for " + language + "-" + 1490 country + "-" + variant); 1491 return LANG_NOT_SUPPORTED; 1492 } 1493 1494 // Load it. 1495 if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) { 1496 Log.w(TAG, "The service claimed " + language + "-" + country + "-" 1497 + variant + " was available with voice name " + voiceName 1498 + " but loadVoice returned ERROR"); 1499 return LANG_NOT_SUPPORTED; 1500 } 1501 1502 // Set the language/country/variant of the voice, so #getLanguage will return 1503 // the currently set voice locale when called. 1504 Voice voice = getVoice(service, voiceName); 1505 if (voice == null) { 1506 Log.w(TAG, "getDefaultVoiceNameFor returned " + voiceName + " for locale " 1507 + language + "-" + country + "-" + variant 1508 + " but getVoice returns null"); 1509 return LANG_NOT_SUPPORTED; 1510 } 1511 String voiceLanguage = ""; 1512 try { 1513 voiceLanguage = voice.getLocale().getISO3Language(); 1514 } catch (MissingResourceException e) { 1515 Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + 1516 voice.getLocale(), e); 1517 } 1518 1519 String voiceCountry = ""; 1520 try { 1521 voiceCountry = voice.getLocale().getISO3Country(); 1522 } catch (MissingResourceException e) { 1523 Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + 1524 voice.getLocale(), e); 1525 } 1526 mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName); 1527 mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage); 1528 mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry); 1529 mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant()); 1530 } 1531 return result; 1532 } 1533 }, LANG_NOT_SUPPORTED, "setLanguage"); 1534 } 1535 1536 /** 1537 * Returns a Locale instance describing the language currently being used for synthesis 1538 * requests sent to the TextToSpeech engine. 1539 * 1540 * In Android 4.2 and before (API <= 17) this function returns the language that is currently 1541 * being used by the TTS engine. That is the last language set by this or any other 1542 * client by a {@link TextToSpeech#setLanguage} call to the same engine. 1543 * 1544 * In Android versions after 4.2 this function returns the language that is currently being 1545 * used for the synthesis requests sent from this client. That is the last language set 1546 * by a {@link TextToSpeech#setLanguage} call on this instance. 1547 * 1548 * If a voice is set (by {@link #setVoice(Voice)}), getLanguage will return the language of 1549 * the currently set voice. 1550 * 1551 * Please note that the Locale object returned by this method is NOT a valid Locale object. Its 1552 * language field contains a three-letter ISO 639-2/T code (where a proper Locale would use 1553 * a two-letter ISO 639-1 code), and the country field contains a three-letter ISO 3166 country 1554 * code (where a proper Locale would use a two-letter ISO 3166-1 code). 1555 * 1556 * @return language, country (if any) and variant (if any) used by the client stored in a 1557 * Locale instance, or {@code null} on error. 1558 * 1559 * @deprecated As of API level 21, please use <code>getVoice().getLocale()</code> 1560 * ({@link #getVoice()}). 1561 */ 1562 @Deprecated 1563 public Locale getLanguage() { 1564 return runAction(new Action<Locale>() { 1565 @Override 1566 public Locale run(ITextToSpeechService service) { 1567 /* No service call, but we're accessing mParams, hence need for 1568 wrapping it as an Action instance */ 1569 String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, ""); 1570 String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, ""); 1571 String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, ""); 1572 return new Locale(lang, country, variant); 1573 } 1574 }, null, "getLanguage"); 1575 } 1576 1577 /** 1578 * Query the engine about the set of available languages. 1579 */ 1580 public Set<Locale> getAvailableLanguages() { 1581 return runAction(new Action<Set<Locale>>() { 1582 @Override 1583 public Set<Locale> run(ITextToSpeechService service) throws RemoteException { 1584 List<Voice> voices = service.getVoices(); 1585 if (voices == null) { 1586 return new HashSet<Locale>(); 1587 } 1588 HashSet<Locale> locales = new HashSet<Locale>(); 1589 for (Voice voice : voices) { 1590 locales.add(voice.getLocale()); 1591 } 1592 return locales; 1593 } 1594 }, null, "getAvailableLanguages"); 1595 } 1596 1597 /** 1598 * Query the engine about the set of available voices. 1599 * 1600 * Each TTS Engine can expose multiple voices for each locale, each with a different set of 1601 * features. 1602 * 1603 * @see #setVoice(Voice) 1604 * @see Voice 1605 */ 1606 public Set<Voice> getVoices() { 1607 return runAction(new Action<Set<Voice>>() { 1608 @Override 1609 public Set<Voice> run(ITextToSpeechService service) throws RemoteException { 1610 List<Voice> voices = service.getVoices(); 1611 return (voices != null) ? new HashSet<Voice>(voices) : new HashSet<Voice>(); 1612 } 1613 }, null, "getVoices"); 1614 } 1615 1616 /** 1617 * Sets the text-to-speech voice. 1618 * 1619 * @param voice One of objects returned by {@link #getVoices()}. 1620 * 1621 * @return {@link #ERROR} or {@link #SUCCESS}. 1622 * 1623 * @see #getVoices 1624 * @see Voice 1625 */ 1626 public int setVoice(final Voice voice) { 1627 return runAction(new Action<Integer>() { 1628 @Override 1629 public Integer run(ITextToSpeechService service) throws RemoteException { 1630 int result = service.loadVoice(getCallerIdentity(), voice.getName()); 1631 if (result == SUCCESS) { 1632 mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voice.getName()); 1633 1634 // Set the language/country/variant, so #getLanguage will return the voice 1635 // locale when called. 1636 String language = ""; 1637 try { 1638 language = voice.getLocale().getISO3Language(); 1639 } catch (MissingResourceException e) { 1640 Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + 1641 voice.getLocale(), e); 1642 } 1643 1644 String country = ""; 1645 try { 1646 country = voice.getLocale().getISO3Country(); 1647 } catch (MissingResourceException e) { 1648 Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + 1649 voice.getLocale(), e); 1650 } 1651 mParams.putString(Engine.KEY_PARAM_LANGUAGE, language); 1652 mParams.putString(Engine.KEY_PARAM_COUNTRY, country); 1653 mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant()); 1654 } 1655 return result; 1656 } 1657 }, LANG_NOT_SUPPORTED, "setVoice"); 1658 } 1659 1660 /** 1661 * Returns a Voice instance describing the voice currently being used for synthesis 1662 * requests sent to the TextToSpeech engine. 1663 * 1664 * @return Voice instance used by the client, or {@code null} if not set or on error. 1665 * 1666 * @see #getVoices 1667 * @see #setVoice 1668 * @see Voice 1669 */ 1670 public Voice getVoice() { 1671 return runAction(new Action<Voice>() { 1672 @Override 1673 public Voice run(ITextToSpeechService service) throws RemoteException { 1674 String voiceName = mParams.getString(Engine.KEY_PARAM_VOICE_NAME, ""); 1675 if (TextUtils.isEmpty(voiceName)) { 1676 return null; 1677 } 1678 return getVoice(service, voiceName); 1679 } 1680 }, null, "getVoice"); 1681 } 1682 1683 1684 /** 1685 * Returns a Voice instance of the voice with the given voice name. 1686 * 1687 * @return Voice instance with the given voice name, or {@code null} if not set or on error. 1688 * 1689 * @see Voice 1690 */ 1691 private Voice getVoice(ITextToSpeechService service, String voiceName) throws RemoteException { 1692 List<Voice> voices = service.getVoices(); 1693 if (voices == null) { 1694 Log.w(TAG, "getVoices returned null"); 1695 return null; 1696 } 1697 for (Voice voice : voices) { 1698 if (voice.getName().equals(voiceName)) { 1699 return voice; 1700 } 1701 } 1702 Log.w(TAG, "Could not find voice " + voiceName + " in voice list"); 1703 return null; 1704 } 1705 1706 /** 1707 * Returns a Voice instance that's the default voice for the default Text-to-speech language. 1708 * @return The default voice instance for the default language, or {@code null} if not set or 1709 * on error. 1710 */ 1711 public Voice getDefaultVoice() { 1712 return runAction(new Action<Voice>() { 1713 @Override 1714 public Voice run(ITextToSpeechService service) throws RemoteException { 1715 1716 String[] defaultLanguage = service.getClientDefaultLanguage(); 1717 1718 if (defaultLanguage == null || defaultLanguage.length == 0) { 1719 Log.e(TAG, "service.getClientDefaultLanguage() returned empty array"); 1720 return null; 1721 } 1722 String language = defaultLanguage[0]; 1723 String country = (defaultLanguage.length > 1) ? defaultLanguage[1] : ""; 1724 String variant = (defaultLanguage.length > 2) ? defaultLanguage[2] : ""; 1725 1726 // Sanitize the locale using isLanguageAvailable. 1727 int result = service.isLanguageAvailable(language, country, variant); 1728 if (result < LANG_AVAILABLE) { 1729 // The default language is not supported. 1730 return null; 1731 } 1732 1733 // Get the default voice name 1734 String voiceName = service.getDefaultVoiceNameFor(language, country, variant); 1735 if (TextUtils.isEmpty(voiceName)) { 1736 return null; 1737 } 1738 1739 // Find it 1740 List<Voice> voices = service.getVoices(); 1741 if (voices == null) { 1742 return null; 1743 } 1744 for (Voice voice : voices) { 1745 if (voice.getName().equals(voiceName)) { 1746 return voice; 1747 } 1748 } 1749 return null; 1750 } 1751 }, null, "getDefaultVoice"); 1752 } 1753 1754 1755 1756 /** 1757 * Checks if the specified language as represented by the Locale is available and supported. 1758 * 1759 * @param loc The Locale describing the language to be used. 1760 * 1761 * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE}, 1762 * {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE}, 1763 * {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}. 1764 */ 1765 public int isLanguageAvailable(final Locale loc) { 1766 return runAction(new Action<Integer>() { 1767 @Override 1768 public Integer run(ITextToSpeechService service) throws RemoteException { 1769 String language = null, country = null; 1770 1771 try { 1772 language = loc.getISO3Language(); 1773 } catch (MissingResourceException e) { 1774 Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e); 1775 return LANG_NOT_SUPPORTED; 1776 } 1777 1778 try { 1779 country = loc.getISO3Country(); 1780 } catch (MissingResourceException e) { 1781 Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e); 1782 return LANG_NOT_SUPPORTED; 1783 } 1784 1785 return service.isLanguageAvailable(language, country, loc.getVariant()); 1786 } 1787 }, LANG_NOT_SUPPORTED, "isLanguageAvailable"); 1788 } 1789 1790 /** 1791 * Synthesizes the given text to a file using the specified parameters. 1792 * This method is asynchronous, i.e. the method just adds the request to the queue of TTS 1793 * requests and then returns. The synthesis might not have finished (or even started!) at the 1794 * time when this method returns. In order to reliably detect errors during synthesis, 1795 * we recommend setting an utterance progress listener (see 1796 * {@link #setOnUtteranceProgressListener}). 1797 * 1798 * @param text The text that should be synthesized. No longer than 1799 * {@link #getMaxSpeechInputLength()} characters. 1800 * @param params Parameters for the request. Can be null. 1801 * Engine specific parameters may be passed in but the parameter keys 1802 * must be prefixed by the name of the engine they are intended for. For example 1803 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 1804 * engine named "com.svox.pico" if it is being used. 1805 * @param file File to write the generated audio data to. 1806 * @param utteranceId An unique identifier for this request. 1807 * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation. 1808 */ 1809 public int synthesizeToFile(final CharSequence text, final Bundle params, 1810 final File file, final String utteranceId) { 1811 return runAction(new Action<Integer>() { 1812 @Override 1813 public Integer run(ITextToSpeechService service) throws RemoteException { 1814 ParcelFileDescriptor fileDescriptor; 1815 int returnValue; 1816 try { 1817 if(file.exists() && !file.canWrite()) { 1818 Log.e(TAG, "Can't write to " + file); 1819 return ERROR; 1820 } 1821 fileDescriptor = ParcelFileDescriptor.open(file, 1822 ParcelFileDescriptor.MODE_WRITE_ONLY | 1823 ParcelFileDescriptor.MODE_CREATE | 1824 ParcelFileDescriptor.MODE_TRUNCATE); 1825 returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text, 1826 fileDescriptor, getParams(params), utteranceId); 1827 fileDescriptor.close(); 1828 return returnValue; 1829 } catch (FileNotFoundException e) { 1830 Log.e(TAG, "Opening file " + file + " failed", e); 1831 return ERROR; 1832 } catch (IOException e) { 1833 Log.e(TAG, "Closing file " + file + " failed", e); 1834 return ERROR; 1835 } 1836 } 1837 }, ERROR, "synthesizeToFile"); 1838 } 1839 1840 /** 1841 * Synthesizes the given text to a file using the specified parameters. 1842 * This method is asynchronous, i.e. the method just adds the request to the queue of TTS 1843 * requests and then returns. The synthesis might not have finished (or even started!) at the 1844 * time when this method returns. In order to reliably detect errors during synthesis, 1845 * we recommend setting an utterance progress listener (see 1846 * {@link #setOnUtteranceProgressListener}) and using the 1847 * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. 1848 * 1849 * @param text The text that should be synthesized. No longer than 1850 * {@link #getMaxSpeechInputLength()} characters. 1851 * @param params Parameters for the request. Can be null. 1852 * Supported parameter names: 1853 * {@link Engine#KEY_PARAM_UTTERANCE_ID}. 1854 * Engine specific parameters may be passed in but the parameter keys 1855 * must be prefixed by the name of the engine they are intended for. For example 1856 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 1857 * engine named "com.svox.pico" if it is being used. 1858 * @param filename Absolute file filename to write the generated audio data to.It should be 1859 * something like "/sdcard/myappsounds/mysound.wav". 1860 * 1861 * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation. 1862 * @deprecated As of API level 21, replaced by 1863 * {@link #synthesizeToFile(CharSequence, Bundle, File, String)}. 1864 */ 1865 @Deprecated 1866 public int synthesizeToFile(final String text, final HashMap<String, String> params, 1867 final String filename) { 1868 return synthesizeToFile(text, convertParamsHashMaptoBundle(params), 1869 new File(filename), params.get(Engine.KEY_PARAM_UTTERANCE_ID)); 1870 } 1871 1872 private Bundle convertParamsHashMaptoBundle(HashMap<String, String> params) { 1873 if (params != null && !params.isEmpty()) { 1874 Bundle bundle = new Bundle(); 1875 copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM); 1876 copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID); 1877 copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID); 1878 copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME); 1879 copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN); 1880 1881 // Copy feature strings defined by the framework. 1882 copyStringParam(bundle, params, Engine.KEY_FEATURE_NETWORK_SYNTHESIS); 1883 copyStringParam(bundle, params, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS); 1884 copyIntParam(bundle, params, Engine.KEY_FEATURE_NETWORK_TIMEOUT_MS); 1885 copyIntParam(bundle, params, Engine.KEY_FEATURE_NETWORK_RETRIES_COUNT); 1886 1887 // Copy over all parameters that start with the name of the 1888 // engine that we are currently connected to. The engine is 1889 // free to interpret them as it chooses. 1890 if (!TextUtils.isEmpty(mCurrentEngine)) { 1891 for (Map.Entry<String, String> entry : params.entrySet()) { 1892 final String key = entry.getKey(); 1893 if (key != null && key.startsWith(mCurrentEngine)) { 1894 bundle.putString(key, entry.getValue()); 1895 } 1896 } 1897 } 1898 1899 return bundle; 1900 } 1901 return null; 1902 } 1903 1904 private Bundle getParams(Bundle params) { 1905 if (params != null && !params.isEmpty()) { 1906 Bundle bundle = new Bundle(mParams); 1907 bundle.putAll(params); 1908 1909 verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_STREAM); 1910 verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_SESSION_ID); 1911 verifyStringBundleParam(bundle, Engine.KEY_PARAM_UTTERANCE_ID); 1912 verifyFloatBundleParam(bundle, Engine.KEY_PARAM_VOLUME); 1913 verifyFloatBundleParam(bundle, Engine.KEY_PARAM_PAN); 1914 1915 // Copy feature strings defined by the framework. 1916 verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_SYNTHESIS); 1917 verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS); 1918 verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_TIMEOUT_MS); 1919 verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_RETRIES_COUNT); 1920 1921 return bundle; 1922 } else { 1923 return mParams; 1924 } 1925 } 1926 1927 private static boolean verifyIntegerBundleParam(Bundle bundle, String key) { 1928 if (bundle.containsKey(key)) { 1929 if (!(bundle.get(key) instanceof Integer || 1930 bundle.get(key) instanceof Long)) { 1931 bundle.remove(key); 1932 Log.w(TAG, "Synthesis request paramter " + key + " containst value " 1933 + " with invalid type. Should be an Integer or a Long"); 1934 return false; 1935 } 1936 } 1937 return true; 1938 } 1939 1940 private static boolean verifyStringBundleParam(Bundle bundle, String key) { 1941 if (bundle.containsKey(key)) { 1942 if (!(bundle.get(key) instanceof String)) { 1943 bundle.remove(key); 1944 Log.w(TAG, "Synthesis request paramter " + key + " containst value " 1945 + " with invalid type. Should be a String"); 1946 return false; 1947 } 1948 } 1949 return true; 1950 } 1951 1952 private static boolean verifyBooleanBundleParam(Bundle bundle, String key) { 1953 if (bundle.containsKey(key)) { 1954 if (!(bundle.get(key) instanceof Boolean || 1955 bundle.get(key) instanceof String)) { 1956 bundle.remove(key); 1957 Log.w(TAG, "Synthesis request paramter " + key + " containst value " 1958 + " with invalid type. Should be a Boolean or String"); 1959 return false; 1960 } 1961 } 1962 return true; 1963 } 1964 1965 1966 private static boolean verifyFloatBundleParam(Bundle bundle, String key) { 1967 if (bundle.containsKey(key)) { 1968 if (!(bundle.get(key) instanceof Float || 1969 bundle.get(key) instanceof Double)) { 1970 bundle.remove(key); 1971 Log.w(TAG, "Synthesis request paramter " + key + " containst value " 1972 + " with invalid type. Should be a Float or a Double"); 1973 return false; 1974 } 1975 } 1976 return true; 1977 } 1978 1979 private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) { 1980 String value = params.get(key); 1981 if (value != null) { 1982 bundle.putString(key, value); 1983 } 1984 } 1985 1986 private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) { 1987 String valueString = params.get(key); 1988 if (!TextUtils.isEmpty(valueString)) { 1989 try { 1990 int value = Integer.parseInt(valueString); 1991 bundle.putInt(key, value); 1992 } catch (NumberFormatException ex) { 1993 // don't set the value in the bundle 1994 } 1995 } 1996 } 1997 1998 private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) { 1999 String valueString = params.get(key); 2000 if (!TextUtils.isEmpty(valueString)) { 2001 try { 2002 float value = Float.parseFloat(valueString); 2003 bundle.putFloat(key, value); 2004 } catch (NumberFormatException ex) { 2005 // don't set the value in the bundle 2006 } 2007 } 2008 } 2009 2010 /** 2011 * Sets the listener that will be notified when synthesis of an utterance completes. 2012 * 2013 * @param listener The listener to use. 2014 * 2015 * @return {@link #ERROR} or {@link #SUCCESS}. 2016 * 2017 * @deprecated Use {@link #setOnUtteranceProgressListener(UtteranceProgressListener)} 2018 * instead. 2019 */ 2020 @Deprecated 2021 public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) { 2022 mUtteranceProgressListener = UtteranceProgressListener.from(listener); 2023 return TextToSpeech.SUCCESS; 2024 } 2025 2026 /** 2027 * Sets the listener that will be notified of various events related to the 2028 * synthesis of a given utterance. 2029 * 2030 * See {@link UtteranceProgressListener} and 2031 * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}. 2032 * 2033 * @param listener the listener to use. 2034 * @return {@link #ERROR} or {@link #SUCCESS} 2035 */ 2036 public int setOnUtteranceProgressListener(UtteranceProgressListener listener) { 2037 mUtteranceProgressListener = listener; 2038 return TextToSpeech.SUCCESS; 2039 } 2040 2041 /** 2042 * Sets the TTS engine to use. 2043 * 2044 * @deprecated This doesn't inform callers when the TTS engine has been 2045 * initialized. {@link #TextToSpeech(Context, OnInitListener, String)} 2046 * can be used with the appropriate engine name. Also, there is no 2047 * guarantee that the engine specified will be loaded. If it isn't 2048 * installed or disabled, the user / system wide defaults will apply. 2049 * 2050 * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico") 2051 * 2052 * @return {@link #ERROR} or {@link #SUCCESS}. 2053 */ 2054 @Deprecated 2055 public int setEngineByPackageName(String enginePackageName) { 2056 mRequestedEngine = enginePackageName; 2057 return initTts(); 2058 } 2059 2060 /** 2061 * Gets the package name of the default speech synthesis engine. 2062 * 2063 * @return Package name of the TTS engine that the user has chosen 2064 * as their default. 2065 */ 2066 public String getDefaultEngine() { 2067 return mEnginesHelper.getDefaultEngine(); 2068 } 2069 2070 /** 2071 * Checks whether the user's settings should override settings requested 2072 * by the calling application. As of the Ice cream sandwich release, 2073 * user settings never forcibly override the app's settings. 2074 */ 2075 @Deprecated 2076 public boolean areDefaultsEnforced() { 2077 return false; 2078 } 2079 2080 /** 2081 * Gets a list of all installed TTS engines. 2082 * 2083 * @return A list of engine info objects. The list can be empty, but never {@code null}. 2084 */ 2085 public List<EngineInfo> getEngines() { 2086 return mEnginesHelper.getEngines(); 2087 } 2088 2089 private class Connection implements ServiceConnection { 2090 private ITextToSpeechService mService; 2091 2092 private SetupConnectionAsyncTask mOnSetupConnectionAsyncTask; 2093 2094 private boolean mEstablished; 2095 2096 private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() { 2097 public void onStop(String utteranceId, boolean isStarted) throws RemoteException { 2098 UtteranceProgressListener listener = mUtteranceProgressListener; 2099 if (listener != null) { 2100 listener.onStop(utteranceId, isStarted); 2101 } 2102 }; 2103 2104 @Override 2105 public void onSuccess(String utteranceId) { 2106 UtteranceProgressListener listener = mUtteranceProgressListener; 2107 if (listener != null) { 2108 listener.onDone(utteranceId); 2109 } 2110 } 2111 2112 @Override 2113 public void onError(String utteranceId, int errorCode) { 2114 UtteranceProgressListener listener = mUtteranceProgressListener; 2115 if (listener != null) { 2116 listener.onError(utteranceId); 2117 } 2118 } 2119 2120 @Override 2121 public void onStart(String utteranceId) { 2122 UtteranceProgressListener listener = mUtteranceProgressListener; 2123 if (listener != null) { 2124 listener.onStart(utteranceId); 2125 } 2126 } 2127 }; 2128 2129 private class SetupConnectionAsyncTask extends AsyncTask<Void, Void, Integer> { 2130 private final ComponentName mName; 2131 2132 public SetupConnectionAsyncTask(ComponentName name) { 2133 mName = name; 2134 } 2135 2136 @Override 2137 protected Integer doInBackground(Void... params) { 2138 synchronized(mStartLock) { 2139 if (isCancelled()) { 2140 return null; 2141 } 2142 2143 try { 2144 mService.setCallback(getCallerIdentity(), mCallback); 2145 2146 if (mParams.getString(Engine.KEY_PARAM_LANGUAGE) == null) { 2147 String[] defaultLanguage = mService.getClientDefaultLanguage(); 2148 mParams.putString(Engine.KEY_PARAM_LANGUAGE, defaultLanguage[0]); 2149 mParams.putString(Engine.KEY_PARAM_COUNTRY, defaultLanguage[1]); 2150 mParams.putString(Engine.KEY_PARAM_VARIANT, defaultLanguage[2]); 2151 2152 // Get the default voice for the locale. 2153 String defaultVoiceName = mService.getDefaultVoiceNameFor( 2154 defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]); 2155 mParams.putString(Engine.KEY_PARAM_VOICE_NAME, defaultVoiceName); 2156 } 2157 2158 Log.i(TAG, "Set up connection to " + mName); 2159 return SUCCESS; 2160 } catch (RemoteException re) { 2161 Log.e(TAG, "Error connecting to service, setCallback() failed"); 2162 return ERROR; 2163 } 2164 } 2165 } 2166 2167 @Override 2168 protected void onPostExecute(Integer result) { 2169 synchronized(mStartLock) { 2170 if (mOnSetupConnectionAsyncTask == this) { 2171 mOnSetupConnectionAsyncTask = null; 2172 } 2173 mEstablished = true; 2174 dispatchOnInit(result); 2175 } 2176 } 2177 } 2178 2179 @Override 2180 public void onServiceConnected(ComponentName name, IBinder service) { 2181 synchronized(mStartLock) { 2182 mConnectingServiceConnection = null; 2183 2184 Log.i(TAG, "Connected to " + name); 2185 2186 if (mOnSetupConnectionAsyncTask != null) { 2187 mOnSetupConnectionAsyncTask.cancel(false); 2188 } 2189 2190 mService = ITextToSpeechService.Stub.asInterface(service); 2191 mServiceConnection = Connection.this; 2192 2193 mEstablished = false; 2194 mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask(name); 2195 mOnSetupConnectionAsyncTask.execute(); 2196 } 2197 } 2198 2199 public IBinder getCallerIdentity() { 2200 return mCallback; 2201 } 2202 2203 /** 2204 * Clear connection related fields and cancel mOnServiceConnectedAsyncTask if set. 2205 * 2206 * @return true if we cancel mOnSetupConnectionAsyncTask in progress. 2207 */ 2208 private boolean clearServiceConnection() { 2209 synchronized(mStartLock) { 2210 boolean result = false; 2211 if (mOnSetupConnectionAsyncTask != null) { 2212 result = mOnSetupConnectionAsyncTask.cancel(false); 2213 mOnSetupConnectionAsyncTask = null; 2214 } 2215 2216 mService = null; 2217 // If this is the active connection, clear it 2218 if (mServiceConnection == this) { 2219 mServiceConnection = null; 2220 } 2221 return result; 2222 } 2223 } 2224 2225 @Override 2226 public void onServiceDisconnected(ComponentName name) { 2227 Log.i(TAG, "Asked to disconnect from " + name); 2228 if (clearServiceConnection()) { 2229 /* We need to protect against a rare case where engine 2230 * dies just after successful connection - and we process onServiceDisconnected 2231 * before OnServiceConnectedAsyncTask.onPostExecute. onServiceDisconnected cancels 2232 * OnServiceConnectedAsyncTask.onPostExecute and we don't call dispatchOnInit 2233 * with ERROR as argument. 2234 */ 2235 dispatchOnInit(ERROR); 2236 } 2237 } 2238 2239 public void disconnect() { 2240 mContext.unbindService(this); 2241 clearServiceConnection(); 2242 } 2243 2244 public boolean isEstablished() { 2245 return mService != null && mEstablished; 2246 } 2247 2248 public <R> R runAction(Action<R> action, R errorResult, String method, 2249 boolean reconnect, boolean onlyEstablishedConnection) { 2250 synchronized (mStartLock) { 2251 try { 2252 if (mService == null) { 2253 Log.w(TAG, method + " failed: not connected to TTS engine"); 2254 return errorResult; 2255 } 2256 if (onlyEstablishedConnection && !isEstablished()) { 2257 Log.w(TAG, method + " failed: TTS engine connection not fully set up"); 2258 return errorResult; 2259 } 2260 return action.run(mService); 2261 } catch (RemoteException ex) { 2262 Log.e(TAG, method + " failed", ex); 2263 if (reconnect) { 2264 disconnect(); 2265 initTts(); 2266 } 2267 return errorResult; 2268 } 2269 } 2270 } 2271 } 2272 2273 private interface Action<R> { 2274 R run(ITextToSpeechService service) throws RemoteException; 2275 } 2276 2277 /** 2278 * Information about an installed text-to-speech engine. 2279 * 2280 * @see TextToSpeech#getEngines 2281 */ 2282 public static class EngineInfo { 2283 /** 2284 * Engine package name.. 2285 */ 2286 public String name; 2287 /** 2288 * Localized label for the engine. 2289 */ 2290 public String label; 2291 /** 2292 * Icon for the engine. 2293 */ 2294 public int icon; 2295 /** 2296 * Whether this engine is a part of the system 2297 * image. 2298 * 2299 * @hide 2300 */ 2301 public boolean system; 2302 /** 2303 * The priority the engine declares for the the intent filter 2304 * {@code android.intent.action.TTS_SERVICE} 2305 * 2306 * @hide 2307 */ 2308 public int priority; 2309 2310 @Override 2311 public String toString() { 2312 return "EngineInfo{name=" + name + "}"; 2313 } 2314 2315 } 2316 2317 /** 2318 * Limit of length of input string passed to speak and synthesizeToFile. 2319 * 2320 * @see #speak 2321 * @see #synthesizeToFile 2322 */ 2323 public static int getMaxSpeechInputLength() { 2324 return 4000; 2325 } 2326 } 2327