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