TextToSpeech.java revision 1126aad736f3322559007b8abc49e26f550e4b54
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.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
25import android.media.AudioManager;
26import android.os.IBinder;
27import android.os.RemoteException;
28import android.util.Log;
29
30import java.util.HashMap;
31import java.util.Locale;
32
33/**
34 *
35 * Synthesizes speech from text for immediate playback or to create a sound file.
36 *
37 */
38//TODO complete javadoc + add links to constants
39public class TextToSpeech {
40
41    /**
42     * Denotes a successful operation.
43     */
44    public static final int TTS_SUCCESS                = 0;
45    /**
46     * Denotes a generic operation failure.
47     */
48    public static final int TTS_ERROR                  = -1;
49
50    /**
51     * Queue mode where all entries in the playback queue (media to be played
52     * and text to be synthesized) are dropped and replaced by the new entry.
53     */
54    public static final int TTS_QUEUE_FLUSH = 0;
55    /**
56     * Queue mode where the new entry is added at the end of the playback queue.
57     */
58    public static final int TTS_QUEUE_ADD = 1;
59
60
61    /**
62     * Denotes the language is available exactly as specified by the locale
63     */
64    public static final int TTS_LANG_COUNTRY_VAR_AVAILABLE = 2;
65
66
67    /**
68     * Denotes the language is available for the language and country specified
69     * by the locale, but not the variant.
70     */
71    public static final int TTS_LANG_COUNTRY_AVAILABLE = 1;
72
73
74    /**
75     * Denotes the language is available for the language by the locale,
76     * but not the country and variant.
77     */
78    public static final int TTS_LANG_AVAILABLE = 0;
79
80    /**
81     * Denotes the language data is missing.
82     */
83    public static final int TTS_LANG_MISSING_DATA = -1;
84
85    /**
86     * Denotes the language is not supported by the current TTS engine.
87     */
88    public static final int TTS_LANG_NOT_SUPPORTED = -2;
89
90
91    /**
92     * Called when the TTS has initialized.
93     *
94     * The InitListener must implement the onInit function. onInit is passed a
95     * status code indicating the result of the TTS initialization.
96     */
97    public interface OnInitListener {
98        public void onInit(int status);
99    }
100
101    /**
102     * Called when the TTS has completed saying something that has an utterance ID set.
103     *
104     * The OnUtteranceCompletedListener must implement the onUtteranceCompleted function.
105     * onUtteranceCompleted is passed a String that is the utteranceId given in
106     * the original speak call.
107     */
108    public interface OnUtteranceCompletedListener {
109        public void onUtteranceCompleted(String utteranceId);
110    }
111
112
113    /**
114     * Internal constants for the TTS functionality
115     *
116     */
117    public class Engine {
118        // default values for a TTS engine when settings are not found in the provider
119        /**
120         * {@hide}
121         */
122        public static final int FALLBACK_TTS_DEFAULT_RATE = 100; // 1x
123        /**
124         * {@hide}
125         */
126        public static final int FALLBACK_TTS_DEFAULT_PITCH = 100;// 1x
127        /**
128         * {@hide}
129         */
130        public static final int FALLBACK_TTS_USE_DEFAULTS = 0; // false
131        /**
132         * {@hide}
133         */
134        public static final String FALLBACK_TTS_DEFAULT_SYNTH = "com.svox.pico";
135
136        // default values for rendering
137        public static final int TTS_DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
138
139        // return codes for a TTS engine's check data activity
140        /**
141         * Indicates success when checking the installation status of the resources used by the
142         * text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
143         */
144        public static final int CHECK_VOICE_DATA_PASS = 1;
145        /**
146         * Indicates failure when checking the installation status of the resources used by the
147         * text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
148         */
149        public static final int CHECK_VOICE_DATA_FAIL = 0;
150        /**
151         * Indicates erroneous data when checking the installation status of the resources used by
152         * the text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
153         */
154        public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
155        /**
156         * Indicates missing resources when checking the installation status of the resources used
157         * by the text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
158         */
159        public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
160        /**
161         * Indicates missing storage volume when checking the installation status of the resources
162         * used by the text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
163         */
164        public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
165
166        // return codes for a TTS engine's check data activity
167        /**
168         * Extra information received with the android.intent.action.CHECK_TTS_DATA intent where
169         * the text-to-speech engine specifies the path to its resources.
170         */
171        public static final String VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
172        /**
173         * Extra information received with the android.intent.action.CHECK_TTS_DATA intent where
174         * the text-to-speech engine specifies the file names of its resources under the
175         * resource path.
176         */
177        public static final String VOICE_DATA_FILES = "dataFiles";
178        /**
179         * Extra information received with the android.intent.action.CHECK_TTS_DATA intent where
180         * the text-to-speech engine specifies the locale associated with each resource file.
181         */
182        public static final String VOICE_DATA_FILES_INFO = "dataFilesInfo";
183
184        // keys for the parameters passed with speak commands. Hidden keys are used internally
185        // to maintain engine state for each TextToSpeech instance.
186        /**
187         * {@hide}
188         */
189        public static final String TTS_KEY_PARAM_RATE = "rate";
190        /**
191         * {@hide}
192         */
193        public static final String TTS_KEY_PARAM_LANGUAGE = "language";
194        /**
195         * {@hide}
196         */
197        public static final String TTS_KEY_PARAM_COUNTRY = "country";
198        /**
199         * {@hide}
200         */
201        public static final String TTS_KEY_PARAM_VARIANT = "variant";
202        /**
203         * Parameter key to specify the audio stream type to be used when speaking text
204         * or playing back a file.
205         */
206        public static final String TTS_KEY_PARAM_STREAM = "streamType";
207        /**
208         * Parameter key to identify an utterance in the completion listener after text has been
209         * spoken, a file has been played back or a silence duration has elapsed.
210         */
211        public static final String TTS_KEY_PARAM_UTTERANCE_ID = "utteranceId";
212
213        // key positions in the array of cached parameters
214        /**
215         * {@hide}
216         */
217        protected static final int TTS_PARAM_POSITION_RATE = 0;
218        /**
219         * {@hide}
220         */
221        protected static final int TTS_PARAM_POSITION_LANGUAGE = 2;
222        /**
223         * {@hide}
224         */
225        protected static final int TTS_PARAM_POSITION_COUNTRY = 4;
226        /**
227         * {@hide}
228         */
229        protected static final int TTS_PARAM_POSITION_VARIANT = 6;
230        /**
231         * {@hide}
232         */
233        protected static final int TTS_PARAM_POSITION_STREAM = 8;
234        /**
235         * {@hide}
236         */
237        protected static final int TTS_PARAM_POSITION_UTTERANCE_ID = 10;
238        /**
239         * {@hide}
240         */
241        protected static final int TTS_NB_CACHED_PARAMS = 6;
242    }
243
244    /**
245     * Connection needed for the TTS.
246     */
247    private ServiceConnection mServiceConnection;
248
249    private ITts mITts = null;
250    private ITtsCallback mITtscallback = null;
251    private Context mContext = null;
252    private String mPackageName = "";
253    private OnInitListener mInitListener = null;
254    private boolean mStarted = false;
255    private final Object mStartLock = new Object();
256    /**
257     * Used to store the cached parameters sent along with each synthesis request to the
258     * TTS service.
259     */
260    private String[] mCachedParams;
261
262    /**
263     * The constructor for the TTS.
264     *
265     * @param context
266     *            The context
267     * @param listener
268     *            The InitListener that will be called when the TTS has
269     *            initialized successfully.
270     */
271    public TextToSpeech(Context context, OnInitListener listener) {
272        mContext = context;
273        mPackageName = mContext.getPackageName();
274        mInitListener = listener;
275
276        mCachedParams = new String[2*Engine.TTS_NB_CACHED_PARAMS]; // store key and value
277        mCachedParams[Engine.TTS_PARAM_POSITION_RATE] = Engine.TTS_KEY_PARAM_RATE;
278        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE] = Engine.TTS_KEY_PARAM_LANGUAGE;
279        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY] = Engine.TTS_KEY_PARAM_COUNTRY;
280        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT] = Engine.TTS_KEY_PARAM_VARIANT;
281        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM] = Engine.TTS_KEY_PARAM_STREAM;
282        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID] = Engine.TTS_KEY_PARAM_UTTERANCE_ID;
283
284        mCachedParams[Engine.TTS_PARAM_POSITION_RATE + 1] =
285                String.valueOf(Engine.FALLBACK_TTS_DEFAULT_RATE);
286        // initialize the language cached parameters with the current Locale
287        Locale defaultLoc = Locale.getDefault();
288        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1] = defaultLoc.getISO3Language();
289        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1] = defaultLoc.getISO3Country();
290        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] = defaultLoc.getVariant();
291
292        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM + 1] =
293                String.valueOf(Engine.TTS_DEFAULT_STREAM);
294        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID + 1] = "";
295
296        initTts();
297    }
298
299
300    private void initTts() {
301        mStarted = false;
302
303        // Initialize the TTS, run the callback after the binding is successful
304        mServiceConnection = new ServiceConnection() {
305            public void onServiceConnected(ComponentName name, IBinder service) {
306                synchronized(mStartLock) {
307                    mITts = ITts.Stub.asInterface(service);
308                    mStarted = true;
309                    if (mInitListener != null) {
310                        // TODO manage failures and missing resources
311                        mInitListener.onInit(TTS_SUCCESS);
312                    }
313                }
314            }
315
316            public void onServiceDisconnected(ComponentName name) {
317                synchronized(mStartLock) {
318                    mITts = null;
319                    mInitListener = null;
320                    mStarted = false;
321                }
322            }
323        };
324
325        Intent intent = new Intent("android.intent.action.START_TTS_SERVICE");
326        intent.addCategory("android.intent.category.TTS");
327        mContext.bindService(intent, mServiceConnection,
328                Context.BIND_AUTO_CREATE);
329        // TODO handle case where the binding works (should always work) but
330        //      the plugin fails
331    }
332
333
334    /**
335     * Shuts down the TTS. It is good practice to call this in the onDestroy
336     * method of the Activity that is using the TTS so that the TTS is stopped
337     * cleanly.
338     */
339    public void shutdown() {
340        try {
341            mContext.unbindService(mServiceConnection);
342        } catch (IllegalArgumentException e) {
343            // Do nothing and fail silently since an error here indicates that
344            // binding never succeeded in the first place.
345        }
346    }
347
348
349    /**
350     * Adds a mapping between a string of text and a sound resource in a
351     * package.
352     *
353     * @see #TTS.speak(String text, int queueMode, String[] params)
354     *
355     * @param text
356     *            Example: <b><code>"south_south_east"</code></b><br/>
357     *
358     * @param packagename
359     *            Pass the packagename of the application that contains the
360     *            resource. If the resource is in your own application (this is
361     *            the most common case), then put the packagename of your
362     *            application here.<br/>
363     *            Example: <b>"com.google.marvin.compass"</b><br/>
364     *            The packagename can be found in the AndroidManifest.xml of
365     *            your application.
366     *            <p>
367     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
368     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
369     *            </p>
370     *
371     * @param resourceId
372     *            Example: <b><code>R.raw.south_south_east</code></b>
373     *
374     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
375     */
376    public int addSpeech(String text, String packagename, int resourceId) {
377        synchronized(mStartLock) {
378            if (!mStarted) {
379                return TTS_ERROR;
380            }
381            try {
382                mITts.addSpeech(mPackageName, text, packagename, resourceId);
383                return TTS_SUCCESS;
384            } catch (RemoteException e) {
385                // TTS died; restart it.
386                Log.e("TextToSpeech.java - addSpeech", "RemoteException");
387                e.printStackTrace();
388                mStarted = false;
389                initTts();
390            } catch (NullPointerException e) {
391                // TTS died; restart it.
392                Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
393                e.printStackTrace();
394                mStarted = false;
395                initTts();
396            } catch (IllegalStateException e) {
397                // TTS died; restart it.
398                Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
399                e.printStackTrace();
400                mStarted = false;
401                initTts();
402            }
403            return TTS_ERROR;
404        }
405    }
406
407
408    /**
409     * Adds a mapping between a string of text and a sound file. Using this, it
410     * is possible to add custom pronounciations for text.
411     *
412     * @param text
413     *            The string of text
414     * @param filename
415     *            The full path to the sound file (for example:
416     *            "/sdcard/mysounds/hello.wav")
417     *
418     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
419     */
420    public int addSpeech(String text, String filename) {
421        synchronized (mStartLock) {
422            if (!mStarted) {
423                return TTS_ERROR;
424            }
425            try {
426                mITts.addSpeechFile(mPackageName, text, filename);
427                return TTS_SUCCESS;
428            } catch (RemoteException e) {
429                // TTS died; restart it.
430                Log.e("TextToSpeech.java - addSpeech", "RemoteException");
431                e.printStackTrace();
432                mStarted = false;
433                initTts();
434            } catch (NullPointerException e) {
435                // TTS died; restart it.
436                Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
437                e.printStackTrace();
438                mStarted = false;
439                initTts();
440            } catch (IllegalStateException e) {
441                // TTS died; restart it.
442                Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
443                e.printStackTrace();
444                mStarted = false;
445                initTts();
446            }
447            return TTS_ERROR;
448        }
449    }
450
451
452    /**
453     * Adds a mapping between a string of text and a sound resource in a
454     * package.
455     *
456     * @see #TTS.playEarcon(String earcon, int queueMode, String[] params)
457     *
458     * @param earcon The name of the earcon
459     *            Example: <b><code>"[tick]"</code></b><br/>
460     *
461     * @param packagename
462     *            Pass the packagename of the application that contains the
463     *            resource. If the resource is in your own application (this is
464     *            the most common case), then put the packagename of your
465     *            application here.<br/>
466     *            Example: <b>"com.google.marvin.compass"</b><br/>
467     *            The packagename can be found in the AndroidManifest.xml of
468     *            your application.
469     *            <p>
470     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
471     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
472     *            </p>
473     *
474     * @param resourceId
475     *            Example: <b><code>R.raw.tick_snd</code></b>
476     *
477     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
478     */
479    public int addEarcon(String earcon, String packagename, int resourceId) {
480        synchronized(mStartLock) {
481            if (!mStarted) {
482                return TTS_ERROR;
483            }
484            try {
485                mITts.addEarcon(mPackageName, earcon, packagename, resourceId);
486                return TTS_SUCCESS;
487            } catch (RemoteException e) {
488                // TTS died; restart it.
489                Log.e("TextToSpeech.java - addEarcon", "RemoteException");
490                e.printStackTrace();
491                mStarted = false;
492                initTts();
493            } catch (NullPointerException e) {
494                // TTS died; restart it.
495                Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
496                e.printStackTrace();
497                mStarted = false;
498                initTts();
499            } catch (IllegalStateException e) {
500                // TTS died; restart it.
501                Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
502                e.printStackTrace();
503                mStarted = false;
504                initTts();
505            }
506            return TTS_ERROR;
507        }
508    }
509
510
511    /**
512     * Adds a mapping between a string of text and a sound file. Using this, it
513     * is possible to add custom earcons.
514     *
515     * @param earcon
516     *            The name of the earcon
517     * @param filename
518     *            The full path to the sound file (for example:
519     *            "/sdcard/mysounds/tick.wav")
520     *
521     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
522     */
523    public int addEarcon(String earcon, String filename) {
524        synchronized (mStartLock) {
525            if (!mStarted) {
526                return TTS_ERROR;
527            }
528            try {
529                mITts.addEarconFile(mPackageName, earcon, filename);
530                return TTS_SUCCESS;
531            } catch (RemoteException e) {
532                // TTS died; restart it.
533                Log.e("TextToSpeech.java - addEarcon", "RemoteException");
534                e.printStackTrace();
535                mStarted = false;
536                initTts();
537            } catch (NullPointerException e) {
538                // TTS died; restart it.
539                Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
540                e.printStackTrace();
541                mStarted = false;
542                initTts();
543            } catch (IllegalStateException e) {
544                // TTS died; restart it.
545                Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
546                e.printStackTrace();
547                mStarted = false;
548                initTts();
549            }
550            return TTS_ERROR;
551        }
552    }
553
554
555    /**
556     * Speaks the string using the specified queuing strategy and speech
557     * parameters. Note that the speech parameters are not universally supported
558     * by all engines and will be treated as a hint. The TTS library will try to
559     * fulfill these parameters as much as possible, but there is no guarantee
560     * that the voice used will have the properties specified.
561     *
562     * @param text
563     *            The string of text to be spoken.
564     * @param queueMode
565     *            The queuing strategy to use.
566     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
567     * @param params
568     *            The hashmap of speech parameters to be used.
569     *
570     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
571     */
572    public int speak(String text, int queueMode, HashMap<String,String> params)
573    {
574        synchronized (mStartLock) {
575            int result = TTS_ERROR;
576            Log.i("TTS received: ", text);
577            if (!mStarted) {
578                return result;
579            }
580            try {
581                if ((params != null) && (!params.isEmpty())) {
582                    String extra = params.get(Engine.TTS_KEY_PARAM_STREAM);
583                    if (extra != null) {
584                        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM + 1] = extra;
585                    }
586                    extra = params.get(Engine.TTS_KEY_PARAM_UTTERANCE_ID);
587                    if (extra != null) {
588                        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID + 1] = extra;
589                    }
590                }
591                result = mITts.speak(mPackageName, text, queueMode, mCachedParams);
592            } catch (RemoteException e) {
593                // TTS died; restart it.
594                Log.e("TextToSpeech.java - speak", "RemoteException");
595                e.printStackTrace();
596                mStarted = false;
597                initTts();
598            } catch (NullPointerException e) {
599                // TTS died; restart it.
600                Log.e("TextToSpeech.java - speak", "NullPointerException");
601                e.printStackTrace();
602                mStarted = false;
603                initTts();
604            } catch (IllegalStateException e) {
605                // TTS died; restart it.
606                Log.e("TextToSpeech.java - speak", "IllegalStateException");
607                e.printStackTrace();
608                mStarted = false;
609                initTts();
610            } finally {
611                resetCachedParams();
612                return result;
613            }
614        }
615    }
616
617
618    /**
619     * Plays the earcon using the specified queueing mode and parameters.
620     *
621     * @param earcon
622     *            The earcon that should be played
623     * @param queueMode
624     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
625     * @param params
626     *            The hashmap of parameters to be used.
627     *
628     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
629     */
630    public int playEarcon(String earcon, int queueMode,
631            HashMap<String,String> params) {
632        synchronized (mStartLock) {
633            int result = TTS_ERROR;
634            if (!mStarted) {
635                return result;
636            }
637            try {
638                if ((params != null) && (!params.isEmpty())) {
639                    String extra = params.get(Engine.TTS_KEY_PARAM_STREAM);
640                    if (extra != null) {
641                        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM + 1] = extra;
642                    }
643                    extra = params.get(Engine.TTS_KEY_PARAM_UTTERANCE_ID);
644                    if (extra != null) {
645                        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID + 1] = extra;
646                    }
647                }
648                result = mITts.playEarcon(mPackageName, earcon, queueMode, null);
649            } catch (RemoteException e) {
650                // TTS died; restart it.
651                Log.e("TextToSpeech.java - playEarcon", "RemoteException");
652                e.printStackTrace();
653                mStarted = false;
654                initTts();
655            } catch (NullPointerException e) {
656                // TTS died; restart it.
657                Log.e("TextToSpeech.java - playEarcon", "NullPointerException");
658                e.printStackTrace();
659                mStarted = false;
660                initTts();
661            } catch (IllegalStateException e) {
662                // TTS died; restart it.
663                Log.e("TextToSpeech.java - playEarcon", "IllegalStateException");
664                e.printStackTrace();
665                mStarted = false;
666                initTts();
667            } finally {
668                resetCachedParams();
669                return result;
670            }
671        }
672    }
673
674    /**
675     * Plays silence for the specified amount of time using the specified
676     * queue mode.
677     *
678     * @param durationInMs
679     *            A long that indicates how long the silence should last.
680     * @param queueMode
681     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
682     *
683     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
684     */
685    public int playSilence(long durationInMs, int queueMode, HashMap<String,String> params) {
686        synchronized (mStartLock) {
687            int result = TTS_ERROR;
688            if (!mStarted) {
689                return result;
690            }
691            try {
692                if ((params != null) && (!params.isEmpty())) {
693                    String extra = params.get(Engine.TTS_KEY_PARAM_UTTERANCE_ID);
694                    if (extra != null) {
695                        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID + 1] = extra;
696                    }
697                }
698                result = mITts.playSilence(mPackageName, durationInMs, queueMode, mCachedParams);
699            } catch (RemoteException e) {
700                // TTS died; restart it.
701                Log.e("TextToSpeech.java - playSilence", "RemoteException");
702                e.printStackTrace();
703                mStarted = false;
704                initTts();
705            } catch (NullPointerException e) {
706                // TTS died; restart it.
707                Log.e("TextToSpeech.java - playSilence", "NullPointerException");
708                e.printStackTrace();
709                mStarted = false;
710                initTts();
711            } catch (IllegalStateException e) {
712                // TTS died; restart it.
713                Log.e("TextToSpeech.java - playSilence", "IllegalStateException");
714                e.printStackTrace();
715                mStarted = false;
716                initTts();
717            } finally {
718              return result;
719            }
720        }
721    }
722
723
724    /**
725     * Returns whether or not the TTS is busy speaking.
726     *
727     * @return Whether or not the TTS is busy speaking.
728     */
729    public boolean isSpeaking() {
730        synchronized (mStartLock) {
731            if (!mStarted) {
732                return false;
733            }
734            try {
735                return mITts.isSpeaking();
736            } catch (RemoteException e) {
737                // TTS died; restart it.
738                Log.e("TextToSpeech.java - isSpeaking", "RemoteException");
739                e.printStackTrace();
740                mStarted = false;
741                initTts();
742            } catch (NullPointerException e) {
743                // TTS died; restart it.
744                Log.e("TextToSpeech.java - isSpeaking", "NullPointerException");
745                e.printStackTrace();
746                mStarted = false;
747                initTts();
748            } catch (IllegalStateException e) {
749                // TTS died; restart it.
750                Log.e("TextToSpeech.java - isSpeaking", "IllegalStateException");
751                e.printStackTrace();
752                mStarted = false;
753                initTts();
754            }
755            return false;
756        }
757    }
758
759
760    /**
761     * Stops speech from the TTS.
762     *
763     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
764     */
765    public int stop() {
766        synchronized (mStartLock) {
767            int result = TTS_ERROR;
768            if (!mStarted) {
769                return result;
770            }
771            try {
772                result = mITts.stop(mPackageName);
773            } catch (RemoteException e) {
774                // TTS died; restart it.
775                Log.e("TextToSpeech.java - stop", "RemoteException");
776                e.printStackTrace();
777                mStarted = false;
778                initTts();
779            } catch (NullPointerException e) {
780                // TTS died; restart it.
781                Log.e("TextToSpeech.java - stop", "NullPointerException");
782                e.printStackTrace();
783                mStarted = false;
784                initTts();
785            } catch (IllegalStateException e) {
786                // TTS died; restart it.
787                Log.e("TextToSpeech.java - stop", "IllegalStateException");
788                e.printStackTrace();
789                mStarted = false;
790                initTts();
791            } finally {
792              return result;
793            }
794        }
795    }
796
797
798    /**
799     * Sets the speech rate for the TTS engine.
800     *
801     * Note that the speech rate is not universally supported by all engines and
802     * will be treated as a hint. The TTS library will try to use the specified
803     * speech rate, but there is no guarantee.
804     * This has no effect on any pre-recorded speech.
805     *
806     * @param speechRate
807     *            The speech rate for the TTS engine. 1 is the normal speed,
808     *            lower values slow down the speech (0.5 is half the normal speech rate),
809     *            greater values accelerate it (2 is twice the normal speech rate).
810     *
811     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
812     */
813    public int setSpeechRate(float speechRate) {
814        synchronized (mStartLock) {
815            int result = TTS_ERROR;
816            if (!mStarted) {
817                return result;
818            }
819            try {
820                if (speechRate > 0) {
821                    int rate = (int)(speechRate*100);
822                    mCachedParams[Engine.TTS_PARAM_POSITION_RATE + 1] = String.valueOf(rate);
823                    // the rate is not set here, instead it is cached so it will be associated
824                    // with all upcoming utterances.
825                    if (speechRate > 0.0f) {
826                        result = TTS_SUCCESS;
827                    } else {
828                        result = TTS_ERROR;
829                    }
830                }
831            } catch (NullPointerException e) {
832                // TTS died; restart it.
833                Log.e("TextToSpeech.java - setSpeechRate", "NullPointerException");
834                e.printStackTrace();
835                mStarted = false;
836                initTts();
837            } catch (IllegalStateException e) {
838                // TTS died; restart it.
839                Log.e("TextToSpeech.java - setSpeechRate", "IllegalStateException");
840                e.printStackTrace();
841                mStarted = false;
842                initTts();
843            } finally {
844              return result;
845            }
846        }
847    }
848
849
850    /**
851     * Sets the speech pitch for the TTS engine.
852     *
853     * Note that the pitch is not universally supported by all engines and
854     * will be treated as a hint. The TTS library will try to use the specified
855     * pitch, but there is no guarantee.
856     * This has no effect on any pre-recorded speech.
857     *
858     * @param pitch
859     *            The pitch for the TTS engine. 1 is the normal pitch,
860     *            lower values lower the tone of the synthesized voice,
861     *            greater values increase it.
862     *
863     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
864     */
865    public int setPitch(float pitch) {
866        synchronized (mStartLock) {
867            int result = TTS_ERROR;
868            if (!mStarted) {
869                return result;
870            }
871            try {
872                if (pitch > 0) {
873                    result = mITts.setPitch(mPackageName, (int)(pitch*100));
874                }
875            } catch (RemoteException e) {
876                // TTS died; restart it.
877                Log.e("TextToSpeech.java - setPitch", "RemoteException");
878                e.printStackTrace();
879                mStarted = false;
880                initTts();
881            } catch (NullPointerException e) {
882                // TTS died; restart it.
883                Log.e("TextToSpeech.java - setPitch", "NullPointerException");
884                e.printStackTrace();
885                mStarted = false;
886                initTts();
887            } catch (IllegalStateException e) {
888                // TTS died; restart it.
889                Log.e("TextToSpeech.java - setPitch", "IllegalStateException");
890                e.printStackTrace();
891                mStarted = false;
892                initTts();
893            } finally {
894              return result;
895            }
896        }
897    }
898
899
900    /**
901     * Sets the language for the TTS engine.
902     *
903     * Note that the language is not universally supported by all engines and
904     * will be treated as a hint. The TTS library will try to use the specified
905     * language as represented by the Locale, but there is no guarantee.
906     *
907     * @param loc
908     *            The locale describing the language to be used.
909     *
910     * @return code indicating the support status for the locale. See {@link #TTS_LANG_AVAILABLE},
911     *         {@link #TTS_LANG_COUNTRY_AVAILABLE}, {@link #TTS_LANG_COUNTRY_VAR_AVAILABLE},
912     *         {@link #TTS_LANG_MISSING_DATA} and {@link #TTS_LANG_NOT_SUPPORTED}.
913     */
914    public int setLanguage(Locale loc) {
915        synchronized (mStartLock) {
916            int result = TTS_LANG_NOT_SUPPORTED;
917            if (!mStarted) {
918                return result;
919            }
920            try {
921                mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1] = loc.getISO3Language();
922                mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1] = loc.getISO3Country();
923                mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] = loc.getVariant();
924
925                result = mITts.setLanguage(mPackageName,
926                        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1],
927                        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1],
928                        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] );
929            } catch (RemoteException e) {
930                // TTS died; restart it.
931                Log.e("TextToSpeech.java - setLanguage", "RemoteException");
932                e.printStackTrace();
933                mStarted = false;
934                initTts();
935            } catch (NullPointerException e) {
936                // TTS died; restart it.
937                Log.e("TextToSpeech.java - setLanguage", "NullPointerException");
938                e.printStackTrace();
939                mStarted = false;
940                initTts();
941            } catch (IllegalStateException e) {
942                // TTS died; restart it.
943                Log.e("TextToSpeech.java - setLanguage", "IllegalStateException");
944                e.printStackTrace();
945                mStarted = false;
946                initTts();
947            } finally {
948              return result;
949            }
950        }
951    }
952
953
954    /**
955     * Returns a Locale instance describing the language currently being used by the TTS engine.
956     * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
957     *     instance, or null is the TTS engine has failed.
958     */
959    public Locale getLanguage() {
960        synchronized (mStartLock) {
961            if (!mStarted) {
962                return null;
963            }
964            try {
965                String[] locStrings =  mITts.getLanguage();
966                if (locStrings.length == 3) {
967                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
968                } else {
969                    return null;
970                }
971            } catch (RemoteException e) {
972                // TTS died; restart it.
973                Log.e("TextToSpeech.java - getLanguage", "RemoteException");
974                e.printStackTrace();
975                mStarted = false;
976                initTts();
977            } catch (NullPointerException e) {
978                // TTS died; restart it.
979                Log.e("TextToSpeech.java - getLanguage", "NullPointerException");
980                e.printStackTrace();
981                mStarted = false;
982                initTts();
983            } catch (IllegalStateException e) {
984                // TTS died; restart it.
985                Log.e("TextToSpeech.java - getLanguage", "IllegalStateException");
986                e.printStackTrace();
987                mStarted = false;
988                initTts();
989            }
990            return null;
991        }
992    }
993
994    /**
995     * Checks if the specified language as represented by the Locale is available.
996     *
997     * @param loc
998     *            The Locale describing the language to be used.
999     *
1000     * @return code indicating the support status for the locale. See {@link #TTS_LANG_AVAILABLE},
1001     *         {@link #TTS_LANG_COUNTRY_AVAILABLE}, {@link #TTS_LANG_COUNTRY_VAR_AVAILABLE},
1002     *         {@link #TTS_LANG_MISSING_DATA} and {@link #TTS_LANG_NOT_SUPPORTED}.
1003     */
1004    public int isLanguageAvailable(Locale loc) {
1005        synchronized (mStartLock) {
1006            int result = TTS_LANG_NOT_SUPPORTED;
1007            if (!mStarted) {
1008                return result;
1009            }
1010            try {
1011                result = mITts.isLanguageAvailable(loc.getISO3Language(),
1012                        loc.getISO3Country(), loc.getVariant());
1013            } catch (RemoteException e) {
1014                // TTS died; restart it.
1015                Log.e("TextToSpeech.java - isLanguageAvailable", "RemoteException");
1016                e.printStackTrace();
1017                mStarted = false;
1018                initTts();
1019            } catch (NullPointerException e) {
1020                // TTS died; restart it.
1021                Log.e("TextToSpeech.java - isLanguageAvailable", "NullPointerException");
1022                e.printStackTrace();
1023                mStarted = false;
1024                initTts();
1025            } catch (IllegalStateException e) {
1026                // TTS died; restart it.
1027                Log.e("TextToSpeech.java - isLanguageAvailable", "IllegalStateException");
1028                e.printStackTrace();
1029                mStarted = false;
1030                initTts();
1031            } finally {
1032              return result;
1033            }
1034        }
1035    }
1036
1037
1038    /**
1039     * Synthesizes the given text to a file using the specified parameters.
1040     *
1041     * @param text
1042     *            The String of text that should be synthesized
1043     * @param params
1044     *            A hashmap of parameters.
1045     * @param filename
1046     *            The string that gives the full output filename; it should be
1047     *            something like "/sdcard/myappsounds/mysound.wav".
1048     *
1049     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
1050     */
1051    public int synthesizeToFile(String text, HashMap<String,String> params,
1052            String filename) {
1053        synchronized (mStartLock) {
1054            int result = TTS_ERROR;
1055            if (!mStarted) {
1056                return result;
1057            }
1058            try {
1059                if ((params != null) && (!params.isEmpty())) {
1060                    // no need to read the stream type here
1061                    String extra = params.get(Engine.TTS_KEY_PARAM_UTTERANCE_ID);
1062                    if (extra != null) {
1063                        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID + 1] = extra;
1064                    }
1065                }
1066                if (mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename)){
1067                    result = TTS_SUCCESS;
1068                }
1069            } catch (RemoteException e) {
1070                // TTS died; restart it.
1071                Log.e("TextToSpeech.java - synthesizeToFile", "RemoteException");
1072                e.printStackTrace();
1073                mStarted = false;
1074                initTts();
1075            } catch (NullPointerException e) {
1076                // TTS died; restart it.
1077                Log.e("TextToSpeech.java - synthesizeToFile", "NullPointerException");
1078                e.printStackTrace();
1079                mStarted = false;
1080                initTts();
1081            } catch (IllegalStateException e) {
1082                // TTS died; restart it.
1083                Log.e("TextToSpeech.java - synthesizeToFile", "IllegalStateException");
1084                e.printStackTrace();
1085                mStarted = false;
1086                initTts();
1087            } finally {
1088                resetCachedParams();
1089                return result;
1090            }
1091        }
1092    }
1093
1094
1095    /**
1096     * Convenience method to reset the cached parameters to the current default values
1097     * if they are not persistent between calls to the service.
1098     */
1099    private void resetCachedParams() {
1100        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM + 1] =
1101                String.valueOf(Engine.TTS_DEFAULT_STREAM);
1102        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID+ 1] = "";
1103    }
1104
1105    /**
1106     * Sets the OnUtteranceCompletedListener that will fire when an utterance completes.
1107     *
1108     * @param listener
1109     *            The OnUtteranceCompletedListener
1110     *
1111     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
1112     */
1113    public int setOnUtteranceCompletedListener(
1114            final OnUtteranceCompletedListener listener) {
1115        synchronized (mStartLock) {
1116            int result = TTS_ERROR;
1117            if (!mStarted) {
1118                return result;
1119            }
1120            mITtscallback = new ITtsCallback.Stub() {
1121                public void utteranceCompleted(String utteranceId) throws RemoteException {
1122                    if (listener != null) {
1123                        listener.onUtteranceCompleted(utteranceId);
1124                    }
1125                }
1126            };
1127            try {
1128                result = mITts.registerCallback(mPackageName, mITtscallback);
1129            } catch (RemoteException e) {
1130                // TTS died; restart it.
1131                Log.e("TextToSpeech.java - registerCallback", "RemoteException");
1132                e.printStackTrace();
1133                mStarted = false;
1134                initTts();
1135            } catch (NullPointerException e) {
1136                // TTS died; restart it.
1137                Log.e("TextToSpeech.java - registerCallback", "NullPointerException");
1138                e.printStackTrace();
1139                mStarted = false;
1140                initTts();
1141            } catch (IllegalStateException e) {
1142                // TTS died; restart it.
1143                Log.e("TextToSpeech.java - registerCallback", "IllegalStateException");
1144                e.printStackTrace();
1145                mStarted = false;
1146                initTts();
1147            } finally {
1148                return result;
1149            }
1150        }
1151    }
1152
1153}
1154