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