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