TextToSpeech.java revision 90e5650f96dabadaaf141beae20a646855073ae1
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 */ 16package android.speech.tts; 17 18import android.annotation.SdkConstant; 19import android.annotation.SdkConstant.SdkConstantType; 20import android.content.ComponentName; 21import android.content.ContentResolver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.ServiceConnection; 25import android.media.AudioManager; 26import android.net.Uri; 27import android.os.Bundle; 28import android.os.IBinder; 29import android.os.RemoteException; 30import android.provider.Settings; 31import android.text.TextUtils; 32import android.util.Log; 33 34import java.util.HashMap; 35import java.util.List; 36import java.util.Locale; 37import 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 */ 49public 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 private final Object mStartLock = new Object(); 444 445 private String mRequestedEngine; 446 private final Map<String, Uri> mEarcons; 447 private final Map<String, Uri> mUtterances; 448 private final Bundle mParams = new Bundle(); 449 private final TtsEngines mEnginesHelper; 450 private volatile String mCurrentEngine = null; 451 452 /** 453 * The constructor for the TextToSpeech class, using the default TTS engine. 454 * This will also initialize the associated TextToSpeech engine if it isn't already running. 455 * 456 * @param context 457 * The context this instance is running in. 458 * @param listener 459 * The {@link TextToSpeech.OnInitListener} that will be called when the 460 * TextToSpeech engine has initialized. 461 */ 462 public TextToSpeech(Context context, OnInitListener listener) { 463 this(context, listener, null); 464 } 465 466 /** 467 * The constructor for the TextToSpeech class, using the given TTS engine. 468 * This will also initialize the associated TextToSpeech engine if it isn't already running. 469 * 470 * @param context 471 * The context this instance is running in. 472 * @param listener 473 * The {@link TextToSpeech.OnInitListener} that will be called when the 474 * TextToSpeech engine has initialized. 475 * @param engine Package name of the TTS engine to use. 476 */ 477 public TextToSpeech(Context context, OnInitListener listener, String engine) { 478 mContext = context; 479 mInitListener = listener; 480 mRequestedEngine = engine; 481 482 mEarcons = new HashMap<String, Uri>(); 483 mUtterances = new HashMap<String, Uri>(); 484 485 mEnginesHelper = new TtsEngines(mContext); 486 initTts(); 487 } 488 489 private String getPackageName() { 490 return mContext.getPackageName(); 491 } 492 493 private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) { 494 return runAction(action, errorResult, method, false); 495 } 496 497 private <R> R runAction(Action<R> action, R errorResult, String method) { 498 return runAction(action, errorResult, method, true); 499 } 500 501 private <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) { 502 synchronized (mStartLock) { 503 if (mServiceConnection == null) { 504 Log.w(TAG, method + " failed: not bound to TTS engine"); 505 return errorResult; 506 } 507 return mServiceConnection.runAction(action, errorResult, method, reconnect); 508 } 509 } 510 511 private int initTts() { 512 // Step 1: Try connecting to the engine that was requested. 513 if (mRequestedEngine != null && mEnginesHelper.isEngineInstalled(mRequestedEngine)) { 514 if (connectToEngine(mRequestedEngine)) { 515 mCurrentEngine = mRequestedEngine; 516 return SUCCESS; 517 } 518 } 519 520 // Step 2: Try connecting to the user's default engine. 521 final String defaultEngine = getDefaultEngine(); 522 if (defaultEngine != null && !defaultEngine.equals(mRequestedEngine)) { 523 if (connectToEngine(defaultEngine)) { 524 mCurrentEngine = defaultEngine; 525 return SUCCESS; 526 } 527 } 528 529 // Step 3: Try connecting to the highest ranked engine in the 530 // system. 531 final String highestRanked = mEnginesHelper.getHighestRankedEngineName(); 532 if (highestRanked != null && !highestRanked.equals(mRequestedEngine) && 533 !highestRanked.equals(defaultEngine)) { 534 if (connectToEngine(highestRanked)) { 535 mCurrentEngine = highestRanked; 536 return SUCCESS; 537 } 538 } 539 540 // NOTE: The API currently does not allow the caller to query whether 541 // they are actually connected to any engine. This might fail for various 542 // reasons like if the user disables all her TTS engines. 543 544 mCurrentEngine = null; 545 dispatchOnInit(ERROR); 546 return ERROR; 547 } 548 549 private boolean connectToEngine(String engine) { 550 Connection connection = new Connection(); 551 Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 552 intent.setPackage(engine); 553 boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 554 if (!bound) { 555 Log.e(TAG, "Failed to bind to " + engine); 556 return false; 557 } else { 558 Log.i(TAG, "Sucessfully bound to " + engine); 559 return true; 560 } 561 } 562 563 private void dispatchOnInit(int result) { 564 synchronized (mStartLock) { 565 if (mInitListener != null) { 566 mInitListener.onInit(result); 567 mInitListener = null; 568 } 569 } 570 } 571 572 /** 573 * Releases the resources used by the TextToSpeech engine. 574 * It is good practice for instance to call this method in the onDestroy() method of an Activity 575 * so the TextToSpeech engine can be cleanly stopped. 576 */ 577 public void shutdown() { 578 runActionNoReconnect(new Action<Void>() { 579 @Override 580 public Void run(ITextToSpeechService service) throws RemoteException { 581 service.setCallback(getPackageName(), null); 582 service.stop(getPackageName()); 583 mServiceConnection.disconnect(); 584 // Context#unbindService does not result in a call to 585 // ServiceConnection#onServiceDisconnected. As a result, the 586 // service ends up being destroyed (if there are no other open 587 // connections to it) but the process lives on and the 588 // ServiceConnection continues to refer to the destroyed service. 589 // 590 // This leads to tons of log spam about SynthThread being dead. 591 mServiceConnection = null; 592 mCurrentEngine = null; 593 return null; 594 } 595 }, null, "shutdown"); 596 } 597 598 /** 599 * Adds a mapping between a string of text and a sound resource in a 600 * package. After a call to this method, subsequent calls to 601 * {@link #speak(String, int, HashMap)} will play the specified sound resource 602 * if it is available, or synthesize the text it is missing. 603 * 604 * @param text 605 * The string of text. Example: <code>"south_south_east"</code> 606 * 607 * @param packagename 608 * Pass the packagename of the application that contains the 609 * resource. If the resource is in your own application (this is 610 * the most common case), then put the packagename of your 611 * application here.<br/> 612 * Example: <b>"com.google.marvin.compass"</b><br/> 613 * The packagename can be found in the AndroidManifest.xml of 614 * your application. 615 * <p> 616 * <code><manifest xmlns:android="..." 617 * package="<b>com.google.marvin.compass</b>"></code> 618 * </p> 619 * 620 * @param resourceId 621 * Example: <code>R.raw.south_south_east</code> 622 * 623 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 624 */ 625 public int addSpeech(String text, String packagename, int resourceId) { 626 synchronized (mStartLock) { 627 mUtterances.put(text, makeResourceUri(packagename, resourceId)); 628 return SUCCESS; 629 } 630 } 631 632 /** 633 * Adds a mapping between a string of text and a sound file. Using this, it 634 * is possible to add custom pronounciations for a string of text. 635 * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)} 636 * will play the specified sound resource if it is available, or synthesize the text it is 637 * missing. 638 * 639 * @param text 640 * The string of text. Example: <code>"south_south_east"</code> 641 * @param filename 642 * The full path to the sound file (for example: 643 * "/sdcard/mysounds/hello.wav") 644 * 645 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 646 */ 647 public int addSpeech(String text, String filename) { 648 synchronized (mStartLock) { 649 mUtterances.put(text, Uri.parse(filename)); 650 return SUCCESS; 651 } 652 } 653 654 655 /** 656 * Adds a mapping between a string of text and a sound resource in a 657 * package. Use this to add custom earcons. 658 * 659 * @see #playEarcon(String, int, HashMap) 660 * 661 * @param earcon The name of the earcon. 662 * Example: <code>"[tick]"</code><br/> 663 * 664 * @param packagename 665 * the package name of the application that contains the 666 * resource. This can for instance be the package name of your own application. 667 * Example: <b>"com.google.marvin.compass"</b><br/> 668 * The package name can be found in the AndroidManifest.xml of 669 * the application containing the resource. 670 * <p> 671 * <code><manifest xmlns:android="..." 672 * package="<b>com.google.marvin.compass</b>"></code> 673 * </p> 674 * 675 * @param resourceId 676 * Example: <code>R.raw.tick_snd</code> 677 * 678 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 679 */ 680 public int addEarcon(String earcon, String packagename, int resourceId) { 681 synchronized(mStartLock) { 682 mEarcons.put(earcon, makeResourceUri(packagename, resourceId)); 683 return SUCCESS; 684 } 685 } 686 687 /** 688 * Adds a mapping between a string of text and a sound file. 689 * Use this to add custom earcons. 690 * 691 * @see #playEarcon(String, int, HashMap) 692 * 693 * @param earcon 694 * The name of the earcon. 695 * Example: <code>"[tick]"</code> 696 * @param filename 697 * The full path to the sound file (for example: 698 * "/sdcard/mysounds/tick.wav") 699 * 700 * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. 701 */ 702 public int addEarcon(String earcon, String filename) { 703 synchronized(mStartLock) { 704 mEarcons.put(earcon, Uri.parse(filename)); 705 return SUCCESS; 706 } 707 } 708 709 private Uri makeResourceUri(String packageName, int resourceId) { 710 return new Uri.Builder() 711 .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 712 .encodedAuthority(packageName) 713 .appendEncodedPath(String.valueOf(resourceId)) 714 .build(); 715 } 716 717 /** 718 * Speaks the string using the specified queuing strategy and speech 719 * parameters. 720 * 721 * @param text The string of text to be spoken. 722 * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 723 * @param params Parameters for the request. Can be null. 724 * Supported parameter names: 725 * {@link Engine#KEY_PARAM_STREAM}, 726 * {@link Engine#KEY_PARAM_UTTERANCE_ID}, 727 * {@link Engine#KEY_PARAM_VOLUME}, 728 * {@link Engine#KEY_PARAM_PAN}. 729 * Engine specific parameters may be passed in but the parameter keys 730 * must be prefixed by the name of the engine they are intended for. For example 731 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 732 * engine named "com.svox.pico" if it is being used. 733 * 734 * @return {@link #ERROR} or {@link #SUCCESS}. 735 */ 736 public int speak(final String text, final int queueMode, final HashMap<String, String> params) { 737 return runAction(new Action<Integer>() { 738 @Override 739 public Integer run(ITextToSpeechService service) throws RemoteException { 740 Uri utteranceUri = mUtterances.get(text); 741 if (utteranceUri != null) { 742 return service.playAudio(getPackageName(), utteranceUri, queueMode, 743 getParams(params)); 744 } else { 745 return service.speak(getPackageName(), text, queueMode, getParams(params)); 746 } 747 } 748 }, ERROR, "speak"); 749 } 750 751 /** 752 * Plays the earcon using the specified queueing mode and parameters. 753 * The earcon must already have been added with {@link #addEarcon(String, String)} or 754 * {@link #addEarcon(String, String, int)}. 755 * 756 * @param earcon The earcon that should be played 757 * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 758 * @param params Parameters for the request. Can be null. 759 * Supported parameter names: 760 * {@link Engine#KEY_PARAM_STREAM}, 761 * {@link Engine#KEY_PARAM_UTTERANCE_ID}. 762 * Engine specific parameters may be passed in but the parameter keys 763 * must be prefixed by the name of the engine they are intended for. For example 764 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 765 * engine named "com.svox.pico" if it is being used. 766 * 767 * @return {@link #ERROR} or {@link #SUCCESS}. 768 */ 769 public int playEarcon(final String earcon, final int queueMode, 770 final HashMap<String, String> params) { 771 return runAction(new Action<Integer>() { 772 @Override 773 public Integer run(ITextToSpeechService service) throws RemoteException { 774 Uri earconUri = mEarcons.get(earcon); 775 if (earconUri == null) { 776 return ERROR; 777 } 778 return service.playAudio(getPackageName(), earconUri, queueMode, 779 getParams(params)); 780 } 781 }, ERROR, "playEarcon"); 782 } 783 784 /** 785 * Plays silence for the specified amount of time using the specified 786 * queue mode. 787 * 788 * @param durationInMs The duration of the silence. 789 * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. 790 * @param params Parameters for the request. Can be null. 791 * Supported parameter names: 792 * {@link Engine#KEY_PARAM_UTTERANCE_ID}. 793 * Engine specific parameters may be passed in but the parameter keys 794 * must be prefixed by the name of the engine they are intended for. For example 795 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 796 * engine named "com.svox.pico" if it is being used. 797 * 798 * @return {@link #ERROR} or {@link #SUCCESS}. 799 */ 800 public int playSilence(final long durationInMs, final int queueMode, 801 final HashMap<String, String> params) { 802 return runAction(new Action<Integer>() { 803 @Override 804 public Integer run(ITextToSpeechService service) throws RemoteException { 805 return service.playSilence(getPackageName(), durationInMs, queueMode, 806 getParams(params)); 807 } 808 }, ERROR, "playSilence"); 809 } 810 811 /** 812 * Checks whether the TTS engine is busy speaking. Note that a speech item is 813 * considered complete once it's audio data has been sent to the audio mixer, or 814 * written to a file. There might be a finite lag between this point, and when 815 * the audio hardware completes playback. 816 * 817 * @return {@code true} if the TTS engine is speaking. 818 */ 819 public boolean isSpeaking() { 820 return runAction(new Action<Boolean>() { 821 @Override 822 public Boolean run(ITextToSpeechService service) throws RemoteException { 823 return service.isSpeaking(); 824 } 825 }, false, "isSpeaking"); 826 } 827 828 /** 829 * Interrupts the current utterance (whether played or rendered to file) and discards other 830 * utterances in the queue. 831 * 832 * @return {@link #ERROR} or {@link #SUCCESS}. 833 */ 834 public int stop() { 835 return runAction(new Action<Integer>() { 836 @Override 837 public Integer run(ITextToSpeechService service) throws RemoteException { 838 return service.stop(getPackageName()); 839 } 840 }, ERROR, "stop"); 841 } 842 843 /** 844 * Sets the speech rate. 845 * 846 * This has no effect on any pre-recorded speech. 847 * 848 * @param speechRate Speech rate. {@code 1.0} is the normal speech rate, 849 * lower values slow down the speech ({@code 0.5} is half the normal speech rate), 850 * greater values accelerate it ({@code 2.0} is twice the normal speech rate). 851 * 852 * @return {@link #ERROR} or {@link #SUCCESS}. 853 */ 854 public int setSpeechRate(float speechRate) { 855 if (speechRate > 0.0f) { 856 int intRate = (int)(speechRate * 100); 857 if (intRate > 0) { 858 synchronized (mStartLock) { 859 mParams.putInt(Engine.KEY_PARAM_RATE, intRate); 860 } 861 return SUCCESS; 862 } 863 } 864 return ERROR; 865 } 866 867 /** 868 * Sets the speech pitch for the TextToSpeech engine. 869 * 870 * This has no effect on any pre-recorded speech. 871 * 872 * @param pitch Speech pitch. {@code 1.0} is the normal pitch, 873 * lower values lower the tone of the synthesized voice, 874 * greater values increase it. 875 * 876 * @return {@link #ERROR} or {@link #SUCCESS}. 877 */ 878 public int setPitch(float pitch) { 879 if (pitch > 0.0f) { 880 int intPitch = (int)(pitch * 100); 881 if (intPitch > 0) { 882 synchronized (mStartLock) { 883 mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch); 884 } 885 return SUCCESS; 886 } 887 } 888 return ERROR; 889 } 890 891 /** 892 * @return the engine currently in use by this TextToSpeech instance. 893 * @hide 894 */ 895 public String getCurrentEngine() { 896 return mCurrentEngine; 897 } 898 899 /** 900 * Sets the text-to-speech language. 901 * The TTS engine will try to use the closest match to the specified 902 * language as represented by the Locale, but there is no guarantee that the exact same Locale 903 * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support 904 * before choosing the language to use for the next utterances. 905 * 906 * @param loc The locale describing the language to be used. 907 * 908 * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE}, 909 * {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE}, 910 * {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}. 911 */ 912 public int setLanguage(final Locale loc) { 913 return runAction(new Action<Integer>() { 914 @Override 915 public Integer run(ITextToSpeechService service) throws RemoteException { 916 if (loc == null) { 917 return LANG_NOT_SUPPORTED; 918 } 919 String language = loc.getISO3Language(); 920 String country = loc.getISO3Country(); 921 String variant = loc.getVariant(); 922 // Check if the language, country, variant are available, and cache 923 // the available parts. 924 // Note that the language is not actually set here, instead it is cached so it 925 // will be associated with all upcoming utterances. 926 int result = service.loadLanguage(language, country, variant); 927 if (result >= LANG_AVAILABLE){ 928 if (result < LANG_COUNTRY_VAR_AVAILABLE) { 929 variant = ""; 930 if (result < LANG_COUNTRY_AVAILABLE) { 931 country = ""; 932 } 933 } 934 mParams.putString(Engine.KEY_PARAM_LANGUAGE, language); 935 mParams.putString(Engine.KEY_PARAM_COUNTRY, country); 936 mParams.putString(Engine.KEY_PARAM_VARIANT, variant); 937 } 938 return result; 939 } 940 }, LANG_NOT_SUPPORTED, "setLanguage"); 941 } 942 943 /** 944 * Returns a Locale instance describing the language currently being used by the TextToSpeech 945 * engine. 946 * 947 * @return language, country (if any) and variant (if any) used by the engine stored in a Locale 948 * instance, or {@code null} on error. 949 */ 950 public Locale getLanguage() { 951 return runAction(new Action<Locale>() { 952 @Override 953 public Locale run(ITextToSpeechService service) throws RemoteException { 954 String[] locStrings = service.getLanguage(); 955 if (locStrings != null && locStrings.length == 3) { 956 return new Locale(locStrings[0], locStrings[1], locStrings[2]); 957 } 958 return null; 959 } 960 }, null, "getLanguage"); 961 } 962 963 /** 964 * Checks if the specified language as represented by the Locale is available and supported. 965 * 966 * @param loc The Locale describing the language to be used. 967 * 968 * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE}, 969 * {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE}, 970 * {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}. 971 */ 972 public int isLanguageAvailable(final Locale loc) { 973 return runAction(new Action<Integer>() { 974 @Override 975 public Integer run(ITextToSpeechService service) throws RemoteException { 976 return service.isLanguageAvailable(loc.getISO3Language(), 977 loc.getISO3Country(), loc.getVariant()); 978 } 979 }, LANG_NOT_SUPPORTED, "isLanguageAvailable"); 980 } 981 982 /** 983 * Synthesizes the given text to a file using the specified parameters. 984 * 985 * @param text The text that should be synthesized 986 * @param params Parameters for the request. Can be null. 987 * Supported parameter names: 988 * {@link Engine#KEY_PARAM_UTTERANCE_ID}. 989 * Engine specific parameters may be passed in but the parameter keys 990 * must be prefixed by the name of the engine they are intended for. For example 991 * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the 992 * engine named "com.svox.pico" if it is being used. 993 * @param filename Absolute file filename to write the generated audio data to.It should be 994 * something like "/sdcard/myappsounds/mysound.wav". 995 * 996 * @return {@link #ERROR} or {@link #SUCCESS}. 997 */ 998 public int synthesizeToFile(final String text, final HashMap<String, String> params, 999 final String filename) { 1000 return runAction(new Action<Integer>() { 1001 @Override 1002 public Integer run(ITextToSpeechService service) throws RemoteException { 1003 return service.synthesizeToFile(getPackageName(), text, filename, 1004 getParams(params)); 1005 } 1006 }, ERROR, "synthesizeToFile"); 1007 } 1008 1009 private Bundle getParams(HashMap<String, String> params) { 1010 if (params != null && !params.isEmpty()) { 1011 Bundle bundle = new Bundle(mParams); 1012 copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM); 1013 copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID); 1014 copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME); 1015 copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN); 1016 1017 // Copy over all parameters that start with the name of the 1018 // engine that we are currently connected to. The engine is 1019 // free to interpret them as it chooses. 1020 if (!TextUtils.isEmpty(mCurrentEngine)) { 1021 for (Map.Entry<String, String> entry : params.entrySet()) { 1022 final String key = entry.getKey(); 1023 if (key != null && key.startsWith(mCurrentEngine)) { 1024 bundle.putString(key, entry.getValue()); 1025 } 1026 } 1027 } 1028 1029 return bundle; 1030 } else { 1031 return mParams; 1032 } 1033 } 1034 1035 private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) { 1036 String value = params.get(key); 1037 if (value != null) { 1038 bundle.putString(key, value); 1039 } 1040 } 1041 1042 private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) { 1043 String valueString = params.get(key); 1044 if (!TextUtils.isEmpty(valueString)) { 1045 try { 1046 int value = Integer.parseInt(valueString); 1047 bundle.putInt(key, value); 1048 } catch (NumberFormatException ex) { 1049 // don't set the value in the bundle 1050 } 1051 } 1052 } 1053 1054 private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) { 1055 String valueString = params.get(key); 1056 if (!TextUtils.isEmpty(valueString)) { 1057 try { 1058 float value = Float.parseFloat(valueString); 1059 bundle.putFloat(key, value); 1060 } catch (NumberFormatException ex) { 1061 // don't set the value in the bundle 1062 } 1063 } 1064 } 1065 1066 /** 1067 * Sets the listener that will be notified when synthesis of an utterance completes. 1068 * 1069 * @param listener The listener to use. 1070 * 1071 * @return {@link #ERROR} or {@link #SUCCESS}. 1072 */ 1073 public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) { 1074 return runAction(new Action<Integer>() { 1075 @Override 1076 public Integer run(ITextToSpeechService service) throws RemoteException { 1077 ITextToSpeechCallback.Stub callback = new ITextToSpeechCallback.Stub() { 1078 public void utteranceCompleted(String utteranceId) { 1079 if (listener != null) { 1080 listener.onUtteranceCompleted(utteranceId); 1081 } 1082 } 1083 }; 1084 service.setCallback(getPackageName(), callback); 1085 return SUCCESS; 1086 } 1087 }, ERROR, "setOnUtteranceCompletedListener"); 1088 } 1089 1090 /** 1091 * Sets the TTS engine to use. 1092 * 1093 * @deprecated This doesn't inform callers when the TTS engine has been 1094 * initialized. {@link #TextToSpeech(Context, OnInitListener, String)} 1095 * can be used with the appropriate engine name. Also, there is no 1096 * guarantee that the engine specified will be loaded. If it isn't 1097 * installed or disabled, the user / system wide defaults will apply. 1098 * 1099 * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico") 1100 * 1101 * @return {@link #ERROR} or {@link #SUCCESS}. 1102 */ 1103 @Deprecated 1104 public int setEngineByPackageName(String enginePackageName) { 1105 mRequestedEngine = enginePackageName; 1106 return initTts(); 1107 } 1108 1109 /** 1110 * Gets the package name of the default speech synthesis engine. 1111 * 1112 * @return Package name of the TTS engine that the user has chosen 1113 * as their default. 1114 */ 1115 public String getDefaultEngine() { 1116 return mEnginesHelper.getDefaultEngine(); 1117 } 1118 1119 /** 1120 * Checks whether the user's settings should override settings requested 1121 * by the calling application. As of the Ice cream sandwich release, 1122 * user settings never forcibly override the app's settings. 1123 */ 1124 public boolean areDefaultsEnforced() { 1125 return false; 1126 } 1127 1128 /** 1129 * Gets a list of all installed TTS engines. 1130 * 1131 * @return A list of engine info objects. The list can be empty, but never {@code null}. 1132 */ 1133 public List<EngineInfo> getEngines() { 1134 return mEnginesHelper.getEngines(); 1135 } 1136 1137 1138 private class Connection implements ServiceConnection { 1139 private ITextToSpeechService mService; 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 dispatchOnInit(SUCCESS); 1151 } 1152 } 1153 1154 public void onServiceDisconnected(ComponentName name) { 1155 synchronized(mStartLock) { 1156 mService = null; 1157 // If this is the active connection, clear it 1158 if (mServiceConnection == this) { 1159 mServiceConnection = null; 1160 } 1161 } 1162 } 1163 1164 public void disconnect() { 1165 mContext.unbindService(this); 1166 } 1167 1168 public <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) { 1169 try { 1170 synchronized (mStartLock) { 1171 if (mService == null) { 1172 Log.w(TAG, method + " failed: not connected to TTS engine"); 1173 return errorResult; 1174 } 1175 return action.run(mService); 1176 } 1177 } catch (RemoteException ex) { 1178 Log.e(TAG, method + " failed", ex); 1179 if (reconnect) { 1180 disconnect(); 1181 initTts(); 1182 } 1183 return errorResult; 1184 } 1185 } 1186 } 1187 1188 private interface Action<R> { 1189 R run(ITextToSpeechService service) throws RemoteException; 1190 } 1191 1192 /** 1193 * Information about an installed text-to-speech engine. 1194 * 1195 * @see TextToSpeech#getEngines 1196 */ 1197 public static class EngineInfo { 1198 /** 1199 * Engine package name.. 1200 */ 1201 public String name; 1202 /** 1203 * Localized label for the engine. 1204 */ 1205 public String label; 1206 /** 1207 * Icon for the engine. 1208 */ 1209 public int icon; 1210 /** 1211 * Whether this engine is a part of the system 1212 * image. 1213 * 1214 * @hide 1215 */ 1216 public boolean system; 1217 /** 1218 * The priority the engine declares for the the intent filter 1219 * {@code android.intent.action.TTS_SERVICE} 1220 * 1221 * @hide 1222 */ 1223 public int priority; 1224 1225 @Override 1226 public String toString() { 1227 return "EngineInfo{name=" + name + "}"; 1228 } 1229 1230 } 1231 1232} 1233