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