TextToSpeech.java revision 630a8de44fa0ca855c4a87d939432f831e8ed531
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     * Internal constants for the TTS functionality
103     *
104     * {@hide}
105     */
106    public class Engine {
107        // default values for a TTS engine when settings are not found in the provider
108        public static final int FALLBACK_TTS_DEFAULT_RATE = 100; // 1x
109        public static final int FALLBACK_TTS_DEFAULT_PITCH = 100;// 1x
110        public static final int FALLBACK_TTS_USE_DEFAULTS = 0; // false
111        public static final String FALLBACK_TTS_DEFAULT_SYNTH = "com.svox.pico";
112
113        // default values for rendering
114        public static final int TTS_DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
115
116        // return codes for a TTS engine's check data activity
117        public static final int CHECK_VOICE_DATA_PASS = 1;
118        public static final int CHECK_VOICE_DATA_FAIL = 0;
119        public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
120        public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
121        public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
122
123        // return codes for a TTS engine's check data activity
124        public static final String VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
125        public static final String VOICE_DATA_FILES = "dataFiles";
126        public static final String VOICE_DATA_FILES_INFO = "dataFilesInfo";
127
128        // keys for the parameters passed with speak commands
129        public static final String TTS_KEY_PARAM_RATE = "rate";
130        public static final String TTS_KEY_PARAM_LANGUAGE = "language";
131        public static final String TTS_KEY_PARAM_COUNTRY = "country";
132        public static final String TTS_KEY_PARAM_VARIANT = "variant";
133        public static final String TTS_KEY_PARAM_STREAM = "streamType";
134        public static final String TTS_KEY_PARAM_UTTERANCE_ID = "utteranceId";
135        protected static final int TTS_PARAM_POSITION_RATE = 0;
136        protected static final int TTS_PARAM_POSITION_LANGUAGE = 2;
137        protected static final int TTS_PARAM_POSITION_COUNTRY = 4;
138        protected static final int TTS_PARAM_POSITION_VARIANT = 6;
139        protected static final int TTS_PARAM_POSITION_STREAM = 8;
140        protected static final int TTS_PARAM_POSITION_UTTERANCE_ID = 10;
141        protected static final int TTS_NB_CACHED_PARAMS = 6;
142    }
143
144    /**
145     * Connection needed for the TTS.
146     */
147    private ServiceConnection mServiceConnection;
148
149    private ITts mITts = null;
150    private Context mContext = null;
151    private String mPackageName = "";
152    private OnInitListener mInitListener = null;
153    private boolean mStarted = false;
154    private final Object mStartLock = new Object();
155    /**
156     * Used to store the cached parameters sent along with each synthesis request to the
157     * TTS service.
158     */
159    private String[] mCachedParams;
160
161    /**
162     * The constructor for the TTS.
163     *
164     * @param context
165     *            The context
166     * @param listener
167     *            The InitListener that will be called when the TTS has
168     *            initialized successfully.
169     */
170    public TextToSpeech(Context context, OnInitListener listener) {
171        mContext = context;
172        mPackageName = mContext.getPackageName();
173        mInitListener = listener;
174
175        mCachedParams = new String[2*Engine.TTS_NB_CACHED_PARAMS]; // store key and value
176        mCachedParams[Engine.TTS_PARAM_POSITION_RATE] = Engine.TTS_KEY_PARAM_RATE;
177        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE] = Engine.TTS_KEY_PARAM_LANGUAGE;
178        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY] = Engine.TTS_KEY_PARAM_COUNTRY;
179        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT] = Engine.TTS_KEY_PARAM_VARIANT;
180        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM] = Engine.TTS_KEY_PARAM_STREAM;
181
182        mCachedParams[Engine.TTS_PARAM_POSITION_RATE + 1] =
183                String.valueOf(Engine.FALLBACK_TTS_DEFAULT_RATE);
184        // initialize the language cached parameters with the current Locale
185        Locale defaultLoc = Locale.getDefault();
186        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1] = defaultLoc.getISO3Language();
187        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1] = defaultLoc.getISO3Country();
188        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] = defaultLoc.getVariant();
189
190        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM + 1] =
191                String.valueOf(Engine.TTS_DEFAULT_STREAM);
192        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID+ 1] = "";
193
194        initTts();
195    }
196
197
198    private void initTts() {
199        mStarted = false;
200
201        // Initialize the TTS, run the callback after the binding is successful
202        mServiceConnection = new ServiceConnection() {
203            public void onServiceConnected(ComponentName name, IBinder service) {
204                synchronized(mStartLock) {
205                    mITts = ITts.Stub.asInterface(service);
206                    mStarted = true;
207                    if (mInitListener != null) {
208                        // TODO manage failures and missing resources
209                        mInitListener.onInit(TTS_SUCCESS);
210                    }
211                }
212            }
213
214            public void onServiceDisconnected(ComponentName name) {
215                synchronized(mStartLock) {
216                    mITts = null;
217                    mInitListener = null;
218                    mStarted = false;
219                }
220            }
221        };
222
223        Intent intent = new Intent("android.intent.action.START_TTS_SERVICE");
224        intent.addCategory("android.intent.category.TTS");
225        mContext.bindService(intent, mServiceConnection,
226                Context.BIND_AUTO_CREATE);
227        // TODO handle case where the binding works (should always work) but
228        //      the plugin fails
229    }
230
231
232    /**
233     * Shuts down the TTS. It is good practice to call this in the onDestroy
234     * method of the Activity that is using the TTS so that the TTS is stopped
235     * cleanly.
236     */
237    public void shutdown() {
238        try {
239            mContext.unbindService(mServiceConnection);
240        } catch (IllegalArgumentException e) {
241            // Do nothing and fail silently since an error here indicates that
242            // binding never succeeded in the first place.
243        }
244    }
245
246
247    /**
248     * Adds a mapping between a string of text and a sound resource in a
249     * package.
250     *
251     * @see #TTS.speak(String text, int queueMode, String[] params)
252     *
253     * @param text
254     *            Example: <b><code>"south_south_east"</code></b><br/>
255     *
256     * @param packagename
257     *            Pass the packagename of the application that contains the
258     *            resource. If the resource is in your own application (this is
259     *            the most common case), then put the packagename of your
260     *            application here.<br/>
261     *            Example: <b>"com.google.marvin.compass"</b><br/>
262     *            The packagename can be found in the AndroidManifest.xml of
263     *            your application.
264     *            <p>
265     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
266     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
267     *            </p>
268     *
269     * @param resourceId
270     *            Example: <b><code>R.raw.south_south_east</code></b>
271     *
272     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
273     */
274    public int addSpeech(String text, String packagename, int resourceId) {
275        synchronized(mStartLock) {
276            if (!mStarted) {
277                return TTS_ERROR;
278            }
279            try {
280                mITts.addSpeech(mPackageName, text, packagename, resourceId);
281                return TTS_SUCCESS;
282            } catch (RemoteException e) {
283                // TTS died; restart it.
284                Log.e("TextToSpeech.java - addSpeech", "RemoteException");
285                e.printStackTrace();
286                mStarted = false;
287                initTts();
288            } catch (NullPointerException e) {
289                // TTS died; restart it.
290                Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
291                e.printStackTrace();
292                mStarted = false;
293                initTts();
294            } catch (IllegalStateException e) {
295                // TTS died; restart it.
296                Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
297                e.printStackTrace();
298                mStarted = false;
299                initTts();
300            }
301            return TTS_ERROR;
302        }
303    }
304
305
306    /**
307     * Adds a mapping between a string of text and a sound file. Using this, it
308     * is possible to add custom pronounciations for text.
309     *
310     * @param text
311     *            The string of text
312     * @param filename
313     *            The full path to the sound file (for example:
314     *            "/sdcard/mysounds/hello.wav")
315     *
316     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
317     */
318    public int addSpeech(String text, String filename) {
319        synchronized (mStartLock) {
320            if (!mStarted) {
321                return TTS_ERROR;
322            }
323            try {
324                mITts.addSpeechFile(mPackageName, text, filename);
325                return TTS_SUCCESS;
326            } catch (RemoteException e) {
327                // TTS died; restart it.
328                Log.e("TextToSpeech.java - addSpeech", "RemoteException");
329                e.printStackTrace();
330                mStarted = false;
331                initTts();
332            } catch (NullPointerException e) {
333                // TTS died; restart it.
334                Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
335                e.printStackTrace();
336                mStarted = false;
337                initTts();
338            } catch (IllegalStateException e) {
339                // TTS died; restart it.
340                Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
341                e.printStackTrace();
342                mStarted = false;
343                initTts();
344            }
345            return TTS_ERROR;
346        }
347    }
348
349
350    /**
351     * Speaks the string using the specified queuing strategy and speech
352     * parameters. Note that the speech parameters are not universally supported
353     * by all engines and will be treated as a hint. The TTS library will try to
354     * fulfill these parameters as much as possible, but there is no guarantee
355     * that the voice used will have the properties specified.
356     *
357     * @param text
358     *            The string of text to be spoken.
359     * @param queueMode
360     *            The queuing strategy to use.
361     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
362     * @param params
363     *            The hashmap of speech parameters to be used.
364     *
365     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
366     */
367    public int speak(String text, int queueMode, HashMap<String,String> params)
368    {
369        synchronized (mStartLock) {
370            int result = TTS_ERROR;
371            Log.i("TTS received: ", text);
372            if (!mStarted) {
373                return result;
374            }
375            try {
376                if ((params != null) && (!params.isEmpty())) {
377                    String extra = params.get(Engine.TTS_KEY_PARAM_STREAM);
378                    if (extra != null) {
379                        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM + 1] = extra;
380                    }
381                    extra = params.get(Engine.TTS_KEY_PARAM_UTTERANCE_ID);
382                    if (extra != null) {
383                        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID] = extra;
384                    }
385                }
386                result = mITts.speak(mPackageName, text, queueMode, mCachedParams);
387            } catch (RemoteException e) {
388                // TTS died; restart it.
389                Log.e("TextToSpeech.java - speak", "RemoteException");
390                e.printStackTrace();
391                mStarted = false;
392                initTts();
393            } catch (NullPointerException e) {
394                // TTS died; restart it.
395                Log.e("TextToSpeech.java - speak", "NullPointerException");
396                e.printStackTrace();
397                mStarted = false;
398                initTts();
399            } catch (IllegalStateException e) {
400                // TTS died; restart it.
401                Log.e("TextToSpeech.java - speak", "IllegalStateException");
402                e.printStackTrace();
403                mStarted = false;
404                initTts();
405            } finally {
406                resetCachedParams();
407                return result;
408            }
409        }
410    }
411
412
413    /**
414     * Plays the earcon using the specified queueing mode and parameters.
415     *
416     * @param earcon
417     *            The earcon that should be played
418     * @param queueMode
419     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
420     * @param params
421     *            The hashmap of parameters to be used.
422     *
423     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
424     */
425    public int playEarcon(String earcon, int queueMode,
426            HashMap<String,String> params) {
427        synchronized (mStartLock) {
428            int result = TTS_ERROR;
429            if (!mStarted) {
430                return result;
431            }
432            try {
433                if ((params != null) && (!params.isEmpty())) {
434                    String extra = params.get(Engine.TTS_KEY_PARAM_STREAM);
435                    if (extra != null) {
436                        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM + 1] = extra;
437                    }
438                    extra = params.get(Engine.TTS_KEY_PARAM_UTTERANCE_ID);
439                    if (extra != null) {
440                        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID] = extra;
441                    }
442                }
443                result = mITts.playEarcon(mPackageName, earcon, queueMode, null);
444            } catch (RemoteException e) {
445                // TTS died; restart it.
446                Log.e("TextToSpeech.java - playEarcon", "RemoteException");
447                e.printStackTrace();
448                mStarted = false;
449                initTts();
450            } catch (NullPointerException e) {
451                // TTS died; restart it.
452                Log.e("TextToSpeech.java - playEarcon", "NullPointerException");
453                e.printStackTrace();
454                mStarted = false;
455                initTts();
456            } catch (IllegalStateException e) {
457                // TTS died; restart it.
458                Log.e("TextToSpeech.java - playEarcon", "IllegalStateException");
459                e.printStackTrace();
460                mStarted = false;
461                initTts();
462            } finally {
463                resetCachedParams();
464                return result;
465            }
466        }
467    }
468
469    /**
470     * Plays silence for the specified amount of time using the specified
471     * queue mode.
472     *
473     * @param durationInMs
474     *            A long that indicates how long the silence should last.
475     * @param queueMode
476     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
477     *
478     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
479     */
480    public int playSilence(long durationInMs, int queueMode) {
481        synchronized (mStartLock) {
482            int result = TTS_ERROR;
483            if (!mStarted) {
484                return result;
485            }
486            try {
487                result = mITts.playSilence(mPackageName, durationInMs, queueMode, mCachedParams);
488            } catch (RemoteException e) {
489                // TTS died; restart it.
490                Log.e("TextToSpeech.java - playSilence", "RemoteException");
491                e.printStackTrace();
492                mStarted = false;
493                initTts();
494            } catch (NullPointerException e) {
495                // TTS died; restart it.
496                Log.e("TextToSpeech.java - playSilence", "NullPointerException");
497                e.printStackTrace();
498                mStarted = false;
499                initTts();
500            } catch (IllegalStateException e) {
501                // TTS died; restart it.
502                Log.e("TextToSpeech.java - playSilence", "IllegalStateException");
503                e.printStackTrace();
504                mStarted = false;
505                initTts();
506            } finally {
507              return result;
508            }
509        }
510    }
511
512
513    /**
514     * Returns whether or not the TTS is busy speaking.
515     *
516     * @return Whether or not the TTS is busy speaking.
517     */
518    public boolean isSpeaking() {
519        synchronized (mStartLock) {
520            if (!mStarted) {
521                return false;
522            }
523            try {
524                return mITts.isSpeaking();
525            } catch (RemoteException e) {
526                // TTS died; restart it.
527                Log.e("TextToSpeech.java - isSpeaking", "RemoteException");
528                e.printStackTrace();
529                mStarted = false;
530                initTts();
531            } catch (NullPointerException e) {
532                // TTS died; restart it.
533                Log.e("TextToSpeech.java - isSpeaking", "NullPointerException");
534                e.printStackTrace();
535                mStarted = false;
536                initTts();
537            } catch (IllegalStateException e) {
538                // TTS died; restart it.
539                Log.e("TextToSpeech.java - isSpeaking", "IllegalStateException");
540                e.printStackTrace();
541                mStarted = false;
542                initTts();
543            }
544            return false;
545        }
546    }
547
548
549    /**
550     * Stops speech from the TTS.
551     *
552     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
553     */
554    public int stop() {
555        synchronized (mStartLock) {
556            int result = TTS_ERROR;
557            if (!mStarted) {
558                return result;
559            }
560            try {
561                result = mITts.stop(mPackageName);
562            } catch (RemoteException e) {
563                // TTS died; restart it.
564                Log.e("TextToSpeech.java - stop", "RemoteException");
565                e.printStackTrace();
566                mStarted = false;
567                initTts();
568            } catch (NullPointerException e) {
569                // TTS died; restart it.
570                Log.e("TextToSpeech.java - stop", "NullPointerException");
571                e.printStackTrace();
572                mStarted = false;
573                initTts();
574            } catch (IllegalStateException e) {
575                // TTS died; restart it.
576                Log.e("TextToSpeech.java - stop", "IllegalStateException");
577                e.printStackTrace();
578                mStarted = false;
579                initTts();
580            } finally {
581              return result;
582            }
583        }
584    }
585
586
587    /**
588     * Sets the speech rate for the TTS engine.
589     *
590     * Note that the speech rate is not universally supported by all engines and
591     * will be treated as a hint. The TTS library will try to use the specified
592     * speech rate, but there is no guarantee.
593     * This has no effect on any pre-recorded speech.
594     *
595     * @param speechRate
596     *            The speech rate for the TTS engine. 1 is the normal speed,
597     *            lower values slow down the speech (0.5 is half the normal speech rate),
598     *            greater values accelerate it (2 is twice the normal speech rate).
599     *
600     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
601     */
602    public int setSpeechRate(float speechRate) {
603        synchronized (mStartLock) {
604            int result = TTS_ERROR;
605            if (!mStarted) {
606                return result;
607            }
608            try {
609                if (speechRate > 0) {
610                    int rate = (int)(speechRate*100);
611                    mCachedParams[Engine.TTS_PARAM_POSITION_RATE + 1] = String.valueOf(rate);
612                    result = mITts.setSpeechRate(mPackageName, rate);
613                }
614            } catch (RemoteException e) {
615                // TTS died; restart it.
616                Log.e("TextToSpeech.java - setSpeechRate", "RemoteException");
617                e.printStackTrace();
618                mStarted = false;
619                initTts();
620            } catch (NullPointerException e) {
621                // TTS died; restart it.
622                Log.e("TextToSpeech.java - setSpeechRate", "NullPointerException");
623                e.printStackTrace();
624                mStarted = false;
625                initTts();
626            } catch (IllegalStateException e) {
627                // TTS died; restart it.
628                Log.e("TextToSpeech.java - setSpeechRate", "IllegalStateException");
629                e.printStackTrace();
630                mStarted = false;
631                initTts();
632            } finally {
633              return result;
634            }
635        }
636    }
637
638
639    /**
640     * Sets the speech pitch for the TTS engine.
641     *
642     * Note that the pitch is not universally supported by all engines and
643     * will be treated as a hint. The TTS library will try to use the specified
644     * pitch, but there is no guarantee.
645     * This has no effect on any pre-recorded speech.
646     *
647     * @param pitch
648     *            The pitch for the TTS engine. 1 is the normal pitch,
649     *            lower values lower the tone of the synthesized voice,
650     *            greater values increase it.
651     *
652     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
653     */
654    public int setPitch(float pitch) {
655        synchronized (mStartLock) {
656            int result = TTS_ERROR;
657            if (!mStarted) {
658                return result;
659            }
660            try {
661                if (pitch > 0) {
662                    result = mITts.setPitch(mPackageName, (int)(pitch*100));
663                }
664            } catch (RemoteException e) {
665                // TTS died; restart it.
666                Log.e("TextToSpeech.java - setPitch", "RemoteException");
667                e.printStackTrace();
668                mStarted = false;
669                initTts();
670            } catch (NullPointerException e) {
671                // TTS died; restart it.
672                Log.e("TextToSpeech.java - setPitch", "NullPointerException");
673                e.printStackTrace();
674                mStarted = false;
675                initTts();
676            } catch (IllegalStateException e) {
677                // TTS died; restart it.
678                Log.e("TextToSpeech.java - setPitch", "IllegalStateException");
679                e.printStackTrace();
680                mStarted = false;
681                initTts();
682            } finally {
683              return result;
684            }
685        }
686    }
687
688
689    /**
690     * Sets the language for the TTS engine.
691     *
692     * Note that the language is not universally supported by all engines and
693     * will be treated as a hint. The TTS library will try to use the specified
694     * language as represented by the Locale, but there is no guarantee.
695     *
696     * @param loc
697     *            The locale describing the language to be used.
698     *
699     * @return Code indicating the support status for the locale. See the TTS_LANG_ codes.
700     */
701    public int setLanguage(Locale loc) {
702        synchronized (mStartLock) {
703            int result = TTS_LANG_NOT_SUPPORTED;
704            if (!mStarted) {
705                return result;
706            }
707            try {
708                mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1] = loc.getISO3Language();
709                mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1] = loc.getISO3Country();
710                mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] = loc.getVariant();
711                result = mITts.setLanguage(mPackageName,
712                        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1],
713                        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1],
714                        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] );
715            } catch (RemoteException e) {
716                // TTS died; restart it.
717                Log.e("TextToSpeech.java - setLanguage", "RemoteException");
718                e.printStackTrace();
719                mStarted = false;
720                initTts();
721            } catch (NullPointerException e) {
722                // TTS died; restart it.
723                Log.e("TextToSpeech.java - setLanguage", "NullPointerException");
724                e.printStackTrace();
725                mStarted = false;
726                initTts();
727            } catch (IllegalStateException e) {
728                // TTS died; restart it.
729                Log.e("TextToSpeech.java - setLanguage", "IllegalStateException");
730                e.printStackTrace();
731                mStarted = false;
732                initTts();
733            } finally {
734              return result;
735            }
736        }
737    }
738
739
740    /**
741     * Returns a Locale instance describing the language currently being used by the TTS engine.
742     * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
743     *     instance, or null is the TTS engine has failed.
744     */
745    public Locale getLanguage() {
746        synchronized (mStartLock) {
747            if (!mStarted) {
748                return null;
749            }
750            try {
751                String[] locStrings =  mITts.getLanguage();
752                if (locStrings.length == 3) {
753                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
754                } else {
755                    return null;
756                }
757            } catch (RemoteException e) {
758                // TTS died; restart it.
759                Log.e("TextToSpeech.java - getLanguage", "RemoteException");
760                e.printStackTrace();
761                mStarted = false;
762                initTts();
763            } catch (NullPointerException e) {
764                // TTS died; restart it.
765                Log.e("TextToSpeech.java - getLanguage", "NullPointerException");
766                e.printStackTrace();
767                mStarted = false;
768                initTts();
769            } catch (IllegalStateException e) {
770                // TTS died; restart it.
771                Log.e("TextToSpeech.java - getLanguage", "IllegalStateException");
772                e.printStackTrace();
773                mStarted = false;
774                initTts();
775            }
776            return null;
777        }
778    }
779
780    /**
781     * Checks if the specified language as represented by the Locale is available.
782     *
783     * @param loc
784     *            The Locale describing the language to be used.
785     *
786     * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
787     *         TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
788     */
789    public int isLanguageAvailable(Locale loc) {
790        synchronized (mStartLock) {
791            int result = TTS_LANG_NOT_SUPPORTED;
792            if (!mStarted) {
793                return result;
794            }
795            try {
796                result = mITts.isLanguageAvailable(loc.getISO3Language(),
797                        loc.getISO3Country(), loc.getVariant());
798            } catch (RemoteException e) {
799                // TTS died; restart it.
800                Log.e("TextToSpeech.java - isLanguageAvailable", "RemoteException");
801                e.printStackTrace();
802                mStarted = false;
803                initTts();
804            } catch (NullPointerException e) {
805                // TTS died; restart it.
806                Log.e("TextToSpeech.java - isLanguageAvailable", "NullPointerException");
807                e.printStackTrace();
808                mStarted = false;
809                initTts();
810            } catch (IllegalStateException e) {
811                // TTS died; restart it.
812                Log.e("TextToSpeech.java - isLanguageAvailable", "IllegalStateException");
813                e.printStackTrace();
814                mStarted = false;
815                initTts();
816            } finally {
817              return result;
818            }
819        }
820    }
821
822
823    /**
824     * Synthesizes the given text to a file using the specified parameters.
825     *
826     * @param text
827     *            The String of text that should be synthesized
828     * @param params
829     *            A hashmap of parameters.
830     * @param filename
831     *            The string that gives the full output filename; it should be
832     *            something like "/sdcard/myappsounds/mysound.wav".
833     *
834     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
835     */
836    public int synthesizeToFile(String text, HashMap<String,String> params,
837            String filename) {
838        synchronized (mStartLock) {
839            int result = TTS_ERROR;
840            if (!mStarted) {
841                return result;
842            }
843            try {
844                if ((params != null) && (!params.isEmpty())) {
845                    // no need to read the stream type here
846                    String extra = params.get(Engine.TTS_KEY_PARAM_UTTERANCE_ID);
847                    if (extra != null) {
848                        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID] = extra;
849                    }
850                }
851                if (mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename)){
852                    result = TTS_SUCCESS;
853                }
854            } catch (RemoteException e) {
855                // TTS died; restart it.
856                Log.e("TextToSpeech.java - synthesizeToFile", "RemoteException");
857                e.printStackTrace();
858                mStarted = false;
859                initTts();
860            } catch (NullPointerException e) {
861                // TTS died; restart it.
862                Log.e("TextToSpeech.java - synthesizeToFile", "NullPointerException");
863                e.printStackTrace();
864                mStarted = false;
865                initTts();
866            } catch (IllegalStateException e) {
867                // TTS died; restart it.
868                Log.e("TextToSpeech.java - synthesizeToFile", "IllegalStateException");
869                e.printStackTrace();
870                mStarted = false;
871                initTts();
872            } finally {
873                resetCachedParams();
874                return result;
875            }
876        }
877    }
878
879
880    /**
881     * Convenience method to reset the cached parameters to the current default values
882     * if they are not persistent between calls to the service.
883     */
884    private void resetCachedParams() {
885        mCachedParams[Engine.TTS_PARAM_POSITION_STREAM + 1] =
886                String.valueOf(Engine.TTS_DEFAULT_STREAM);
887        mCachedParams[Engine.TTS_PARAM_POSITION_UTTERANCE_ID+ 1] = "";
888    }
889
890}
891