TextToSpeech.java revision 2ea5349583de4a505501530d04133524bb6d5d38
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.os.IBinder;
26import android.os.RemoteException;
27import android.util.Log;
28
29import java.util.HashMap;
30import java.util.Locale;
31
32/**
33 *
34 * Synthesizes speech from text.
35 *
36 * {@hide}
37 */
38//TODO #TTS# review + 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     * Denotes a failure due to a missing resource.
51     */
52    public static final int TTS_ERROR_MISSING_RESOURCE = -2;
53
54    /**
55     * Queue mode where all entries in the playback queue (media to be played
56     * and text to be synthesized) are dropped and replaced by the new entry.
57     */
58    public static final int TTS_QUEUE_FLUSH = 0;
59    /**
60     * Queue mode where the new entry is added at the end of the playback queue.
61     */
62    public static final int TTS_QUEUE_ADD = 1;
63
64    /**
65     * Called when the TTS has initialized.
66     *
67     * The InitListener must implement the onInit function. onInit is passed a
68     * status code indicating the result of the TTS initialization.
69     */
70    public interface OnInitListener {
71        public void onInit(int status);
72    }
73
74    /**
75     * Called when the TTS has finished speaking by itself (speaking
76     * finished without being canceled).
77     *
78     */
79    public interface OnSpeechCompletedListener {
80        public void onSpeechCompleted();
81    }
82
83    /**
84     * Internal constants for the TTS functionality
85     *
86     * {@hide}
87     */
88    public class Engine {
89        // default values for a TTS engine when settings are not found in the provider
90        public static final int FALLBACK_TTS_DEFAULT_RATE = 100; // 1x
91        public static final int FALLBACK_TTS_DEFAULT_PITCH = 100;// 1x
92        public static final int FALLBACK_TTS_USE_DEFAULTS = 0; // false
93        public static final String FALLBACK_TTS_DEFAULT_LANG = "eng";
94        public static final String FALLBACK_TTS_DEFAULT_COUNTRY = "";
95        public static final String FALLBACK_TTS_DEFAULT_VARIANT = "";
96
97        // return codes for a TTS engine's check data activity
98        public static final int CHECK_VOICE_DATA_PASS = 1;
99        public static final int CHECK_VOICE_DATA_FAIL = 0;
100        public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
101        public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
102        public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
103    }
104
105    /**
106     * Connection needed for the TTS.
107     */
108    private ServiceConnection mServiceConnection;
109
110    private ITts mITts = null;
111    private Context mContext = null;
112    private OnInitListener mInitListener = null;
113    private boolean mStarted = false;
114    private final Object mStartLock = new Object();
115    private ITtsCallback mITtsCallback;
116    private OnSpeechCompletedListener mSpeechCompListener = null;
117    private final Object mSpeechCompListenerLock = new Object();
118
119
120
121    /**
122     * The constructor for the TTS.
123     *
124     * @param context
125     *            The context
126     * @param listener
127     *            The InitListener that will be called when the TTS has
128     *            initialized successfully.
129     */
130    public TextToSpeech(Context context, OnInitListener listener) {
131        mContext = context;
132        mInitListener = listener;
133        initTts();
134    }
135
136
137    public void setOnSpeechCompletedListener(final OnSpeechCompletedListener listener) {
138        synchronized(mSpeechCompListenerLock) {
139            mSpeechCompListener = listener;
140        }
141    }
142
143
144    private boolean dataFilesCheck() {
145        // TODO #TTS# config manager will be in settings
146        Log.i("TTS_FIXME", "FIXME in Tts: config manager will be in settings");
147        // TODO #TTS# implement checking of the correct installation of
148        //             the data files.
149
150        return true;
151    }
152
153
154    private void initTts() {
155        mStarted = false;
156
157        // Initialize the TTS, run the callback after the binding is successful
158        mServiceConnection = new ServiceConnection() {
159            public void onServiceConnected(ComponentName name, IBinder service) {
160                synchronized(mStartLock) {
161                    mITts = ITts.Stub.asInterface(service);
162                    try {
163                        mITtsCallback = new ITtsCallback.Stub() {
164                            public void markReached(String mark)
165                            throws RemoteException {
166                                // call the listener of that event, but not
167                                // while locked.
168                                OnSpeechCompletedListener listener = null;
169                                synchronized(mSpeechCompListenerLock) {
170                                    listener = mSpeechCompListener;
171                                }
172                                if (listener != null) {
173                                    listener.onSpeechCompleted();
174                                }
175                            }
176                        };
177                        mITts.registerCallback(mITtsCallback);
178
179                    } catch (RemoteException e) {
180                        initTts();
181                        return;
182                    }
183
184                    mStarted = true;
185                    // The callback can become null if the Android OS decides to
186                    // restart the TTS process as well as whatever is using it.
187                    // In such cases, do nothing - the error handling from the
188                    // speaking calls will kick in and force a proper restart of
189                    // the TTS.
190                    if (mInitListener != null) {
191                        // TODO manage failures and missing resources
192                        mInitListener.onInit(TTS_SUCCESS);
193                    }
194                }
195            }
196
197            public void onServiceDisconnected(ComponentName name) {
198                synchronized(mStartLock) {
199                    mITts = null;
200                    mInitListener = null;
201                    mStarted = false;
202                }
203            }
204        };
205
206        Intent intent = new Intent("android.intent.action.USE_TTS");
207        intent.addCategory("android.intent.category.TTS");
208        mContext.bindService(intent, mServiceConnection,
209                Context.BIND_AUTO_CREATE);
210        // TODO handle case where the binding works (should always work) but
211        //      the plugin fails
212    }
213
214
215    /**
216     * Shuts down the TTS. It is good practice to call this in the onDestroy
217     * method of the Activity that is using the TTS so that the TTS is stopped
218     * cleanly.
219     */
220    public void shutdown() {
221        try {
222            mContext.unbindService(mServiceConnection);
223        } catch (IllegalArgumentException e) {
224            // Do nothing and fail silently since an error here indicates that
225            // binding never succeeded in the first place.
226        }
227    }
228
229
230    /**
231     * Adds a mapping between a string of text and a sound resource in a
232     * package.
233     *
234     * @see #TTS.speak(String text, int queueMode, String[] params)
235     *
236     * @param text
237     *            Example: <b><code>"south_south_east"</code></b><br/>
238     *
239     * @param packagename
240     *            Pass the packagename of the application that contains the
241     *            resource. If the resource is in your own application (this is
242     *            the most common case), then put the packagename of your
243     *            application here.<br/>
244     *            Example: <b>"com.google.marvin.compass"</b><br/>
245     *            The packagename can be found in the AndroidManifest.xml of
246     *            your application.
247     *            <p>
248     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
249     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
250     *            </p>
251     *
252     * @param resourceId
253     *            Example: <b><code>R.raw.south_south_east</code></b>
254     */
255    public void addSpeech(String text, String packagename, int resourceId) {
256        synchronized(mStartLock) {
257            if (!mStarted) {
258                return;
259            }
260            try {
261                mITts.addSpeech(text, packagename, resourceId);
262            } catch (RemoteException e) {
263                // TTS died; restart it.
264                mStarted = false;
265                initTts();
266            } catch (NullPointerException e) {
267                // TTS died; restart it.
268                mStarted = false;
269                initTts();
270            } catch (IllegalStateException e) {
271                // TTS died; restart it.
272                mStarted = false;
273                initTts();
274            }
275        }
276    }
277
278
279    /**
280     * Adds a mapping between a string of text and a sound file. Using this, it
281     * is possible to add custom pronounciations for text.
282     *
283     * @param text
284     *            The string of text
285     * @param filename
286     *            The full path to the sound file (for example:
287     *            "/sdcard/mysounds/hello.wav")
288     */
289    public void addSpeech(String text, String filename) {
290        synchronized (mStartLock) {
291            if (!mStarted) {
292                return;
293            }
294            try {
295                mITts.addSpeechFile(text, filename);
296            } catch (RemoteException e) {
297                // TTS died; restart it.
298                mStarted = false;
299                initTts();
300            } catch (NullPointerException e) {
301                // TTS died; restart it.
302                mStarted = false;
303                initTts();
304            } catch (IllegalStateException e) {
305                // TTS died; restart it.
306                mStarted = false;
307                initTts();
308            }
309        }
310    }
311
312
313    /**
314     * Speaks the string using the specified queuing strategy and speech
315     * parameters. Note that the speech parameters are not universally supported
316     * by all engines and will be treated as a hint. The TTS library will try to
317     * fulfill these parameters as much as possible, but there is no guarantee
318     * that the voice used will have the properties specified.
319     *
320     * @param text
321     *            The string of text to be spoken.
322     * @param queueMode
323     *            The queuing strategy to use.
324     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
325     * @param params
326     *            The hashmap of speech parameters to be used.
327     */
328    public void speak(String text, int queueMode, HashMap<String,String> params)
329    {
330        synchronized (mStartLock) {
331            Log.i("TTS received: ", text);
332            if (!mStarted) {
333                return;
334            }
335            try {
336                // TODO support extra parameters, passing null for the moment
337                mITts.speak(text, queueMode, null);
338            } catch (RemoteException e) {
339                // TTS died; restart it.
340                mStarted = false;
341                initTts();
342            } catch (NullPointerException e) {
343                // TTS died; restart it.
344                mStarted = false;
345                initTts();
346            } catch (IllegalStateException e) {
347                // TTS died; restart it.
348                mStarted = false;
349                initTts();
350            }
351        }
352    }
353
354
355    /**
356     * Plays the earcon using the specified queueing mode and parameters.
357     *
358     * @param earcon
359     *            The earcon that should be played
360     * @param queueMode
361     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
362     * @param params
363     *            The hashmap of parameters to be used.
364     */
365    public void playEarcon(String earcon, int queueMode,
366            HashMap<String,String> params) {
367        synchronized (mStartLock) {
368            if (!mStarted) {
369                return;
370            }
371            try {
372                // TODO support extra parameters, passing null for the moment
373                mITts.playEarcon(earcon, queueMode, null);
374            } catch (RemoteException e) {
375                // TTS died; restart it.
376                mStarted = false;
377                initTts();
378            } catch (NullPointerException e) {
379                // TTS died; restart it.
380                mStarted = false;
381                initTts();
382            } catch (IllegalStateException e) {
383                // TTS died; restart it.
384                mStarted = false;
385                initTts();
386            }
387        }
388    }
389
390
391    public void playSilence(long durationInMs, int queueMode) {
392        // TODO implement, already present in TTS service
393    }
394
395
396    /**
397     * Returns whether or not the TTS is busy speaking.
398     *
399     * @return Whether or not the TTS is busy speaking.
400     */
401    public boolean isSpeaking() {
402        synchronized (mStartLock) {
403            if (!mStarted) {
404                return false;
405            }
406            try {
407                return mITts.isSpeaking();
408            } catch (RemoteException e) {
409                // TTS died; restart it.
410                mStarted = false;
411                initTts();
412            } catch (NullPointerException e) {
413                // TTS died; restart it.
414                mStarted = false;
415                initTts();
416            } catch (IllegalStateException e) {
417                // TTS died; restart it.
418                mStarted = false;
419                initTts();
420            }
421            return false;
422        }
423    }
424
425
426    /**
427     * Stops speech from the TTS.
428     */
429    public void stop() {
430        synchronized (mStartLock) {
431            if (!mStarted) {
432                return;
433            }
434            try {
435                mITts.stop();
436            } catch (RemoteException e) {
437                // TTS died; restart it.
438                mStarted = false;
439                initTts();
440            } catch (NullPointerException e) {
441                // TTS died; restart it.
442                mStarted = false;
443                initTts();
444            } catch (IllegalStateException e) {
445                // TTS died; restart it.
446                mStarted = false;
447                initTts();
448            }
449        }
450    }
451
452
453    /**
454     * Sets the speech rate for the TTS engine.
455     *
456     * Note that the speech rate is not universally supported by all engines and
457     * will be treated as a hint. The TTS library will try to use the specified
458     * speech rate, but there is no guarantee.
459     * This has no effect on any pre-recorded speech.
460     *
461     * @param speechRate
462     *            The speech rate for the TTS engine. 1 is the normal speed,
463     *            lower values slow down the speech (0.5 is half the normal speech rate),
464     *            greater values accelerate it (2 is twice the normal speech rate).
465     */
466    public void setSpeechRate(float speechRate) {
467        synchronized (mStartLock) {
468            if (!mStarted) {
469                return;
470            }
471            try {
472                if (speechRate > 0) {
473                    mITts.setSpeechRate((int)(speechRate*100));
474                }
475            } catch (RemoteException e) {
476                // TTS died; restart it.
477                mStarted = false;
478                initTts();
479            }
480        }
481    }
482
483
484    /**
485     * Sets the speech pitch for the TTS engine.
486     *
487     * Note that the pitch is not universally supported by all engines and
488     * will be treated as a hint. The TTS library will try to use the specified
489     * pitch, but there is no guarantee.
490     * This has no effect on any pre-recorded speech.
491     *
492     * @param pitch
493     *            The pitch for the TTS engine. 1 is the normal pitch,
494     *            lower values lower the tone of the synthesized voice,
495     *            greater values increase it.
496     */
497    public void setPitch(float pitch) {
498        synchronized (mStartLock) {
499            if (!mStarted) {
500                return;
501            }
502            try {
503                if (pitch > 0) {
504                    mITts.setPitch((int)(pitch*100));
505                }
506            } catch (RemoteException e) {
507                // TTS died; restart it.
508                mStarted = false;
509                initTts();
510            }
511        }
512    }
513
514
515    /**
516     * Sets the language for the TTS engine.
517     *
518     * Note that the language is not universally supported by all engines and
519     * will be treated as a hint. The TTS library will try to use the specified
520     * language as represented by the Locale, but there is no guarantee.
521     *
522     * @param loc
523     *            The locale describing the language to be used.
524     */
525    public void setLanguage(Locale loc) {
526        synchronized (mStartLock) {
527            if (!mStarted) {
528                return;
529            }
530            try {
531                mITts.setLanguage(loc.getISO3Language(), loc.getISO3Country(), loc.getVariant());
532            } catch (RemoteException e) {
533                // TTS died; restart it.
534                mStarted = false;
535                initTts();
536            }
537        }
538    }
539
540
541    /**
542     * Speaks the given text using the specified queueing mode and parameters.
543     *
544     * @param text
545     *            The String of text that should be synthesized
546     * @param params
547     *            A hashmap of parameters.
548     * @param filename
549     *            The string that gives the full output filename; it should be
550     *            something like "/sdcard/myappsounds/mysound.wav".
551     * @return A boolean that indicates if the synthesis succeeded
552     */
553    public boolean synthesizeToFile(String text, HashMap<String,String> params,
554            String filename) {
555        synchronized (mStartLock) {
556            if (!mStarted) {
557                return false;
558            }
559            try {
560                // TODO support extra parameters, passing null for the moment
561                return mITts.synthesizeToFile(text, null, filename);
562            } catch (RemoteException e) {
563                // TTS died; restart it.
564                mStarted = false;
565                initTts();
566            } catch (NullPointerException e) {
567                // TTS died; restart it.
568                mStarted = false;
569                initTts();
570            } catch (IllegalStateException e) {
571                // TTS died; restart it.
572                mStarted = false;
573                initTts();
574            }
575            return false;
576        }
577    }
578
579
580}
581