TtsService.java revision a9c5e4bf2639f8f09be8bace4230613b7b689f0e
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.tts; 17 18import android.app.Service; 19import android.content.ContentResolver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.SharedPreferences; 23import android.content.pm.PackageManager; 24import android.content.pm.PackageManager.NameNotFoundException; 25import android.media.MediaPlayer; 26import android.media.MediaPlayer.OnCompletionListener; 27import android.net.Uri; 28import android.os.IBinder; 29import android.os.RemoteCallbackList; 30import android.os.RemoteException; 31import android.preference.PreferenceManager; 32import android.speech.tts.ITts.Stub; 33import android.speech.tts.ITtsCallback; 34import android.speech.tts.TextToSpeech; 35import android.util.Log; 36import java.util.ArrayList; 37import java.util.Arrays; 38import java.util.HashMap; 39import java.util.Locale; 40import java.util.concurrent.locks.ReentrantLock; 41import java.util.concurrent.TimeUnit; 42 43 44/** 45 * @hide Synthesizes speech from text. This is implemented as a service so that 46 * other applications can call the TTS without needing to bundle the TTS 47 * in the build. 48 * 49 */ 50public class TtsService extends Service implements OnCompletionListener { 51 52 private static class SpeechItem { 53 public static final int TEXT = 0; 54 public static final int EARCON = 1; 55 public static final int SILENCE = 2; 56 public static final int TEXT_TO_FILE = 3; 57 public String mText = ""; 58 public ArrayList<String> mParams = null; 59 public int mType = TEXT; 60 public long mDuration = 0; 61 public String mFilename = null; 62 public String callingApp = ""; 63 64 public SpeechItem(String source, String text, ArrayList<String> params, int itemType) { 65 mText = text; 66 mParams = params; 67 mType = itemType; 68 callingApp = source; 69 } 70 71 public SpeechItem(String source, long silenceTime) { 72 mDuration = silenceTime; 73 mType = SILENCE; 74 callingApp = source; 75 } 76 77 public SpeechItem(String source, String text, ArrayList<String> params, int itemType, String filename) { 78 mText = text; 79 mParams = params; 80 mType = itemType; 81 mFilename = filename; 82 callingApp = source; 83 } 84 85 } 86 87 /** 88 * Contains the information needed to access a sound resource; the name of 89 * the package that contains the resource and the resID of the resource 90 * within that package. 91 */ 92 private static class SoundResource { 93 public String mSourcePackageName = null; 94 public int mResId = -1; 95 public String mFilename = null; 96 97 public SoundResource(String packageName, int id) { 98 mSourcePackageName = packageName; 99 mResId = id; 100 mFilename = null; 101 } 102 103 public SoundResource(String file) { 104 mSourcePackageName = null; 105 mResId = -1; 106 mFilename = file; 107 } 108 } 109 110 private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000; 111 private static final int MAX_FILENAME_LENGTH = 250; 112 113 private static final String ACTION = "android.intent.action.START_TTS_SERVICE"; 114 private static final String CATEGORY = "android.intent.category.TTS"; 115 private static final String PKGNAME = "android.tts"; 116 117 final RemoteCallbackList<android.speech.tts.ITtsCallback> mCallbacks = new RemoteCallbackList<ITtsCallback>(); 118 119 private Boolean mIsSpeaking; 120 private ArrayList<SpeechItem> mSpeechQueue; 121 private HashMap<String, SoundResource> mEarcons; 122 private HashMap<String, SoundResource> mUtterances; 123 private MediaPlayer mPlayer; 124 private TtsService mSelf; 125 126 private ContentResolver mResolver; 127 128 private final ReentrantLock speechQueueLock = new ReentrantLock(); 129 private final ReentrantLock synthesizerLock = new ReentrantLock(); 130 131 private SynthProxy nativeSynth; 132 @Override 133 public void onCreate() { 134 super.onCreate(); 135 //Log.i("TTS", "TTS starting"); 136 137 mResolver = getContentResolver(); 138 139 String soLibPath = "/system/lib/libttspico.so"; 140 nativeSynth = new SynthProxy(soLibPath); 141 142 mSelf = this; 143 mIsSpeaking = false; 144 145 mEarcons = new HashMap<String, SoundResource>(); 146 mUtterances = new HashMap<String, SoundResource>(); 147 148 mSpeechQueue = new ArrayList<SpeechItem>(); 149 mPlayer = null; 150 151 setDefaultSettings(); 152 } 153 154 @Override 155 public void onDestroy() { 156 super.onDestroy(); 157 // Don't hog the media player 158 cleanUpPlayer(); 159 160 nativeSynth.shutdown(); 161 162 // Unregister all callbacks. 163 mCallbacks.kill(); 164 } 165 166 167 private void setDefaultSettings() { 168 setLanguage("", this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant()); 169 170 // speech rate 171 setSpeechRate("", getDefaultRate()); 172 } 173 174 175 private boolean isDefaultEnforced() { 176 return (android.provider.Settings.Secure.getInt(mResolver, 177 android.provider.Settings.Secure.TTS_USE_DEFAULTS, 178 TextToSpeech.Engine.FALLBACK_TTS_USE_DEFAULTS) 179 == 1 ); 180 } 181 182 183 private int getDefaultRate() { 184 return android.provider.Settings.Secure.getInt(mResolver, 185 android.provider.Settings.Secure.TTS_DEFAULT_RATE, 186 TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_RATE); 187 } 188 189 190 private String getDefaultLanguage() { 191 String defaultLang = android.provider.Settings.Secure.getString(mResolver, 192 android.provider.Settings.Secure.TTS_DEFAULT_LANG); 193 if (defaultLang == null) { 194 // no setting found, use the current Locale to determine the default language 195 return Locale.getDefault().getISO3Language(); 196 } else { 197 return defaultLang; 198 } 199 } 200 201 202 private String getDefaultCountry() { 203 String defaultCountry = android.provider.Settings.Secure.getString(mResolver, 204 android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY); 205 if (defaultCountry == null) { 206 // no setting found, use the current Locale to determine the default country 207 return Locale.getDefault().getISO3Country(); 208 } else { 209 return defaultCountry; 210 } 211 } 212 213 214 private String getDefaultLocVariant() { 215 String defaultVar = android.provider.Settings.Secure.getString(mResolver, 216 android.provider.Settings.Secure.TTS_DEFAULT_VARIANT); 217 if (defaultVar == null) { 218 // no setting found, use the current Locale to determine the default variant 219 return Locale.getDefault().getVariant(); 220 } else { 221 return defaultVar; 222 } 223 } 224 225 226 private int setSpeechRate(String callingApp, int rate) { 227 if (isDefaultEnforced()) { 228 return nativeSynth.setSpeechRate(getDefaultRate()); 229 } else { 230 return nativeSynth.setSpeechRate(rate); 231 } 232 } 233 234 235 private int setPitch(String callingApp, int pitch) { 236 return nativeSynth.setPitch(pitch); 237 } 238 239 240 private int isLanguageAvailable(String lang, String country, String variant) { 241 //Log.v("TTS", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")"); 242 return nativeSynth.isLanguageAvailable(lang, country, variant); 243 } 244 245 246 private String[] getLanguage() { 247 return nativeSynth.getLanguage(); 248 } 249 250 251 private int setLanguage(String callingApp, String lang, String country, String variant) { 252 //Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")"); 253 if (isDefaultEnforced()) { 254 return nativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(), 255 getDefaultLocVariant()); 256 } else { 257 return nativeSynth.setLanguage(lang, country, variant); 258 } 259 } 260 261 262 /** 263 * Adds a sound resource to the TTS. 264 * 265 * @param text 266 * The text that should be associated with the sound resource 267 * @param packageName 268 * The name of the package which has the sound resource 269 * @param resId 270 * The resource ID of the sound within its package 271 */ 272 private void addSpeech(String callingApp, String text, String packageName, int resId) { 273 mUtterances.put(text, new SoundResource(packageName, resId)); 274 } 275 276 /** 277 * Adds a sound resource to the TTS. 278 * 279 * @param text 280 * The text that should be associated with the sound resource 281 * @param filename 282 * The filename of the sound resource. This must be a complete 283 * path like: (/sdcard/mysounds/mysoundbite.mp3). 284 */ 285 private void addSpeech(String callingApp, String text, String filename) { 286 mUtterances.put(text, new SoundResource(filename)); 287 } 288 289 /** 290 * Adds a sound resource to the TTS as an earcon. 291 * 292 * @param earcon 293 * The text that should be associated with the sound resource 294 * @param packageName 295 * The name of the package which has the sound resource 296 * @param resId 297 * The resource ID of the sound within its package 298 */ 299 private void addEarcon(String callingApp, String earcon, String packageName, int resId) { 300 mEarcons.put(earcon, new SoundResource(packageName, resId)); 301 } 302 303 /** 304 * Adds a sound resource to the TTS as an earcon. 305 * 306 * @param earcon 307 * The text that should be associated with the sound resource 308 * @param filename 309 * The filename of the sound resource. This must be a complete 310 * path like: (/sdcard/mysounds/mysoundbite.mp3). 311 */ 312 private void addEarcon(String callingApp, String earcon, String filename) { 313 mEarcons.put(earcon, new SoundResource(filename)); 314 } 315 316 /** 317 * Speaks the given text using the specified queueing mode and parameters. 318 * 319 * @param text 320 * The text that should be spoken 321 * @param queueMode 322 * 0 for no queue (interrupts all previous utterances), 1 for 323 * queued 324 * @param params 325 * An ArrayList of parameters. This is not implemented for all 326 * engines. 327 */ 328 private int speak(String callingApp, String text, int queueMode, ArrayList<String> params) { 329 if (queueMode == 0) { 330 stop(callingApp); 331 } 332 mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT)); 333 if (!mIsSpeaking) { 334 processSpeechQueue(); 335 } 336 return TextToSpeech.TTS_SUCCESS; 337 } 338 339 /** 340 * Plays the earcon using the specified queueing mode and parameters. 341 * 342 * @param earcon 343 * The earcon that should be played 344 * @param queueMode 345 * 0 for no queue (interrupts all previous utterances), 1 for 346 * queued 347 * @param params 348 * An ArrayList of parameters. This is not implemented for all 349 * engines. 350 */ 351 private int playEarcon(String callingApp, String earcon, int queueMode, 352 ArrayList<String> params) { 353 if (queueMode == 0) { 354 stop(callingApp); 355 } 356 mSpeechQueue.add(new SpeechItem(callingApp, earcon, params, SpeechItem.EARCON)); 357 if (!mIsSpeaking) { 358 processSpeechQueue(); 359 } 360 return TextToSpeech.TTS_SUCCESS; 361 } 362 363 /** 364 * Stops all speech output and removes any utterances still in the queue. 365 */ 366 private int stop(String callingApp) { 367 int result = TextToSpeech.TTS_ERROR; 368 boolean speechQueueAvailable = false; 369 try{ 370 // If the queue is locked for more than 1 second, 371 // something has gone very wrong with processSpeechQueue. 372 speechQueueAvailable = speechQueueLock.tryLock(1000, TimeUnit.MILLISECONDS); 373 if (speechQueueAvailable) { 374 Log.i("TTS", "Stopping"); 375 for (int i = mSpeechQueue.size() - 1; i > -1; i--){ 376 if (mSpeechQueue.get(i).callingApp.equals(callingApp)){ 377 mSpeechQueue.remove(i); 378 } 379 } 380 381 result = nativeSynth.stop(); 382 mIsSpeaking = false; 383 if (mPlayer != null) { 384 try { 385 mPlayer.stop(); 386 } catch (IllegalStateException e) { 387 // Do nothing, the player is already stopped. 388 } 389 } 390 Log.i("TTS", "Stopped"); 391 } 392 } catch (InterruptedException e) { 393 Log.e("TTS stop", "tryLock interrupted"); 394 e.printStackTrace(); 395 } finally { 396 // This check is needed because finally will always run; even if the 397 // method returns somewhere in the try block. 398 if (speechQueueAvailable) { 399 speechQueueLock.unlock(); 400 } 401 return result; 402 } 403 } 404 405 public void onCompletion(MediaPlayer arg0) { 406 processSpeechQueue(); 407 } 408 409 private int playSilence(String callingApp, long duration, int queueMode, 410 ArrayList<String> params) { 411 if (queueMode == 0) { 412 stop(callingApp); 413 } 414 mSpeechQueue.add(new SpeechItem(callingApp, duration)); 415 if (!mIsSpeaking) { 416 processSpeechQueue(); 417 } 418 return TextToSpeech.TTS_SUCCESS; 419 } 420 421 private void silence(final long duration) { 422 class SilenceThread implements Runnable { 423 public void run() { 424 try { 425 Thread.sleep(duration); 426 } catch (InterruptedException e) { 427 e.printStackTrace(); 428 } finally { 429 processSpeechQueue(); 430 } 431 } 432 } 433 Thread slnc = (new Thread(new SilenceThread())); 434 slnc.setPriority(Thread.MIN_PRIORITY); 435 slnc.start(); 436 } 437 438 private void speakInternalOnly(final String text, 439 final ArrayList<String> params) { 440 class SynthThread implements Runnable { 441 public void run() { 442 boolean synthAvailable = false; 443 try { 444 synthAvailable = synthesizerLock.tryLock(); 445 if (!synthAvailable) { 446 Thread.sleep(100); 447 Thread synth = (new Thread(new SynthThread())); 448 synth.setPriority(Thread.MIN_PRIORITY); 449 synth.start(); 450 return; 451 } 452 if (params != null){ 453 String language = ""; 454 String country = ""; 455 String variant = ""; 456 for (int i = 0; i < params.size() - 1; i = i + 2){ 457 String param = params.get(i); 458 if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){ 459 setSpeechRate("", Integer.parseInt(params.get(i+1))); 460 } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){ 461 language = params.get(i+1); 462 } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){ 463 country = params.get(i+1); 464 } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){ 465 variant = params.get(i+1); 466 } 467 } 468 if (language.length() > 0){ 469 setLanguage("", language, country, variant); 470 } 471 } 472 nativeSynth.speak(text); 473 } catch (InterruptedException e) { 474 Log.e("TTS speakInternalOnly", "tryLock interrupted"); 475 e.printStackTrace(); 476 } finally { 477 // This check is needed because finally will always run; 478 // even if the 479 // method returns somewhere in the try block. 480 if (synthAvailable) { 481 synthesizerLock.unlock(); 482 } 483 processSpeechQueue(); 484 } 485 } 486 } 487 Thread synth = (new Thread(new SynthThread())); 488 synth.setPriority(Thread.MIN_PRIORITY); 489 synth.start(); 490 } 491 492 private void synthToFileInternalOnly(final String text, 493 final ArrayList<String> params, final String filename) { 494 class SynthThread implements Runnable { 495 public void run() { 496 Log.i("TTS", "Synthesizing to " + filename); 497 boolean synthAvailable = false; 498 try { 499 synthAvailable = synthesizerLock.tryLock(); 500 if (!synthAvailable) { 501 Thread.sleep(100); 502 Thread synth = (new Thread(new SynthThread())); 503 synth.setPriority(Thread.MIN_PRIORITY); 504 synth.start(); 505 return; 506 } 507 if (params != null){ 508 String language = ""; 509 String country = ""; 510 String variant = ""; 511 for (int i = 0; i < params.size() - 1; i = i + 2){ 512 String param = params.get(i); 513 if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){ 514 setSpeechRate("", Integer.parseInt(params.get(i+1))); 515 } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){ 516 language = params.get(i+1); 517 } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){ 518 country = params.get(i+1); 519 } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){ 520 variant = params.get(i+1); 521 } 522 } 523 if (language.length() > 0){ 524 setLanguage("", language, country, variant); 525 } 526 } 527 nativeSynth.synthesizeToFile(text, filename); 528 } catch (InterruptedException e) { 529 Log.e("TTS synthToFileInternalOnly", "tryLock interrupted"); 530 e.printStackTrace(); 531 } finally { 532 // This check is needed because finally will always run; 533 // even if the 534 // method returns somewhere in the try block. 535 if (synthAvailable) { 536 synthesizerLock.unlock(); 537 } 538 processSpeechQueue(); 539 } 540 } 541 } 542 Thread synth = (new Thread(new SynthThread())); 543 synth.setPriority(Thread.MIN_PRIORITY); 544 synth.start(); 545 } 546 547 private SoundResource getSoundResource(SpeechItem speechItem) { 548 SoundResource sr = null; 549 String text = speechItem.mText; 550 if (speechItem.mType == SpeechItem.SILENCE) { 551 // Do nothing if this is just silence 552 } else if (speechItem.mType == SpeechItem.EARCON) { 553 sr = mEarcons.get(text); 554 } else { 555 sr = mUtterances.get(text); 556 } 557 return sr; 558 } 559 560 private void broadcastTtsQueueProcessingCompleted(){ 561 Intent i = new Intent(Intent.ACTION_TTS_QUEUE_PROCESSING_COMPLETED); 562 sendBroadcast(i); 563 } 564 565 private void dispatchSpeechCompletedCallbacks(String mark) { 566 Log.i("TTS callback", "dispatch started"); 567 // Broadcast to all clients the new value. 568 final int N = mCallbacks.beginBroadcast(); 569 for (int i = 0; i < N; i++) { 570 try { 571 mCallbacks.getBroadcastItem(i).markReached(mark); 572 } catch (RemoteException e) { 573 // The RemoteCallbackList will take care of removing 574 // the dead object for us. 575 } 576 } 577 mCallbacks.finishBroadcast(); 578 Log.i("TTS callback", "dispatch completed to " + N); 579 } 580 581 private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){ 582 if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){ 583 return currentSpeechItem; 584 } else { 585 String callingApp = currentSpeechItem.callingApp; 586 ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>(); 587 int start = 0; 588 int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1; 589 String splitText; 590 SpeechItem splitItem; 591 while (end < currentSpeechItem.mText.length()){ 592 splitText = currentSpeechItem.mText.substring(start, end); 593 splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT); 594 splitItems.add(splitItem); 595 start = end; 596 end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1; 597 } 598 splitText = currentSpeechItem.mText.substring(start); 599 splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT); 600 splitItems.add(splitItem); 601 mSpeechQueue.remove(0); 602 for (int i = splitItems.size() - 1; i >= 0; i--){ 603 mSpeechQueue.add(0, splitItems.get(i)); 604 } 605 return mSpeechQueue.get(0); 606 } 607 } 608 609 private void processSpeechQueue() { 610 boolean speechQueueAvailable = false; 611 try { 612 speechQueueAvailable = speechQueueLock.tryLock(); 613 if (!speechQueueAvailable) { 614 return; 615 } 616 if (mSpeechQueue.size() < 1) { 617 mIsSpeaking = false; 618 broadcastTtsQueueProcessingCompleted(); 619 return; 620 } 621 622 SpeechItem currentSpeechItem = mSpeechQueue.get(0); 623 mIsSpeaking = true; 624 SoundResource sr = getSoundResource(currentSpeechItem); 625 // Synth speech as needed - synthesizer should call 626 // processSpeechQueue to continue running the queue 627 Log.i("TTS processing: ", currentSpeechItem.mText); 628 if (sr == null) { 629 if (currentSpeechItem.mType == SpeechItem.TEXT) { 630 currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem); 631 speakInternalOnly(currentSpeechItem.mText, 632 currentSpeechItem.mParams); 633 } else if (currentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) { 634 synthToFileInternalOnly(currentSpeechItem.mText, 635 currentSpeechItem.mParams, currentSpeechItem.mFilename); 636 } else { 637 // This is either silence or an earcon that was missing 638 silence(currentSpeechItem.mDuration); 639 } 640 } else { 641 cleanUpPlayer(); 642 if (sr.mSourcePackageName == PKGNAME) { 643 // Utterance is part of the TTS library 644 mPlayer = MediaPlayer.create(this, sr.mResId); 645 } else if (sr.mSourcePackageName != null) { 646 // Utterance is part of the app calling the library 647 Context ctx; 648 try { 649 ctx = this.createPackageContext(sr.mSourcePackageName, 650 0); 651 } catch (NameNotFoundException e) { 652 e.printStackTrace(); 653 mSpeechQueue.remove(0); // Remove it from the queue and 654 // move on 655 mIsSpeaking = false; 656 return; 657 } 658 mPlayer = MediaPlayer.create(ctx, sr.mResId); 659 } else { 660 // Utterance is coming from a file 661 mPlayer = MediaPlayer.create(this, Uri.parse(sr.mFilename)); 662 } 663 664 // Check if Media Server is dead; if it is, clear the queue and 665 // give up for now - hopefully, it will recover itself. 666 if (mPlayer == null) { 667 mSpeechQueue.clear(); 668 mIsSpeaking = false; 669 return; 670 } 671 mPlayer.setOnCompletionListener(this); 672 try { 673 mPlayer.start(); 674 } catch (IllegalStateException e) { 675 mSpeechQueue.clear(); 676 mIsSpeaking = false; 677 cleanUpPlayer(); 678 return; 679 } 680 } 681 if (mSpeechQueue.size() > 0) { 682 mSpeechQueue.remove(0); 683 } 684 } finally { 685 // This check is needed because finally will always run; even if the 686 // method returns somewhere in the try block. 687 if (speechQueueAvailable) { 688 speechQueueLock.unlock(); 689 } 690 } 691 } 692 693 private void cleanUpPlayer() { 694 if (mPlayer != null) { 695 mPlayer.release(); 696 mPlayer = null; 697 } 698 } 699 700 /** 701 * Synthesizes the given text to a file using the specified parameters. 702 * 703 * @param text 704 * The String of text that should be synthesized 705 * @param params 706 * An ArrayList of parameters. The first element of this array 707 * controls the type of voice to use. 708 * @param filename 709 * The string that gives the full output filename; it should be 710 * something like "/sdcard/myappsounds/mysound.wav". 711 * @return A boolean that indicates if the synthesis succeeded 712 */ 713 private boolean synthesizeToFile(String callingApp, String text, ArrayList<String> params, 714 String filename) { 715 // Don't allow a filename that is too long 716 if (filename.length() > MAX_FILENAME_LENGTH) { 717 return false; 718 } 719 // Don't allow anything longer than the max text length; since this 720 // is synthing to a file, don't even bother splitting it. 721 if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){ 722 return false; 723 } 724 mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT_TO_FILE, filename)); 725 if (!mIsSpeaking) { 726 processSpeechQueue(); 727 } 728 return true; 729 } 730 731 @Override 732 public IBinder onBind(Intent intent) { 733 if (ACTION.equals(intent.getAction())) { 734 for (String category : intent.getCategories()) { 735 if (category.equals(CATEGORY)) { 736 return mBinder; 737 } 738 } 739 } 740 return null; 741 } 742 743 private final android.speech.tts.ITts.Stub mBinder = new Stub() { 744 745 public void registerCallback(ITtsCallback cb) { 746 if (cb != null) 747 mCallbacks.register(cb); 748 } 749 750 public void unregisterCallback(ITtsCallback cb) { 751 if (cb != null) 752 mCallbacks.unregister(cb); 753 } 754 755 /** 756 * Speaks the given text using the specified queueing mode and 757 * parameters. 758 * 759 * @param text 760 * The text that should be spoken 761 * @param queueMode 762 * 0 for no queue (interrupts all previous utterances), 1 for 763 * queued 764 * @param params 765 * An ArrayList of parameters. The first element of this 766 * array controls the type of voice to use. 767 */ 768 public int speak(String callingApp, String text, int queueMode, String[] params) { 769 ArrayList<String> speakingParams = new ArrayList<String>(); 770 if (params != null) { 771 speakingParams = new ArrayList<String>(Arrays.asList(params)); 772 } 773 return mSelf.speak(callingApp, text, queueMode, speakingParams); 774 } 775 776 /** 777 * Plays the earcon using the specified queueing mode and parameters. 778 * 779 * @param earcon 780 * The earcon that should be played 781 * @param queueMode 782 * 0 for no queue (interrupts all previous utterances), 1 for 783 * queued 784 * @param params 785 * An ArrayList of parameters. 786 */ 787 public int playEarcon(String callingApp, String earcon, int queueMode, String[] params) { 788 ArrayList<String> speakingParams = new ArrayList<String>(); 789 if (params != null) { 790 speakingParams = new ArrayList<String>(Arrays.asList(params)); 791 } 792 return mSelf.playEarcon(callingApp, earcon, queueMode, speakingParams); 793 } 794 795 /** 796 * Plays the silence using the specified queueing mode and parameters. 797 * 798 * @param duration 799 * The duration of the silence that should be played 800 * @param queueMode 801 * 0 for no queue (interrupts all previous utterances), 1 for 802 * queued 803 * @param params 804 * An ArrayList of parameters. 805 */ 806 public int playSilence(String callingApp, long duration, int queueMode, String[] params) { 807 ArrayList<String> speakingParams = new ArrayList<String>(); 808 if (params != null) { 809 speakingParams = new ArrayList<String>(Arrays.asList(params)); 810 } 811 return mSelf.playSilence(callingApp, duration, queueMode, speakingParams); 812 } 813 814 /** 815 * Stops all speech output and removes any utterances still in the 816 * queue. 817 */ 818 public int stop(String callingApp) { 819 return mSelf.stop(callingApp); 820 } 821 822 /** 823 * Returns whether or not the TTS is speaking. 824 * 825 * @return Boolean to indicate whether or not the TTS is speaking 826 */ 827 public boolean isSpeaking() { 828 return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1)); 829 } 830 831 /** 832 * Adds a sound resource to the TTS. 833 * 834 * @param text 835 * The text that should be associated with the sound resource 836 * @param packageName 837 * The name of the package which has the sound resource 838 * @param resId 839 * The resource ID of the sound within its package 840 */ 841 public void addSpeech(String callingApp, String text, String packageName, int resId) { 842 mSelf.addSpeech(callingApp, text, packageName, resId); 843 } 844 845 /** 846 * Adds a sound resource to the TTS. 847 * 848 * @param text 849 * The text that should be associated with the sound resource 850 * @param filename 851 * The filename of the sound resource. This must be a 852 * complete path like: (/sdcard/mysounds/mysoundbite.mp3). 853 */ 854 public void addSpeechFile(String callingApp, String text, String filename) { 855 mSelf.addSpeech(callingApp, text, filename); 856 } 857 858 /** 859 * Adds a sound resource to the TTS as an earcon. 860 * 861 * @param earcon 862 * The text that should be associated with the sound resource 863 * @param packageName 864 * The name of the package which has the sound resource 865 * @param resId 866 * The resource ID of the sound within its package 867 */ 868 public void addEarcon(String callingApp, String earcon, String packageName, int resId) { 869 mSelf.addEarcon(callingApp, earcon, packageName, resId); 870 } 871 872 /** 873 * Adds a sound resource to the TTS as an earcon. 874 * 875 * @param earcon 876 * The text that should be associated with the sound resource 877 * @param filename 878 * The filename of the sound resource. This must be a 879 * complete path like: (/sdcard/mysounds/mysoundbite.mp3). 880 */ 881 public void addEarconFile(String callingApp, String earcon, String filename) { 882 mSelf.addEarcon(callingApp, earcon, filename); 883 } 884 885 /** 886 * Sets the speech rate for the TTS. Note that this will only have an 887 * effect on synthesized speech; it will not affect pre-recorded speech. 888 * 889 * @param speechRate 890 * The speech rate that should be used 891 */ 892 public int setSpeechRate(String callingApp, int speechRate) { 893 return mSelf.setSpeechRate(callingApp, speechRate); 894 } 895 896 /** 897 * Sets the pitch for the TTS. Note that this will only have an 898 * effect on synthesized speech; it will not affect pre-recorded speech. 899 * 900 * @param pitch 901 * The pitch that should be used for the synthesized voice 902 */ 903 public int setPitch(String callingApp, int pitch) { 904 return mSelf.setPitch(callingApp, pitch); 905 } 906 907 /** 908 * Returns the level of support for the specified language. 909 * 910 * @param lang the three letter ISO language code. 911 * @param country the three letter ISO country code. 912 * @param variant the variant code associated with the country and language pair. 913 * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE, 914 * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in 915 * android.speech.tts.TextToSpeech. 916 */ 917 public int isLanguageAvailable(String lang, String country, String variant) { 918 return mSelf.isLanguageAvailable(lang, country, variant); 919 } 920 921 /** 922 * Returns the currently set language / country / variant strings representing the 923 * language used by the TTS engine. 924 * @return null is no language is set, or an array of 3 string containing respectively 925 * the language, country and variant. 926 */ 927 public String[] getLanguage() { 928 return mSelf.getLanguage(); 929 } 930 931 /** 932 * Sets the speech rate for the TTS, which affects the synthesized voice. 933 * 934 * @param lang the three letter ISO language code. 935 * @param country the three letter ISO country code. 936 * @param variant the variant code associated with the country and language pair. 937 */ 938 public int setLanguage(String callingApp, String lang, String country, String variant) { 939 return mSelf.setLanguage(callingApp, lang, country, variant); 940 } 941 942 /** 943 * Synthesizes the given text to a file using the specified 944 * parameters. 945 * 946 * @param text 947 * The String of text that should be synthesized 948 * @param params 949 * An ArrayList of parameters. The first element of this 950 * array controls the type of voice to use. 951 * @param filename 952 * The string that gives the full output filename; it should 953 * be something like "/sdcard/myappsounds/mysound.wav". 954 * @return A boolean that indicates if the synthesis succeeded 955 */ 956 public boolean synthesizeToFile(String callingApp, String text, String[] params, 957 String filename) { 958 ArrayList<String> speakingParams = new ArrayList<String>(); 959 if (params != null) { 960 speakingParams = new ArrayList<String>(Arrays.asList(params)); 961 } 962 return mSelf.synthesizeToFile(callingApp, text, speakingParams, filename); 963 } 964 965 }; 966 967} 968