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