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