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