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