TextToSpeech.java revision a9b417e9bfea3da908884c726ffc9bf4f64691cf
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         * Parameter key to specify the audio stream type to be used when speaking text
290         * or playing back a file.
291         * @see TextToSpeech#speak(String, int, HashMap)
292         * @see TextToSpeech#playEarcon(String, int, HashMap)
293         */
294        public static final String KEY_PARAM_STREAM = "streamType";
295        /**
296         * Parameter key to identify an utterance in the
297         * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
298         * spoken, a file has been played back or a silence duration has elapsed.
299         * @see TextToSpeech#speak(String, int, HashMap)
300         * @see TextToSpeech#playEarcon(String, int, HashMap)
301         * @see TextToSpeech#synthesizeToFile(String, HashMap, String)
302         */
303        public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId";
304
305        // key positions in the array of cached parameters
306        /**
307         * {@hide}
308         */
309        protected static final int PARAM_POSITION_RATE = 0;
310        /**
311         * {@hide}
312         */
313        protected static final int PARAM_POSITION_LANGUAGE = 2;
314        /**
315         * {@hide}
316         */
317        protected static final int PARAM_POSITION_COUNTRY = 4;
318        /**
319         * {@hide}
320         */
321        protected static final int PARAM_POSITION_VARIANT = 6;
322        /**
323         * {@hide}
324         */
325        protected static final int PARAM_POSITION_STREAM = 8;
326        /**
327         * {@hide}
328         */
329        protected static final int PARAM_POSITION_UTTERANCE_ID = 10;
330        /**
331         * {@hide}
332         */
333        protected static final int NB_CACHED_PARAMS = 6;
334    }
335
336    /**
337     * Connection needed for the TTS.
338     */
339    private ServiceConnection mServiceConnection;
340
341    private ITts mITts = null;
342    private ITtsCallback mITtscallback = null;
343    private Context mContext = null;
344    private String mPackageName = "";
345    private OnInitListener mInitListener = null;
346    private boolean mStarted = false;
347    private final Object mStartLock = new Object();
348    /**
349     * Used to store the cached parameters sent along with each synthesis request to the
350     * TTS service.
351     */
352    private String[] mCachedParams;
353
354    /**
355     * The constructor for the TextToSpeech class.
356     * This will also initialize the associated TextToSpeech engine if it isn't already running.
357     *
358     * @param context
359     *            The context this instance is running in.
360     * @param listener
361     *            The {@link TextToSpeech.OnInitListener} that will be called when the
362     *            TextToSpeech engine has initialized.
363     */
364    public TextToSpeech(Context context, OnInitListener listener) {
365        mContext = context;
366        mPackageName = mContext.getPackageName();
367        mInitListener = listener;
368
369        mCachedParams = new String[2*Engine.NB_CACHED_PARAMS]; // store key and value
370        mCachedParams[Engine.PARAM_POSITION_RATE] = Engine.KEY_PARAM_RATE;
371        mCachedParams[Engine.PARAM_POSITION_LANGUAGE] = Engine.KEY_PARAM_LANGUAGE;
372        mCachedParams[Engine.PARAM_POSITION_COUNTRY] = Engine.KEY_PARAM_COUNTRY;
373        mCachedParams[Engine.PARAM_POSITION_VARIANT] = Engine.KEY_PARAM_VARIANT;
374        mCachedParams[Engine.PARAM_POSITION_STREAM] = Engine.KEY_PARAM_STREAM;
375        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID] = Engine.KEY_PARAM_UTTERANCE_ID;
376
377        mCachedParams[Engine.PARAM_POSITION_RATE + 1] =
378                String.valueOf(Engine.DEFAULT_RATE);
379        // initialize the language cached parameters with the current Locale
380        Locale defaultLoc = Locale.getDefault();
381        mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = defaultLoc.getISO3Language();
382        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = defaultLoc.getISO3Country();
383        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = defaultLoc.getVariant();
384
385        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
386                String.valueOf(Engine.DEFAULT_STREAM);
387        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = "";
388
389        initTts();
390    }
391
392
393    private void initTts() {
394        mStarted = false;
395
396        // Initialize the TTS, run the callback after the binding is successful
397        mServiceConnection = new ServiceConnection() {
398            public void onServiceConnected(ComponentName name, IBinder service) {
399                synchronized(mStartLock) {
400                    mITts = ITts.Stub.asInterface(service);
401                    mStarted = true;
402                    if (mInitListener != null) {
403                        // TODO manage failures and missing resources
404                        mInitListener.onInit(SUCCESS);
405                    }
406                }
407            }
408
409            public void onServiceDisconnected(ComponentName name) {
410                synchronized(mStartLock) {
411                    mITts = null;
412                    mInitListener = null;
413                    mStarted = false;
414                }
415            }
416        };
417
418        Intent intent = new Intent("android.intent.action.START_TTS_SERVICE");
419        intent.addCategory("android.intent.category.TTS");
420        mContext.bindService(intent, mServiceConnection,
421                Context.BIND_AUTO_CREATE);
422        // TODO handle case where the binding works (should always work) but
423        //      the plugin fails
424    }
425
426
427    /**
428     * Releases the resources used by the TextToSpeech engine.
429     * It is good practice for instance to call this method in the onDestroy() method of an Activity
430     * so the TextToSpeech engine can be cleanly stopped.
431     */
432    public void shutdown() {
433        try {
434            mContext.unbindService(mServiceConnection);
435        } catch (IllegalArgumentException e) {
436            // Do nothing and fail silently since an error here indicates that
437            // binding never succeeded in the first place.
438        }
439    }
440
441
442    /**
443     * Adds a mapping between a string of text and a sound resource in a
444     * package. After a call to this method, subsequent calls to
445     * {@link #speak(String, int, HashMap)} will play the specified sound resource
446     * if it is available, or synthesize the text it is missing.
447     *
448     * @param text
449     *            The string of text. Example: <code>"south_south_east"</code>
450     *
451     * @param packagename
452     *            Pass the packagename of the application that contains the
453     *            resource. If the resource is in your own application (this is
454     *            the most common case), then put the packagename of your
455     *            application here.<br/>
456     *            Example: <b>"com.google.marvin.compass"</b><br/>
457     *            The packagename can be found in the AndroidManifest.xml of
458     *            your application.
459     *            <p>
460     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
461     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
462     *            </p>
463     *
464     * @param resourceId
465     *            Example: <code>R.raw.south_south_east</code>
466     *
467     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
468     */
469    public int addSpeech(String text, String packagename, int resourceId) {
470        synchronized(mStartLock) {
471            if (!mStarted) {
472                return ERROR;
473            }
474            try {
475                mITts.addSpeech(mPackageName, text, packagename, resourceId);
476                return SUCCESS;
477            } catch (RemoteException e) {
478                // TTS died; restart it.
479                Log.e("TextToSpeech.java - addSpeech", "RemoteException");
480                e.printStackTrace();
481                mStarted = false;
482                initTts();
483            } catch (NullPointerException e) {
484                // TTS died; restart it.
485                Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
486                e.printStackTrace();
487                mStarted = false;
488                initTts();
489            } catch (IllegalStateException e) {
490                // TTS died; restart it.
491                Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
492                e.printStackTrace();
493                mStarted = false;
494                initTts();
495            }
496            return ERROR;
497        }
498    }
499
500
501    /**
502     * Adds a mapping between a string of text and a sound file. Using this, it
503     * is possible to add custom pronounciations for a string of text.
504     * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
505     * will play the specified sound resource if it is available, or synthesize the text it is
506     * missing.
507     *
508     * @param text
509     *            The string of text. Example: <code>"south_south_east"</code>
510     * @param filename
511     *            The full path to the sound file (for example:
512     *            "/sdcard/mysounds/hello.wav")
513     *
514     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
515     */
516    public int addSpeech(String text, String filename) {
517        synchronized (mStartLock) {
518            if (!mStarted) {
519                return ERROR;
520            }
521            try {
522                mITts.addSpeechFile(mPackageName, text, filename);
523                return SUCCESS;
524            } catch (RemoteException e) {
525                // TTS died; restart it.
526                Log.e("TextToSpeech.java - addSpeech", "RemoteException");
527                e.printStackTrace();
528                mStarted = false;
529                initTts();
530            } catch (NullPointerException e) {
531                // TTS died; restart it.
532                Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
533                e.printStackTrace();
534                mStarted = false;
535                initTts();
536            } catch (IllegalStateException e) {
537                // TTS died; restart it.
538                Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
539                e.printStackTrace();
540                mStarted = false;
541                initTts();
542            }
543            return ERROR;
544        }
545    }
546
547
548    /**
549     * Adds a mapping between a string of text and a sound resource in a
550     * package. Use this to add custom earcons.
551     *
552     * @see #playEarcon(String, int, HashMap)
553     *
554     * @param earcon The name of the earcon.
555     *            Example: <code>"[tick]"</code><br/>
556     *
557     * @param packagename
558     *            the package name of the application that contains the
559     *            resource. This can for instance be the package name of your own application.
560     *            Example: <b>"com.google.marvin.compass"</b><br/>
561     *            The package name can be found in the AndroidManifest.xml of
562     *            the application containing the resource.
563     *            <p>
564     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
565     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
566     *            </p>
567     *
568     * @param resourceId
569     *            Example: <code>R.raw.tick_snd</code>
570     *
571     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
572     */
573    public int addEarcon(String earcon, String packagename, int resourceId) {
574        synchronized(mStartLock) {
575            if (!mStarted) {
576                return ERROR;
577            }
578            try {
579                mITts.addEarcon(mPackageName, earcon, packagename, resourceId);
580                return SUCCESS;
581            } catch (RemoteException e) {
582                // TTS died; restart it.
583                Log.e("TextToSpeech.java - addEarcon", "RemoteException");
584                e.printStackTrace();
585                mStarted = false;
586                initTts();
587            } catch (NullPointerException e) {
588                // TTS died; restart it.
589                Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
590                e.printStackTrace();
591                mStarted = false;
592                initTts();
593            } catch (IllegalStateException e) {
594                // TTS died; restart it.
595                Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
596                e.printStackTrace();
597                mStarted = false;
598                initTts();
599            }
600            return ERROR;
601        }
602    }
603
604
605    /**
606     * Adds a mapping between a string of text and a sound file.
607     * Use this to add custom earcons.
608     *
609     * @see #playEarcon(String, int, HashMap)
610     *
611     * @param earcon
612     *            The name of the earcon.
613     *            Example: <code>"[tick]"</code>
614     * @param filename
615     *            The full path to the sound file (for example:
616     *            "/sdcard/mysounds/tick.wav")
617     *
618     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
619     */
620    public int addEarcon(String earcon, String filename) {
621        synchronized (mStartLock) {
622            if (!mStarted) {
623                return ERROR;
624            }
625            try {
626                mITts.addEarconFile(mPackageName, earcon, filename);
627                return SUCCESS;
628            } catch (RemoteException e) {
629                // TTS died; restart it.
630                Log.e("TextToSpeech.java - addEarcon", "RemoteException");
631                e.printStackTrace();
632                mStarted = false;
633                initTts();
634            } catch (NullPointerException e) {
635                // TTS died; restart it.
636                Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
637                e.printStackTrace();
638                mStarted = false;
639                initTts();
640            } catch (IllegalStateException e) {
641                // TTS died; restart it.
642                Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
643                e.printStackTrace();
644                mStarted = false;
645                initTts();
646            }
647            return ERROR;
648        }
649    }
650
651
652    /**
653     * Speaks the string using the specified queuing strategy and speech
654     * parameters.
655     *
656     * @param text
657     *            The string of text to be spoken.
658     * @param queueMode
659     *            The queuing strategy to use.
660     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
661     * @param params
662     *            The list of parameters to be used. Can be null if no parameters are given.
663     *            They are specified using a (key, value) pair, where the key can be
664     *            {@link Engine#KEY_PARAM_STREAM} or
665     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
666     *
667     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
668     */
669    public int speak(String text, int queueMode, HashMap<String,String> params)
670    {
671        synchronized (mStartLock) {
672            int result = ERROR;
673            Log.i("TTS received: ", text);
674            if (!mStarted) {
675                return result;
676            }
677            try {
678                if ((params != null) && (!params.isEmpty())) {
679                    String extra = params.get(Engine.KEY_PARAM_STREAM);
680                    if (extra != null) {
681                        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] = extra;
682                    }
683                    extra = params.get(Engine.KEY_PARAM_UTTERANCE_ID);
684                    if (extra != null) {
685                        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra;
686                    }
687                }
688                result = mITts.speak(mPackageName, text, queueMode, mCachedParams);
689            } catch (RemoteException e) {
690                // TTS died; restart it.
691                Log.e("TextToSpeech.java - speak", "RemoteException");
692                e.printStackTrace();
693                mStarted = false;
694                initTts();
695            } catch (NullPointerException e) {
696                // TTS died; restart it.
697                Log.e("TextToSpeech.java - speak", "NullPointerException");
698                e.printStackTrace();
699                mStarted = false;
700                initTts();
701            } catch (IllegalStateException e) {
702                // TTS died; restart it.
703                Log.e("TextToSpeech.java - speak", "IllegalStateException");
704                e.printStackTrace();
705                mStarted = false;
706                initTts();
707            } finally {
708                resetCachedParams();
709                return result;
710            }
711        }
712    }
713
714
715    /**
716     * Plays the earcon using the specified queueing mode and parameters.
717     *
718     * @param earcon
719     *            The earcon that should be played
720     * @param queueMode
721     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
722     * @param params
723     *            The list of parameters to be used. Can be null if no parameters are given.
724     *            They are specified using a (key, value) pair, where the key can be
725     *            {@link Engine#KEY_PARAM_STREAM} or
726     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
727     *
728     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
729     */
730    public int playEarcon(String earcon, int queueMode,
731            HashMap<String,String> params) {
732        synchronized (mStartLock) {
733            int result = ERROR;
734            if (!mStarted) {
735                return result;
736            }
737            try {
738                if ((params != null) && (!params.isEmpty())) {
739                    String extra = params.get(Engine.KEY_PARAM_STREAM);
740                    if (extra != null) {
741                        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] = extra;
742                    }
743                    extra = params.get(Engine.KEY_PARAM_UTTERANCE_ID);
744                    if (extra != null) {
745                        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra;
746                    }
747                }
748                result = mITts.playEarcon(mPackageName, earcon, queueMode, null);
749            } catch (RemoteException e) {
750                // TTS died; restart it.
751                Log.e("TextToSpeech.java - playEarcon", "RemoteException");
752                e.printStackTrace();
753                mStarted = false;
754                initTts();
755            } catch (NullPointerException e) {
756                // TTS died; restart it.
757                Log.e("TextToSpeech.java - playEarcon", "NullPointerException");
758                e.printStackTrace();
759                mStarted = false;
760                initTts();
761            } catch (IllegalStateException e) {
762                // TTS died; restart it.
763                Log.e("TextToSpeech.java - playEarcon", "IllegalStateException");
764                e.printStackTrace();
765                mStarted = false;
766                initTts();
767            } finally {
768                resetCachedParams();
769                return result;
770            }
771        }
772    }
773
774    /**
775     * Plays silence for the specified amount of time using the specified
776     * queue mode.
777     *
778     * @param durationInMs
779     *            A long that indicates how long the silence should last.
780     * @param queueMode
781     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
782     * @param params
783     *            The list of parameters to be used. Can be null if no parameters are given.
784     *            They are specified using a (key, value) pair, where the key can be
785     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
786     *
787     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
788     */
789    public int playSilence(long durationInMs, int queueMode, HashMap<String,String> params) {
790        synchronized (mStartLock) {
791            int result = ERROR;
792            if (!mStarted) {
793                return result;
794            }
795            try {
796                if ((params != null) && (!params.isEmpty())) {
797                    String extra = params.get(Engine.KEY_PARAM_UTTERANCE_ID);
798                    if (extra != null) {
799                        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra;
800                    }
801                }
802                result = mITts.playSilence(mPackageName, durationInMs, queueMode, mCachedParams);
803            } catch (RemoteException e) {
804                // TTS died; restart it.
805                Log.e("TextToSpeech.java - playSilence", "RemoteException");
806                e.printStackTrace();
807                mStarted = false;
808                initTts();
809            } catch (NullPointerException e) {
810                // TTS died; restart it.
811                Log.e("TextToSpeech.java - playSilence", "NullPointerException");
812                e.printStackTrace();
813                mStarted = false;
814                initTts();
815            } catch (IllegalStateException e) {
816                // TTS died; restart it.
817                Log.e("TextToSpeech.java - playSilence", "IllegalStateException");
818                e.printStackTrace();
819                mStarted = false;
820                initTts();
821            } finally {
822              return result;
823            }
824        }
825    }
826
827
828    /**
829     * Returns whether or not the TextToSpeech engine is busy speaking.
830     *
831     * @return Whether or not the TextToSpeech engine is busy speaking.
832     */
833    public boolean isSpeaking() {
834        synchronized (mStartLock) {
835            if (!mStarted) {
836                return false;
837            }
838            try {
839                return mITts.isSpeaking();
840            } catch (RemoteException e) {
841                // TTS died; restart it.
842                Log.e("TextToSpeech.java - isSpeaking", "RemoteException");
843                e.printStackTrace();
844                mStarted = false;
845                initTts();
846            } catch (NullPointerException e) {
847                // TTS died; restart it.
848                Log.e("TextToSpeech.java - isSpeaking", "NullPointerException");
849                e.printStackTrace();
850                mStarted = false;
851                initTts();
852            } catch (IllegalStateException e) {
853                // TTS died; restart it.
854                Log.e("TextToSpeech.java - isSpeaking", "IllegalStateException");
855                e.printStackTrace();
856                mStarted = false;
857                initTts();
858            }
859            return false;
860        }
861    }
862
863
864    /**
865     * Interrupts the current utterance (whether played or rendered to file) and discards other
866     * utterances in the queue.
867     *
868     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
869     */
870    public int stop() {
871        synchronized (mStartLock) {
872            int result = ERROR;
873            if (!mStarted) {
874                return result;
875            }
876            try {
877                result = mITts.stop(mPackageName);
878            } catch (RemoteException e) {
879                // TTS died; restart it.
880                Log.e("TextToSpeech.java - stop", "RemoteException");
881                e.printStackTrace();
882                mStarted = false;
883                initTts();
884            } catch (NullPointerException e) {
885                // TTS died; restart it.
886                Log.e("TextToSpeech.java - stop", "NullPointerException");
887                e.printStackTrace();
888                mStarted = false;
889                initTts();
890            } catch (IllegalStateException e) {
891                // TTS died; restart it.
892                Log.e("TextToSpeech.java - stop", "IllegalStateException");
893                e.printStackTrace();
894                mStarted = false;
895                initTts();
896            } finally {
897              return result;
898            }
899        }
900    }
901
902
903    /**
904     * Sets the speech rate for the TextToSpeech engine.
905     *
906     * This has no effect on any pre-recorded speech.
907     *
908     * @param speechRate
909     *            The speech rate for the TextToSpeech engine. 1 is the normal speed,
910     *            lower values slow down the speech (0.5 is half the normal speech rate),
911     *            greater values accelerate it (2 is twice the normal speech rate).
912     *
913     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
914     */
915    public int setSpeechRate(float speechRate) {
916        synchronized (mStartLock) {
917            int result = ERROR;
918            if (!mStarted) {
919                return result;
920            }
921            try {
922                if (speechRate > 0) {
923                    int rate = (int)(speechRate*100);
924                    mCachedParams[Engine.PARAM_POSITION_RATE + 1] = String.valueOf(rate);
925                    // the rate is not set here, instead it is cached so it will be associated
926                    // with all upcoming utterances.
927                    if (speechRate > 0.0f) {
928                        result = SUCCESS;
929                    } else {
930                        result = ERROR;
931                    }
932                }
933            } catch (NullPointerException e) {
934                // TTS died; restart it.
935                Log.e("TextToSpeech.java - setSpeechRate", "NullPointerException");
936                e.printStackTrace();
937                mStarted = false;
938                initTts();
939            } catch (IllegalStateException e) {
940                // TTS died; restart it.
941                Log.e("TextToSpeech.java - setSpeechRate", "IllegalStateException");
942                e.printStackTrace();
943                mStarted = false;
944                initTts();
945            } finally {
946              return result;
947            }
948        }
949    }
950
951
952    /**
953     * Sets the speech pitch for the TextToSpeech engine.
954     *
955     * This has no effect on any pre-recorded speech.
956     *
957     * @param pitch
958     *            The pitch for the TextToSpeech engine. 1 is the normal pitch,
959     *            lower values lower the tone of the synthesized voice,
960     *            greater values increase it.
961     *
962     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
963     */
964    public int setPitch(float pitch) {
965        synchronized (mStartLock) {
966            int result = ERROR;
967            if (!mStarted) {
968                return result;
969            }
970            try {
971                if (pitch > 0) {
972                    result = mITts.setPitch(mPackageName, (int)(pitch*100));
973                }
974            } catch (RemoteException e) {
975                // TTS died; restart it.
976                Log.e("TextToSpeech.java - setPitch", "RemoteException");
977                e.printStackTrace();
978                mStarted = false;
979                initTts();
980            } catch (NullPointerException e) {
981                // TTS died; restart it.
982                Log.e("TextToSpeech.java - setPitch", "NullPointerException");
983                e.printStackTrace();
984                mStarted = false;
985                initTts();
986            } catch (IllegalStateException e) {
987                // TTS died; restart it.
988                Log.e("TextToSpeech.java - setPitch", "IllegalStateException");
989                e.printStackTrace();
990                mStarted = false;
991                initTts();
992            } finally {
993              return result;
994            }
995        }
996    }
997
998
999    /**
1000     * Sets the language for the TextToSpeech engine.
1001     * The TextToSpeech engine will try to use the closest match to the specified
1002     * language as represented by the Locale, but there is no guarantee that the exact same Locale
1003     * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support
1004     * before choosing the language to use for the next utterances.
1005     *
1006     * @param loc
1007     *            The locale describing the language to be used.
1008     *
1009     * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1010     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1011     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1012     */
1013    public int setLanguage(Locale loc) {
1014        synchronized (mStartLock) {
1015            int result = LANG_NOT_SUPPORTED;
1016            if (!mStarted) {
1017                return result;
1018            }
1019            try {
1020                mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = loc.getISO3Language();
1021                mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = loc.getISO3Country();
1022                mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = loc.getVariant();
1023                // the language is not set here, instead it is cached so it will be associated
1024                // with all upcoming utterances. But we still need to report the language support,
1025                // which is achieved by calling isLanguageAvailable()
1026                result = mITts.isLanguageAvailable(
1027                        mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1],
1028                        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1],
1029                        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] );
1030            } catch (RemoteException e) {
1031                // TTS died; restart it.
1032                Log.e("TextToSpeech.java - setLanguage", "RemoteException");
1033                e.printStackTrace();
1034                mStarted = false;
1035                initTts();
1036            } catch (NullPointerException e) {
1037                // TTS died; restart it.
1038                Log.e("TextToSpeech.java - setLanguage", "NullPointerException");
1039                e.printStackTrace();
1040                mStarted = false;
1041                initTts();
1042            } catch (IllegalStateException e) {
1043                // TTS died; restart it.
1044                Log.e("TextToSpeech.java - setLanguage", "IllegalStateException");
1045                e.printStackTrace();
1046                mStarted = false;
1047                initTts();
1048            } finally {
1049              return result;
1050            }
1051        }
1052    }
1053
1054
1055    /**
1056     * Returns a Locale instance describing the language currently being used by the TextToSpeech
1057     * engine.
1058     * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
1059     *     instance, or null is the TextToSpeech engine has failed.
1060     */
1061    public Locale getLanguage() {
1062        synchronized (mStartLock) {
1063            if (!mStarted) {
1064                return null;
1065            }
1066            try {
1067                String[] locStrings =  mITts.getLanguage();
1068                if ((locStrings != null) && (locStrings.length == 3)) {
1069                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
1070                } else {
1071                    return null;
1072                }
1073            } catch (RemoteException e) {
1074                // TTS died; restart it.
1075                Log.e("TextToSpeech.java - getLanguage", "RemoteException");
1076                e.printStackTrace();
1077                mStarted = false;
1078                initTts();
1079            } catch (NullPointerException e) {
1080                // TTS died; restart it.
1081                Log.e("TextToSpeech.java - getLanguage", "NullPointerException");
1082                e.printStackTrace();
1083                mStarted = false;
1084                initTts();
1085            } catch (IllegalStateException e) {
1086                // TTS died; restart it.
1087                Log.e("TextToSpeech.java - getLanguage", "IllegalStateException");
1088                e.printStackTrace();
1089                mStarted = false;
1090                initTts();
1091            }
1092            return null;
1093        }
1094    }
1095
1096    /**
1097     * Checks if the specified language as represented by the Locale is available and supported.
1098     *
1099     * @param loc
1100     *            The Locale describing the language to be used.
1101     *
1102     * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1103     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1104     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1105     */
1106    public int isLanguageAvailable(Locale loc) {
1107        synchronized (mStartLock) {
1108            int result = LANG_NOT_SUPPORTED;
1109            if (!mStarted) {
1110                return result;
1111            }
1112            try {
1113                result = mITts.isLanguageAvailable(loc.getISO3Language(),
1114                        loc.getISO3Country(), loc.getVariant());
1115            } catch (RemoteException e) {
1116                // TTS died; restart it.
1117                Log.e("TextToSpeech.java - isLanguageAvailable", "RemoteException");
1118                e.printStackTrace();
1119                mStarted = false;
1120                initTts();
1121            } catch (NullPointerException e) {
1122                // TTS died; restart it.
1123                Log.e("TextToSpeech.java - isLanguageAvailable", "NullPointerException");
1124                e.printStackTrace();
1125                mStarted = false;
1126                initTts();
1127            } catch (IllegalStateException e) {
1128                // TTS died; restart it.
1129                Log.e("TextToSpeech.java - isLanguageAvailable", "IllegalStateException");
1130                e.printStackTrace();
1131                mStarted = false;
1132                initTts();
1133            } finally {
1134              return result;
1135            }
1136        }
1137    }
1138
1139
1140    /**
1141     * Synthesizes the given text to a file using the specified parameters.
1142     *
1143     * @param text
1144     *            The String of text that should be synthesized
1145     * @param params
1146     *            The list of parameters to be used. Can be null if no parameters are given.
1147     *            They are specified using a (key, value) pair, where the key can be
1148     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
1149     * @param filename
1150     *            The string that gives the full output filename; it should be
1151     *            something like "/sdcard/myappsounds/mysound.wav".
1152     *
1153     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
1154     */
1155    public int synthesizeToFile(String text, HashMap<String,String> params,
1156            String filename) {
1157        synchronized (mStartLock) {
1158            int result = ERROR;
1159            if (!mStarted) {
1160                return result;
1161            }
1162            try {
1163                if ((params != null) && (!params.isEmpty())) {
1164                    // no need to read the stream type here
1165                    String extra = params.get(Engine.KEY_PARAM_UTTERANCE_ID);
1166                    if (extra != null) {
1167                        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra;
1168                    }
1169                }
1170                if (mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename)){
1171                    result = SUCCESS;
1172                }
1173            } catch (RemoteException e) {
1174                // TTS died; restart it.
1175                Log.e("TextToSpeech.java - synthesizeToFile", "RemoteException");
1176                e.printStackTrace();
1177                mStarted = false;
1178                initTts();
1179            } catch (NullPointerException e) {
1180                // TTS died; restart it.
1181                Log.e("TextToSpeech.java - synthesizeToFile", "NullPointerException");
1182                e.printStackTrace();
1183                mStarted = false;
1184                initTts();
1185            } catch (IllegalStateException e) {
1186                // TTS died; restart it.
1187                Log.e("TextToSpeech.java - synthesizeToFile", "IllegalStateException");
1188                e.printStackTrace();
1189                mStarted = false;
1190                initTts();
1191            } finally {
1192                resetCachedParams();
1193                return result;
1194            }
1195        }
1196    }
1197
1198
1199    /**
1200     * Convenience method to reset the cached parameters to the current default values
1201     * if they are not persistent between calls to the service.
1202     */
1203    private void resetCachedParams() {
1204        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
1205                String.valueOf(Engine.DEFAULT_STREAM);
1206        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID+ 1] = "";
1207    }
1208
1209    /**
1210     * Sets the OnUtteranceCompletedListener that will fire when an utterance completes.
1211     *
1212     * @param listener
1213     *            The OnUtteranceCompletedListener
1214     *
1215     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
1216     */
1217    public int setOnUtteranceCompletedListener(
1218            final OnUtteranceCompletedListener listener) {
1219        synchronized (mStartLock) {
1220            int result = ERROR;
1221            if (!mStarted) {
1222                return result;
1223            }
1224            mITtscallback = new ITtsCallback.Stub() {
1225                public void utteranceCompleted(String utteranceId) throws RemoteException {
1226                    if (listener != null) {
1227                        listener.onUtteranceCompleted(utteranceId);
1228                    }
1229                }
1230            };
1231            try {
1232                result = mITts.registerCallback(mPackageName, mITtscallback);
1233            } catch (RemoteException e) {
1234                // TTS died; restart it.
1235                Log.e("TextToSpeech.java - registerCallback", "RemoteException");
1236                e.printStackTrace();
1237                mStarted = false;
1238                initTts();
1239            } catch (NullPointerException e) {
1240                // TTS died; restart it.
1241                Log.e("TextToSpeech.java - registerCallback", "NullPointerException");
1242                e.printStackTrace();
1243                mStarted = false;
1244                initTts();
1245            } catch (IllegalStateException e) {
1246                // TTS died; restart it.
1247                Log.e("TextToSpeech.java - registerCallback", "IllegalStateException");
1248                e.printStackTrace();
1249                mStarted = false;
1250                initTts();
1251            } finally {
1252                return result;
1253            }
1254        }
1255    }
1256
1257}
1258