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