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.SdkConstant; 19 import android.annotation.SdkConstant.SdkConstantType; 20 import android.content.ComponentName; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.ServiceConnection; 25 import android.media.AudioManager; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.provider.Settings; 31 import android.text.TextUtils; 32 import android.util.Log; 33 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Locale; 37 import java.util.Map; 38 39 /** 40 * 41 * Synthesizes speech from text for immediate playback or to create a sound file. 42 * <p>A TextToSpeech instance can only be used to synthesize text once it has completed its 43 * initialization. Implement the {@link TextToSpeech.OnInitListener} to be 44 * notified of the completion of the initialization.<br> 45 * When you are done using the TextToSpeech instance, call the {@link #shutdown()} method 46 * to release the native resources used by the TextToSpeech engine. 47 * 48 */ 49 public class TextToSpeech { 50 51 private static final String TAG = "TextToSpeech"; 52 53 /** 54 * Denotes a successful operation. 55 */ 56 public static final int SUCCESS = 0; 57 /** 58 * Denotes a generic operation failure. 59 */ 60 public static final int ERROR = -1; 61 62 /** 63 * Queue mode where all entries in the playback queue (media to be played 64 * and text to be synthesized) are dropped and replaced by the new entry. 65 * Queues are flushed with respect to a given calling app. Entries in the queue 66 * from other callees are not discarded. 67 */ 68 public static final int QUEUE_FLUSH = 0; 69 /** 70 * Queue mode where the new entry is added at the end of the playback queue. 71 */ 72 public static final int QUEUE_ADD = 1; 73 /** 74 * Queue mode where the entire playback queue is purged. This is different 75 * from {@link #QUEUE_FLUSH} in that all entries are purged, not just entries 76 * from a given caller. 77 * 78 * @hide 79 */ 80 static final int QUEUE_DESTROY = 2; 81 82 /** 83 * Denotes the language is available exactly as specified by the locale. 84 */ 85 public static final int LANG_COUNTRY_VAR_AVAILABLE = 2; 86 87 /** 88 * Denotes the language is available for the language and country specified 89 * by the locale, but not the variant. 90 */ 91 public static final int LANG_COUNTRY_AVAILABLE = 1; 92 93 /** 94 * Denotes the language is available for the language by the locale, 95 * but not the country and variant. 96 */ 97 public static final int LANG_AVAILABLE = 0; 98 99 /** 100 * Denotes the language data is missing. 101 */ 102 public static final int LANG_MISSING_DATA = -1; 103 104 /** 105 * Denotes the language is not supported. 106 */ 107 public static final int LANG_NOT_SUPPORTED = -2; 108 109 /** 110 * Broadcast Action: The TextToSpeech synthesizer has completed processing 111 * of all the text in the speech queue. 112 * 113 * Note that this notifies callers when the <b>engine</b> has finished has 114 * processing text data. Audio playback might not have completed (or even started) 115 * at this point. If you wish to be notified when this happens, see 116 * {@link OnUtteranceCompletedListener}. 117 */ 118 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 119 public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = 120 "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED"; 121 122 /** 123 * Interface definition of a callback to be invoked indicating the completion of the 124 * TextToSpeech engine initialization. 125 */ 126 public interface OnInitListener { 127 /** 128 * Called to signal the completion of the TextToSpeech engine initialization. 129 * 130 * @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}. 131 */ 132 public void onInit(int status); 133 } 134 135 /** 136 * Listener that will be called when the TTS service has 137 * completed synthesizing an utterance. This is only called if the utterance 138 * has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}). 139 */ 140 public interface OnUtteranceCompletedListener { 141 /** 142 * Called when an utterance has been synthesized. 143 * 144 * @param utteranceId the identifier of the utterance. 145 */ 146 public void onUtteranceCompleted(String utteranceId); 147 } 148 149 /** 150 * Constants and parameter names for controlling text-to-speech. 151 */ 152 public class Engine { 153 154 /** 155 * Default speech rate. 156 * @hide 157 */ 158 public static final int DEFAULT_RATE = 100; 159 160 /** 161 * Default pitch. 162 * @hide 163 */ 164 public static final int DEFAULT_PITCH = 100; 165 166 /** 167 * Default volume. 168 * @hide 169 */ 170 public static final float DEFAULT_VOLUME = 1.0f; 171 172 /** 173 * Default pan (centered). 174 * @hide 175 */ 176 public static final float DEFAULT_PAN = 0.0f; 177 178 /** 179 * Default value for {@link Settings.Secure#TTS_USE_DEFAULTS}. 180 * @hide 181 */ 182 public static final int USE_DEFAULTS = 0; // false 183 184 /** 185 * Package name of the default TTS engine. 186 * 187 * @hide 188 * @deprecated No longer in use, the default engine is determined by 189 * the sort order defined in {@link TtsEngines}. Note that 190 * this doesn't "break" anything because there is no guarantee that 191 * the engine specified below is installed on a given build, let 192 * alone be the default. 193 */ 194 @Deprecated 195 public static final String DEFAULT_ENGINE = "com.svox.pico"; 196 197 /** 198 * Default audio stream used when playing synthesized speech. 199 */ 200 public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC; 201 202 /** 203 * Indicates success when checking the installation status of the resources used by the 204 * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 205 */ 206 public static final int CHECK_VOICE_DATA_PASS = 1; 207 208 /** 209 * Indicates failure when checking the installation status of the resources used by the 210 * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 211 */ 212 public static final int CHECK_VOICE_DATA_FAIL = 0; 213 214 /** 215 * Indicates erroneous data when checking the installation status of the resources used by 216 * the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 217 */ 218 public static final int CHECK_VOICE_DATA_BAD_DATA = -1; 219 220 /** 221 * Indicates missing resources when checking the installation status of the resources used 222 * by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 223 */ 224 public static final int CHECK_VOICE_DATA_MISSING_DATA = -2; 225 226 /** 227 * Indicates missing storage volume when checking the installation status of the resources 228 * used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent. 229 */ 230 public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3; 231 232 /** 233 * Intent for starting a TTS service. Services that handle this intent must 234 * extend {@link TextToSpeechService}. Normal applications should not use this intent 235 * directly, instead they should talk to the TTS service using the the methods in this 236 * class. 237 */ 238 @SdkConstant(SdkConstantType.SERVICE_ACTION) 239 public static final String INTENT_ACTION_TTS_SERVICE = 240 "android.intent.action.TTS_SERVICE"; 241 242 /** 243 * Name under which a text to speech engine publishes information about itself. 244 * This meta-data should reference an XML resource containing a 245 * <code><{@link android.R.styleable#TextToSpeechEngine tts-engine}></code> 246 * tag. 247 */ 248 public static final String SERVICE_META_DATA = "android.speech.tts"; 249 250 // intents to ask engine to install data or check its data 251 /** 252 * Activity Action: Triggers the platform TextToSpeech engine to 253 * start the activity that installs the resource files on the device 254 * that are required for TTS to be operational. Since the installation 255 * of the data can be interrupted or declined by the user, the application 256 * shouldn't expect successful installation upon return from that intent, 257 * and if need be, should check installation status with 258 * {@link #ACTION_CHECK_TTS_DATA}. 259 */ 260 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 261 public static final String ACTION_INSTALL_TTS_DATA = 262 "android.speech.tts.engine.INSTALL_TTS_DATA"; 263 264 /** 265 * Broadcast Action: broadcast to signal the completion of the installation of 266 * the data files used by the synthesis engine. Success or failure is indicated in the 267 * {@link #EXTRA_TTS_DATA_INSTALLED} extra. 268 */ 269 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 270 public static final String ACTION_TTS_DATA_INSTALLED = 271 "android.speech.tts.engine.TTS_DATA_INSTALLED"; 272 273 /** 274 * Activity Action: Starts the activity from the platform TextToSpeech 275 * engine to verify the proper installation and availability of the 276 * resource files on the system. Upon completion, the activity will 277 * return one of the following codes: 278 * {@link #CHECK_VOICE_DATA_PASS}, 279 * {@link #CHECK_VOICE_DATA_FAIL}, 280 * {@link #CHECK_VOICE_DATA_BAD_DATA}, 281 * {@link #CHECK_VOICE_DATA_MISSING_DATA}, or 282 * {@link #CHECK_VOICE_DATA_MISSING_VOLUME}. 283 * <p> Moreover, the data received in the activity result will contain the following 284 * fields: 285 * <ul> 286 * <li>{@link #EXTRA_VOICE_DATA_ROOT_DIRECTORY} which 287 * indicates the path to the location of the resource files,</li> 288 * <li>{@link #EXTRA_VOICE_DATA_FILES} which contains 289 * the list of all the resource files,</li> 290 * <li>and {@link #EXTRA_VOICE_DATA_FILES_INFO} which 291 * contains, for each resource file, the description of the language covered by 292 * the file in the xxx-YYY format, where xxx is the 3-letter ISO language code, 293 * and YYY is the 3-letter ISO country code.</li> 294 * </ul> 295 */ 296 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 297 public static final String ACTION_CHECK_TTS_DATA = 298 "android.speech.tts.engine.CHECK_TTS_DATA"; 299 300 /** 301 * Activity intent for getting some sample text to use for demonstrating TTS. 302 * 303 * @hide This intent was used by engines written against the old API. 304 * Not sure if it should be exposed. 305 */ 306 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 307 public static final String ACTION_GET_SAMPLE_TEXT = 308 "android.speech.tts.engine.GET_SAMPLE_TEXT"; 309 310 // extras for a TTS engine's check data activity 311 /** 312 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where 313 * the TextToSpeech engine specifies the path to its resources. 314 */ 315 public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot"; 316 317 /** 318 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where 319 * the TextToSpeech engine specifies the file names of its resources under the 320 * resource path. 321 */ 322 public static final String EXTRA_VOICE_DATA_FILES = "dataFiles"; 323 324 /** 325 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where 326 * the TextToSpeech engine specifies the locale associated with each resource file. 327 */ 328 public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo"; 329 330 /** 331 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where 332 * the TextToSpeech engine returns an ArrayList<String> of all the available voices. 333 * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are 334 * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE"). 335 */ 336 public static final String EXTRA_AVAILABLE_VOICES = "availableVoices"; 337 338 /** 339 * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where 340 * the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices. 341 * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are 342 * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE"). 343 */ 344 public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices"; 345 346 /** 347 * Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the 348 * caller indicates to the TextToSpeech engine which specific sets of voice data to 349 * check for by sending an ArrayList<String> of the voices that are of interest. 350 * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are 351 * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE"). 352 */ 353 public static final String EXTRA_CHECK_VOICE_DATA_FOR = "checkVoiceDataFor"; 354 355 // extras for a TTS engine's data installation 356 /** 357 * Extra information received with the {@link #ACTION_TTS_DATA_INSTALLED} intent. 358 * It indicates whether the data files for the synthesis engine were successfully 359 * installed. The installation was initiated with the {@link #ACTION_INSTALL_TTS_DATA} 360 * intent. The possible values for this extra are 361 * {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}. 362 */ 363 public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled"; 364 365 // keys for the parameters passed with speak commands. Hidden keys are used internally 366 // to maintain engine state for each TextToSpeech instance. 367 /** 368 * @hide 369 */ 370 public static final String KEY_PARAM_RATE = "rate"; 371 372 /** 373 * @hide 374 */ 375 public static final String KEY_PARAM_LANGUAGE = "language"; 376 377 /** 378 * @hide 379 */ 380 public static final String KEY_PARAM_COUNTRY = "country"; 381 382 /** 383 * @hide 384 */ 385 public static final String KEY_PARAM_VARIANT = "variant"; 386 387 /** 388 * @hide 389 */ 390 public static final String KEY_PARAM_ENGINE = "engine"; 391 392 /** 393 * @hide 394 */ 395 public static final String KEY_PARAM_PITCH = "pitch"; 396 397 /** 398 * Parameter key to specify the audio stream type to be used when speaking text 399 * or playing back a file. The value should be one of the STREAM_ constants 400 * defined in {@link AudioManager}. 401 * 402 * @see TextToSpeech#speak(String, int, HashMap) 403 * @see TextToSpeech#playEarcon(String, int, HashMap) 404 */ 405 public static final String KEY_PARAM_STREAM = "streamType"; 406 407 /** 408 * Parameter key to identify an utterance in the 409 * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been 410 * spoken, a file has been played back or a silence duration has elapsed. 411 * 412 * @see TextToSpeech#speak(String, int, HashMap) 413 * @see TextToSpeech#playEarcon(String, int, HashMap) 414 * @see TextToSpeech#synthesizeToFile(String, HashMap, String) 415 */ 416 public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId"; 417 418 /** 419 * Parameter key to specify the speech volume relative to the current stream type 420 * volume used when speaking text. Volume is specified as a float ranging from 0 to 1 421 * where 0 is silence, and 1 is the maximum volume (the default behavior). 422 * 423 * @see TextToSpeech#speak(String, int, HashMap) 424 * @see TextToSpeech#playEarcon(String, int, HashMap) 425 */ 426 public static final String KEY_PARAM_VOLUME = "volume"; 427 428 /** 429 * Parameter key to specify how the speech is panned from left to right when speaking text. 430 * Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan, 431 * 0 to center (the default behavior), and +1 to hard-right. 432 * 433 * @see TextToSpeech#speak(String, int, HashMap) 434 * @see TextToSpeech#playEarcon(String, int, HashMap) 435 */ 436 public static final String KEY_PARAM_PAN = "pan"; 437 438 } 439 440 private final Context mContext; 441 private Connection mServiceConnection; 442 private OnInitListener mInitListener; 443 // Written from an unspecified application thread, read from 444 // a binder thread. 445 private volatile OnUtteranceCompletedListener mUtteranceCompletedListener; 446 private final Object mStartLock = new Object(); 447 448 private String mRequestedEngine; 449 private final Map<String, Uri> mEarcons; 450 private final Map<String, Uri> mUtterances; 451 private final Bundle mParams = new Bundle(); 452 private final TtsEngines mEnginesHelper; 453 private volatile String mCurrentEngine = null; 454 455 /** 456 * The constructor for the TextToSpeech class, using the default TTS engine. 457 * This will also initialize the associated TextToSpeech engine if it isn't already running. 458 * 459 * @param context 460 * The context this instance is running in. 461 * @param listener 462 * The {@link TextToSpeech.OnInitListener} that will be called when the 463 * TextToSpeech engine has initialized. 464 */ 465 public TextToSpeech(Context context, OnInitListener listener) { 466 this(context, listener, null); 467 } 468 469 /** 470 * The constructor for the TextToSpeech class, using the given TTS engine. 471 * This will also initialize the associated TextToSpeech engine if it isn't already running. 472 * 473 * @param context 474 * The context this instance is running in. 475 * @param listener 476 * The {@link TextToSpeech.OnInitListener} that will be called when the 477 * TextToSpeech engine has initialized. 478 * @param engine Package name of the TTS engine to use. 479 */ 480 public TextToSpeech(Context context, OnInitListener listener, String engine) { 481 mContext = context; 482 mInitListener = listener; 483 mRequestedEngine = engine; 484 485 mEarcons = new HashMap<String, Uri>(); 486 mUtterances = new HashMap<String, Uri>(); 487 488 mEnginesHelper = new TtsEngines(mContext); 489 initTts(); 490 } 491 492 private String getPackageName() { 493 return mContext.getPackageName(); 494 } 495 496 private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) { 497 return runAction(action, errorResult, method, false); 498 } 499 500 private <R> R runAction(Action<R> action, R errorResult, String method) { 501 return runAction(action, errorResult, method, true); 502 } 503 504 private <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) { 505 synchronized (mStartLock) { 506 if (mServiceConnection == null) { 507 Log.w(TAG, method + " failed: not bound to TTS engine"); 508 return errorResult; 509 } 510 return mServiceConnection.runAction(action, errorResult, method, reconnect); 511 } 512 } 513 514 private int initTts() { 515 // Step 1: Try connecting to the engine that was requested. 516 if (mRequestedEngine != null && mEnginesHelper.isEngineInstalled(mRequestedEngine)) { 517 if (connectToEngine(mRequestedEngine)) { 518 mCurrentEngine = mRequestedEngine; 519 return SUCCESS; 520 } 521 } 522 523 // Step 2: Try connecting to the user's default engine. 524 final String defaultEngine = getDefaultEngine(); 525 if (defaultEngine != null && !defaultEngine.equals(mRequestedEngine)) { 526 if (connectToEngine(defaultEngine)) { 527 mCurrentEngine = defaultEngine; 528 return SUCCESS; 529 } 530 } 531 532 // Step 3: Try connecting to the highest ranked engine in the 533 // system. 534 final String highestRanked = mEnginesHelper.getHighestRankedEngineName(); 535 if (highestRanked != null && !highestRanked.equals(mRequestedEngine) && 536 !highestRanked.equals(defaultEngine)) { 537 if (connectToEngine(highestRanked)) { 538 mCurrentEngine = highestRanked; 539 return SUCCESS; 540 } 541 } 542 543 // NOTE: The API currently does not allow the caller to query whether 544 // they are actually connected to any engine. This might fail for various 545 // reasons like if the user disables all her TTS engines. 546 547 mCurrentEngine = null; 548 dispatchOnInit(ERROR); 549 return ERROR; 550 } 551 552 private boolean connectToEngine(String engine) { 553 Connection connection = new Connection(); 554 Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 555 intent.setPackage(engine); 556 boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 557 if (!bound) { 558 Log.e(TAG, "Failed to bind to " + engine); 559 return false; 560 } else { 561 Log.i(TAG, "Sucessfully bound to " + engine); 562 return true; 563 } 564 } 565 566 private void dispatchOnInit(int result) { 567 synchronized (mStartLock) { 568 if (mInitListener != null) { 569 mInitListener.onInit(result); 570 mInitListener = null; 571 } 572 } 573 } 574 575 /** 576 * Releases the resources used by the TextToSpeech engine. 577 * It is good practice for instance to call this method in the onDestroy() method of an Activity 578 * so the TextToSpeech engine can be cleanly stopped. 579 */ 580 public void shutdown() { 581 runActionNoReconnect(new Action<Void>() { 582 @Override 583 public Void run(ITextToSpeechService service) throws RemoteException { 584 service.setCallback(getPackageName(), null); 585 service.stop(getPackageName()); 586 mServiceConnection.disconnect(); 587 // Context#unbindService does not result in a call to 588 // ServiceConnection#onServiceDisconnected. As a result, the 589 // service ends up being destroyed (if there are no other open 590 // connections to it) but the process lives on and the 591 // ServiceConnection continues to refer to the destroyed service. 592 // 593 // This leads to tons of log spam about SynthThread being dead. 594 mServiceConnection = null; 595 mCurrentEngine = null; 596 return null; 597 } 598 }, null, "shutdown"); 599 } 600 601 /** 602 * Adds a mapping between a string of text and a sound resource in a 603 * package. After a call to this method, subsequent calls to 604 * {@link #speak(String, int, HashMap)} will play the specified sound resource 605 * if it is available, or synthesize the text it is missing. 606 * 607 * @param text 608 * The string of text. Example: <code>"south_south_east"</code> 609 * 610 * @param packagename 611 * Pass the packagename of the application that contains the 612 * resource. If the resource is in your own application (this is 613 * the most common case), then put the packagename of your 614 * application here.<br/> 615 * Example: <b>"com.google.marvin.compass"</b><br/> 616 * The packagename can be found in the AndroidManifest.xml of 617 * your application. 618 * <p> 619 * <code><manifest xmlns:android="..." 620 * package="<b>com.google.marvin.compass</b>"></code> 621 * </p> 622 * 623 * @param resourceId 624 * Example: <code>R.raw.south_south_east</code> 625 * 626 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 627 */ 628 public int addSpeech(String text, String packagename, int resourceId) { 629 synchronized (mStartLock) { 630 mUtterances.put(text, makeResourceUri(packagename, resourceId)); 631 return SUCCESS; 632 } 633 } 634 635 /** 636 * Adds a mapping between a string of text and a sound file. Using this, it 637 * is possible to add custom pronounciations for a string of text. 638 * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)} 639 * will play the specified sound resource if it is available, or synthesize the text it is 640 * missing. 641 * 642 * @param text 643 * The string of text. Example: <code>"south_south_east"</code> 644 * @param filename 645 * The full path to the sound file (for example: 646 * "/sdcard/mysounds/hello.wav") 647 * 648 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 649 */ 650 public int addSpeech(String text, String filename) { 651 synchronized (mStartLock) { 652 mUtterances.put(text, Uri.parse(filename)); 653 return SUCCESS; 654 } 655 } 656 657 658 /** 659 * Adds a mapping between a string of text and a sound resource in a 660 * package. Use this to add custom earcons. 661 * 662 * @see #playEarcon(String, int, HashMap) 663 * 664 * @param earcon The name of the earcon. 665 * Example: <code>"[tick]"</code><br/> 666 * 667 * @param packagename 668 * the package name of the application that contains the 669 * resource. This can for instance be the package name of your own application. 670 * Example: <b>"com.google.marvin.compass"</b><br/> 671 * The package name can be found in the AndroidManifest.xml of 672 * the application containing the resource. 673 * <p> 674 * <code><manifest xmlns:android="..." 675 * package="<b>com.google.marvin.compass</b>"></code> 676 * </p> 677 * 678 * @param resourceId 679 * Example: <code>R.raw.tick_snd</code> 680 * 681 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 682 */ 683 public int addEarcon(String earcon, String packagename, int resourceId) { 684 synchronized(mStartLock) { 685 mEarcons.put(earcon, makeResourceUri(packagename, resourceId)); 686 return SUCCESS; 687 } 688 } 689 690 /** 691 * Adds a mapping between a string of text and a sound file. 692 * Use this to add custom earcons. 693 * 694 * @see #playEarcon(String, int, HashMap) 695 * 696 * @param earcon 697 * The name of the earcon. 698 * Example: <code>"[tick]"</code> 699 * @param filename 700 * The full path to the sound file (for example: 701 * "/sdcard/mysounds/tick.wav") 702 * 703 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 704 */ 705 public int addEarcon(String earcon, String filename) { 706 synchronized(mStartLock) { 707 mEarcons.put(earcon, Uri.parse(filename)); 708 return SUCCESS; 709 } 710 } 711 712 private Uri makeResourceUri(String packageName, int resourceId) { 713 return new Uri.Builder() 714 .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 715 .encodedAuthority(packageName) 716 .appendEncodedPath(String.valueOf(resourceId)) 717 .build(); 718 } 719 720 /** 721 * Speaks the string using the specified queuing strategy and speech 722 * parameters. 723 * 724 * @param text The string of text to be spoken. 725 * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 726 * @param params Parameters for the request. Can be null. 727 * Supported parameter names: 728 * {@link Engine#KEY_PARAM_STREAM}, 729 * {@link Engine#KEY_PARAM_UTTERANCE_ID}, 730 * {@link Engine#KEY_PARAM_VOLUME}, 731 * {@link Engine#KEY_PARAM_PAN}. 732 * Engine specific parameters may be passed in but the parameter keys 733 * must be prefixed by the name of the engine they are intended for. For example 734 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 735 * engine named "com.svox.pico" if it is being used. 736 * 737 * @return {@link #ERROR} or {@link #SUCCESS}. 738 */ 739 public int speak(final String text, final int queueMode, final HashMap<String, String> params) { 740 return runAction(new Action<Integer>() { 741 @Override 742 public Integer run(ITextToSpeechService service) throws RemoteException { 743 Uri utteranceUri = mUtterances.get(text); 744 if (utteranceUri != null) { 745 return service.playAudio(getPackageName(), utteranceUri, queueMode, 746 getParams(params)); 747 } else { 748 return service.speak(getPackageName(), text, queueMode, getParams(params)); 749 } 750 } 751 }, ERROR, "speak"); 752 } 753 754 /** 755 * Plays the earcon using the specified queueing mode and parameters. 756 * The earcon must already have been added with {@link #addEarcon(String, String)} or 757 * {@link #addEarcon(String, String, int)}. 758 * 759 * @param earcon The earcon that should be played 760 * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 761 * @param params Parameters for the request. Can be null. 762 * Supported parameter names: 763 * {@link Engine#KEY_PARAM_STREAM}, 764 * {@link Engine#KEY_PARAM_UTTERANCE_ID}. 765 * Engine specific parameters may be passed in but the parameter keys 766 * must be prefixed by the name of the engine they are intended for. For example 767 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 768 * engine named "com.svox.pico" if it is being used. 769 * 770 * @return {@link #ERROR} or {@link #SUCCESS}. 771 */ 772 public int playEarcon(final String earcon, final int queueMode, 773 final HashMap<String, String> params) { 774 return runAction(new Action<Integer>() { 775 @Override 776 public Integer run(ITextToSpeechService service) throws RemoteException { 777 Uri earconUri = mEarcons.get(earcon); 778 if (earconUri == null) { 779 return ERROR; 780 } 781 return service.playAudio(getPackageName(), earconUri, queueMode, 782 getParams(params)); 783 } 784 }, ERROR, "playEarcon"); 785 } 786 787 /** 788 * Plays silence for the specified amount of time using the specified 789 * queue mode. 790 * 791 * @param durationInMs The duration of the silence. 792 * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 793 * @param params Parameters for the request. Can be null. 794 * Supported parameter names: 795 * {@link Engine#KEY_PARAM_UTTERANCE_ID}. 796 * Engine specific parameters may be passed in but the parameter keys 797 * must be prefixed by the name of the engine they are intended for. For example 798 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 799 * engine named "com.svox.pico" if it is being used. 800 * 801 * @return {@link #ERROR} or {@link #SUCCESS}. 802 */ 803 public int playSilence(final long durationInMs, final int queueMode, 804 final HashMap<String, String> params) { 805 return runAction(new Action<Integer>() { 806 @Override 807 public Integer run(ITextToSpeechService service) throws RemoteException { 808 return service.playSilence(getPackageName(), durationInMs, queueMode, 809 getParams(params)); 810 } 811 }, ERROR, "playSilence"); 812 } 813 814 /** 815 * Checks whether the TTS engine is busy speaking. Note that a speech item is 816 * considered complete once it's audio data has been sent to the audio mixer, or 817 * written to a file. There might be a finite lag between this point, and when 818 * the audio hardware completes playback. 819 * 820 * @return {@code true} if the TTS engine is speaking. 821 */ 822 public boolean isSpeaking() { 823 return runAction(new Action<Boolean>() { 824 @Override 825 public Boolean run(ITextToSpeechService service) throws RemoteException { 826 return service.isSpeaking(); 827 } 828 }, false, "isSpeaking"); 829 } 830 831 /** 832 * Interrupts the current utterance (whether played or rendered to file) and discards other 833 * utterances in the queue. 834 * 835 * @return {@link #ERROR} or {@link #SUCCESS}. 836 */ 837 public int stop() { 838 return runAction(new Action<Integer>() { 839 @Override 840 public Integer run(ITextToSpeechService service) throws RemoteException { 841 return service.stop(getPackageName()); 842 } 843 }, ERROR, "stop"); 844 } 845 846 /** 847 * Sets the speech rate. 848 * 849 * This has no effect on any pre-recorded speech. 850 * 851 * @param speechRate Speech rate. {@code 1.0} is the normal speech rate, 852 * lower values slow down the speech ({@code 0.5} is half the normal speech rate), 853 * greater values accelerate it ({@code 2.0} is twice the normal speech rate). 854 * 855 * @return {@link #ERROR} or {@link #SUCCESS}. 856 */ 857 public int setSpeechRate(float speechRate) { 858 if (speechRate > 0.0f) { 859 int intRate = (int)(speechRate * 100); 860 if (intRate > 0) { 861 synchronized (mStartLock) { 862 mParams.putInt(Engine.KEY_PARAM_RATE, intRate); 863 } 864 return SUCCESS; 865 } 866 } 867 return ERROR; 868 } 869 870 /** 871 * Sets the speech pitch for the TextToSpeech engine. 872 * 873 * This has no effect on any pre-recorded speech. 874 * 875 * @param pitch Speech pitch. {@code 1.0} is the normal pitch, 876 * lower values lower the tone of the synthesized voice, 877 * greater values increase it. 878 * 879 * @return {@link #ERROR} or {@link #SUCCESS}. 880 */ 881 public int setPitch(float pitch) { 882 if (pitch > 0.0f) { 883 int intPitch = (int)(pitch * 100); 884 if (intPitch > 0) { 885 synchronized (mStartLock) { 886 mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch); 887 } 888 return SUCCESS; 889 } 890 } 891 return ERROR; 892 } 893 894 /** 895 * @return the engine currently in use by this TextToSpeech instance. 896 * @hide 897 */ 898 public String getCurrentEngine() { 899 return mCurrentEngine; 900 } 901 902 /** 903 * Sets the text-to-speech language. 904 * The TTS engine will try to use the closest match to the specified 905 * language as represented by the Locale, but there is no guarantee that the exact same Locale 906 * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support 907 * before choosing the language to use for the next utterances. 908 * 909 * @param loc The locale describing the language to be used. 910 * 911 * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE}, 912 * {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE}, 913 * {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}. 914 */ 915 public int setLanguage(final Locale loc) { 916 return runAction(new Action<Integer>() { 917 @Override 918 public Integer run(ITextToSpeechService service) throws RemoteException { 919 if (loc == null) { 920 return LANG_NOT_SUPPORTED; 921 } 922 String language = loc.getISO3Language(); 923 String country = loc.getISO3Country(); 924 String variant = loc.getVariant(); 925 // Check if the language, country, variant are available, and cache 926 // the available parts. 927 // Note that the language is not actually set here, instead it is cached so it 928 // will be associated with all upcoming utterances. 929 int result = service.loadLanguage(language, country, variant); 930 if (result >= LANG_AVAILABLE){ 931 if (result < LANG_COUNTRY_VAR_AVAILABLE) { 932 variant = ""; 933 if (result < LANG_COUNTRY_AVAILABLE) { 934 country = ""; 935 } 936 } 937 mParams.putString(Engine.KEY_PARAM_LANGUAGE, language); 938 mParams.putString(Engine.KEY_PARAM_COUNTRY, country); 939 mParams.putString(Engine.KEY_PARAM_VARIANT, variant); 940 } 941 return result; 942 } 943 }, LANG_NOT_SUPPORTED, "setLanguage"); 944 } 945 946 /** 947 * Returns a Locale instance describing the language currently being used by the TextToSpeech 948 * engine. 949 * 950 * @return language, country (if any) and variant (if any) used by the engine stored in a Locale 951 * instance, or {@code null} on error. 952 */ 953 public Locale getLanguage() { 954 return runAction(new Action<Locale>() { 955 @Override 956 public Locale run(ITextToSpeechService service) throws RemoteException { 957 String[] locStrings = service.getLanguage(); 958 if (locStrings != null && locStrings.length == 3) { 959 return new Locale(locStrings[0], locStrings[1], locStrings[2]); 960 } 961 return null; 962 } 963 }, null, "getLanguage"); 964 } 965 966 /** 967 * Checks if the specified language as represented by the Locale is available and supported. 968 * 969 * @param loc The Locale describing the language to be used. 970 * 971 * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE}, 972 * {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE}, 973 * {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}. 974 */ 975 public int isLanguageAvailable(final Locale loc) { 976 return runAction(new Action<Integer>() { 977 @Override 978 public Integer run(ITextToSpeechService service) throws RemoteException { 979 return service.isLanguageAvailable(loc.getISO3Language(), 980 loc.getISO3Country(), loc.getVariant()); 981 } 982 }, LANG_NOT_SUPPORTED, "isLanguageAvailable"); 983 } 984 985 /** 986 * Synthesizes the given text to a file using the specified parameters. 987 * 988 * @param text The text that should be synthesized 989 * @param params Parameters for the request. Can be null. 990 * Supported parameter names: 991 * {@link Engine#KEY_PARAM_UTTERANCE_ID}. 992 * Engine specific parameters may be passed in but the parameter keys 993 * must be prefixed by the name of the engine they are intended for. For example 994 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 995 * engine named "com.svox.pico" if it is being used. 996 * @param filename Absolute file filename to write the generated audio data to.It should be 997 * something like "/sdcard/myappsounds/mysound.wav". 998 * 999 * @return {@link #ERROR} or {@link #SUCCESS}. 1000 */ 1001 public int synthesizeToFile(final String text, final HashMap<String, String> params, 1002 final String filename) { 1003 return runAction(new Action<Integer>() { 1004 @Override 1005 public Integer run(ITextToSpeechService service) throws RemoteException { 1006 return service.synthesizeToFile(getPackageName(), text, filename, 1007 getParams(params)); 1008 } 1009 }, ERROR, "synthesizeToFile"); 1010 } 1011 1012 private Bundle getParams(HashMap<String, String> params) { 1013 if (params != null && !params.isEmpty()) { 1014 Bundle bundle = new Bundle(mParams); 1015 copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM); 1016 copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID); 1017 copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME); 1018 copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN); 1019 1020 // Copy over all parameters that start with the name of the 1021 // engine that we are currently connected to. The engine is 1022 // free to interpret them as it chooses. 1023 if (!TextUtils.isEmpty(mCurrentEngine)) { 1024 for (Map.Entry<String, String> entry : params.entrySet()) { 1025 final String key = entry.getKey(); 1026 if (key != null && key.startsWith(mCurrentEngine)) { 1027 bundle.putString(key, entry.getValue()); 1028 } 1029 } 1030 } 1031 1032 return bundle; 1033 } else { 1034 return mParams; 1035 } 1036 } 1037 1038 private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) { 1039 String value = params.get(key); 1040 if (value != null) { 1041 bundle.putString(key, value); 1042 } 1043 } 1044 1045 private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) { 1046 String valueString = params.get(key); 1047 if (!TextUtils.isEmpty(valueString)) { 1048 try { 1049 int value = Integer.parseInt(valueString); 1050 bundle.putInt(key, value); 1051 } catch (NumberFormatException ex) { 1052 // don't set the value in the bundle 1053 } 1054 } 1055 } 1056 1057 private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) { 1058 String valueString = params.get(key); 1059 if (!TextUtils.isEmpty(valueString)) { 1060 try { 1061 float value = Float.parseFloat(valueString); 1062 bundle.putFloat(key, value); 1063 } catch (NumberFormatException ex) { 1064 // don't set the value in the bundle 1065 } 1066 } 1067 } 1068 1069 /** 1070 * Sets the listener that will be notified when synthesis of an utterance completes. 1071 * 1072 * @param listener The listener to use. 1073 * 1074 * @return {@link #ERROR} or {@link #SUCCESS}. 1075 */ 1076 public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) { 1077 mUtteranceCompletedListener = listener; 1078 return TextToSpeech.SUCCESS; 1079 } 1080 1081 /** 1082 * Sets the TTS engine to use. 1083 * 1084 * @deprecated This doesn't inform callers when the TTS engine has been 1085 * initialized. {@link #TextToSpeech(Context, OnInitListener, String)} 1086 * can be used with the appropriate engine name. Also, there is no 1087 * guarantee that the engine specified will be loaded. If it isn't 1088 * installed or disabled, the user / system wide defaults will apply. 1089 * 1090 * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico") 1091 * 1092 * @return {@link #ERROR} or {@link #SUCCESS}. 1093 */ 1094 @Deprecated 1095 public int setEngineByPackageName(String enginePackageName) { 1096 mRequestedEngine = enginePackageName; 1097 return initTts(); 1098 } 1099 1100 /** 1101 * Gets the package name of the default speech synthesis engine. 1102 * 1103 * @return Package name of the TTS engine that the user has chosen 1104 * as their default. 1105 */ 1106 public String getDefaultEngine() { 1107 return mEnginesHelper.getDefaultEngine(); 1108 } 1109 1110 /** 1111 * Checks whether the user's settings should override settings requested 1112 * by the calling application. As of the Ice cream sandwich release, 1113 * user settings never forcibly override the app's settings. 1114 */ 1115 public boolean areDefaultsEnforced() { 1116 return false; 1117 } 1118 1119 /** 1120 * Gets a list of all installed TTS engines. 1121 * 1122 * @return A list of engine info objects. The list can be empty, but never {@code null}. 1123 */ 1124 public List<EngineInfo> getEngines() { 1125 return mEnginesHelper.getEngines(); 1126 } 1127 1128 1129 private class Connection implements ServiceConnection { 1130 private ITextToSpeechService mService; 1131 private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() { 1132 @Override 1133 public void utteranceCompleted(String utteranceId) { 1134 OnUtteranceCompletedListener listener = mUtteranceCompletedListener; 1135 if (listener != null) { 1136 listener.onUtteranceCompleted(utteranceId); 1137 } 1138 } 1139 }; 1140 1141 public void onServiceConnected(ComponentName name, IBinder service) { 1142 Log.i(TAG, "Connected to " + name); 1143 synchronized(mStartLock) { 1144 if (mServiceConnection != null) { 1145 // Disconnect any previous service connection 1146 mServiceConnection.disconnect(); 1147 } 1148 mServiceConnection = this; 1149 mService = ITextToSpeechService.Stub.asInterface(service); 1150 try { 1151 mService.setCallback(getPackageName(), mCallback); 1152 dispatchOnInit(SUCCESS); 1153 } catch (RemoteException re) { 1154 Log.e(TAG, "Error connecting to service, setCallback() failed"); 1155 dispatchOnInit(ERROR); 1156 } 1157 } 1158 } 1159 1160 public void onServiceDisconnected(ComponentName name) { 1161 synchronized(mStartLock) { 1162 mService = null; 1163 // If this is the active connection, clear it 1164 if (mServiceConnection == this) { 1165 mServiceConnection = null; 1166 } 1167 } 1168 } 1169 1170 public void disconnect() { 1171 mContext.unbindService(this); 1172 } 1173 1174 public <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) { 1175 try { 1176 synchronized (mStartLock) { 1177 if (mService == null) { 1178 Log.w(TAG, method + " failed: not connected to TTS engine"); 1179 return errorResult; 1180 } 1181 return action.run(mService); 1182 } 1183 } catch (RemoteException ex) { 1184 Log.e(TAG, method + " failed", ex); 1185 if (reconnect) { 1186 disconnect(); 1187 initTts(); 1188 } 1189 return errorResult; 1190 } 1191 } 1192 } 1193 1194 private interface Action<R> { 1195 R run(ITextToSpeechService service) throws RemoteException; 1196 } 1197 1198 /** 1199 * Information about an installed text-to-speech engine. 1200 * 1201 * @see TextToSpeech#getEngines 1202 */ 1203 public static class EngineInfo { 1204 /** 1205 * Engine package name.. 1206 */ 1207 public String name; 1208 /** 1209 * Localized label for the engine. 1210 */ 1211 public String label; 1212 /** 1213 * Icon for the engine. 1214 */ 1215 public int icon; 1216 /** 1217 * Whether this engine is a part of the system 1218 * image. 1219 * 1220 * @hide 1221 */ 1222 public boolean system; 1223 /** 1224 * The priority the engine declares for the the intent filter 1225 * {@code android.intent.action.TTS_SERVICE} 1226 * 1227 * @hide 1228 */ 1229 public int priority; 1230 1231 @Override 1232 public String toString() { 1233 return "EngineInfo{name=" + name + "}"; 1234 } 1235 1236 } 1237 1238 } 1239