TextToSpeech.java revision f032bc7da536774a0b6a1c77632c65b935eee6fa
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    /**
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_LANG = "eng";
112        public static final String FALLBACK_TTS_DEFAULT_COUNTRY = "";
113        public static final String FALLBACK_TTS_DEFAULT_VARIANT = "";
114
115        // return codes for a TTS engine's check data activity
116        public static final int CHECK_VOICE_DATA_PASS = 1;
117        public static final int CHECK_VOICE_DATA_FAIL = 0;
118        public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
119        public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
120        public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
121
122        // keys for the parameters passed with speak commands
123        public static final String TTS_KEY_PARAM_RATE = "rate";
124        public static final String TTS_KEY_PARAM_LANGUAGE = "language";
125        public static final String TTS_KEY_PARAM_COUNTRY = "country";
126        public static final String TTS_KEY_PARAM_VARIANT = "variant";
127        public static final int TTS_PARAM_POSITION_RATE = 0;
128        public static final int TTS_PARAM_POSITION_LANGUAGE = 2;
129        public static final int TTS_PARAM_POSITION_COUNTRY = 4;
130        public static final int TTS_PARAM_POSITION_VARIANT = 6;
131    }
132
133    /**
134     * Connection needed for the TTS.
135     */
136    private ServiceConnection mServiceConnection;
137
138    private ITts mITts = null;
139    private Context mContext = null;
140    private OnInitListener mInitListener = null;
141    private boolean mStarted = false;
142    private final Object mStartLock = new Object();
143    private int mCachedRate = Engine.FALLBACK_TTS_DEFAULT_RATE;
144    private String mCachedLang = Engine.FALLBACK_TTS_DEFAULT_LANG;
145    private String mCachedCountry = Engine.FALLBACK_TTS_DEFAULT_COUNTRY;
146    private String mCachedVariant = Engine.FALLBACK_TTS_DEFAULT_VARIANT;
147    private String[] mCachedParams;
148
149    /**
150     * The constructor for the TTS.
151     *
152     * @param context
153     *            The context
154     * @param listener
155     *            The InitListener that will be called when the TTS has
156     *            initialized successfully.
157     */
158    public TextToSpeech(Context context, OnInitListener listener) {
159        mContext = context;
160        mInitListener = listener;
161
162        mCachedParams = new String[2*4]; //4 parameters, store key and value
163        mCachedParams[Engine.TTS_PARAM_POSITION_RATE] = Engine.TTS_KEY_PARAM_RATE;
164        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE] = Engine.TTS_KEY_PARAM_LANGUAGE;
165        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY] = Engine.TTS_KEY_PARAM_COUNTRY;
166        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT] = Engine.TTS_KEY_PARAM_VARIANT;
167        updateCachedParamArray();
168
169        initTts();
170    }
171
172
173    private void updateCachedParamArray() {
174        mCachedParams[Engine.TTS_PARAM_POSITION_RATE+1] = String.valueOf(mCachedRate);
175        mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE+1] = mCachedLang;
176        mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY+1] = mCachedCountry;
177        mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT+1] = mCachedVariant;
178    }
179
180
181    private void initTts() {
182        mStarted = false;
183
184        // Initialize the TTS, run the callback after the binding is successful
185        mServiceConnection = new ServiceConnection() {
186            public void onServiceConnected(ComponentName name, IBinder service) {
187                synchronized(mStartLock) {
188                    mITts = ITts.Stub.asInterface(service);
189                    mStarted = true;
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 cache of current parameters for the moment
337                mITts.speak(text, queueMode, mCachedParams);
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     * Speaks the IPA string using the specified queuing strategy and speech
357     * parameters. Note that the speech parameters are not universally supported
358     * by all engines and will be treated as a hint. The TTS library will try to
359     * fulfill these parameters as much as possible, but there is no guarantee
360     * that the voice used will have the properties specified.
361     *
362     * @param ipaText
363     *            The string of IPA text to be spoken.
364     * @param queueMode
365     *            The queuing strategy to use.
366     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
367     * @param params
368     *            The hashmap of speech parameters to be used.
369     */
370    public void speakIpa(String ipaText, int queueMode, HashMap<String,String> params)
371    {
372        //TODO: Implement speakIpa
373    }
374
375
376    /**
377     * Plays the earcon using the specified queueing mode and parameters.
378     *
379     * @param earcon
380     *            The earcon that should be played
381     * @param queueMode
382     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
383     * @param params
384     *            The hashmap of parameters to be used.
385     */
386    public void playEarcon(String earcon, int queueMode,
387            HashMap<String,String> params) {
388        synchronized (mStartLock) {
389            if (!mStarted) {
390                return;
391            }
392            try {
393                // TODO support extra parameters, passing null for the moment
394                mITts.playEarcon(earcon, queueMode, null);
395            } catch (RemoteException e) {
396                // TTS died; restart it.
397                mStarted = false;
398                initTts();
399            } catch (NullPointerException e) {
400                // TTS died; restart it.
401                mStarted = false;
402                initTts();
403            } catch (IllegalStateException e) {
404                // TTS died; restart it.
405                mStarted = false;
406                initTts();
407            }
408        }
409    }
410
411
412    public void playSilence(long durationInMs, int queueMode) {
413        synchronized (mStartLock) {
414            if (!mStarted) {
415                return;
416            }
417            try {
418                // TODO support extra parameters, passing cache of current parameters for the moment
419                mITts.playSilence(durationInMs, queueMode, mCachedParams);
420            } catch (RemoteException e) {
421                // TTS died; restart it.
422                mStarted = false;
423                initTts();
424            } catch (NullPointerException e) {
425                // TTS died; restart it.
426                mStarted = false;
427                initTts();
428            } catch (IllegalStateException e) {
429                // TTS died; restart it.
430                mStarted = false;
431                initTts();
432            }
433        }
434    }
435
436
437    /**
438     * Returns whether or not the TTS is busy speaking.
439     *
440     * @return Whether or not the TTS is busy speaking.
441     */
442    public boolean isSpeaking() {
443        synchronized (mStartLock) {
444            if (!mStarted) {
445                return false;
446            }
447            try {
448                return mITts.isSpeaking();
449            } catch (RemoteException e) {
450                // TTS died; restart it.
451                mStarted = false;
452                initTts();
453            } catch (NullPointerException e) {
454                // TTS died; restart it.
455                mStarted = false;
456                initTts();
457            } catch (IllegalStateException e) {
458                // TTS died; restart it.
459                mStarted = false;
460                initTts();
461            }
462            return false;
463        }
464    }
465
466
467    /**
468     * Stops speech from the TTS.
469     */
470    public void stop() {
471        synchronized (mStartLock) {
472            if (!mStarted) {
473                return;
474            }
475            try {
476                mITts.stop();
477            } catch (RemoteException e) {
478                // TTS died; restart it.
479                mStarted = false;
480                initTts();
481            } catch (NullPointerException e) {
482                // TTS died; restart it.
483                mStarted = false;
484                initTts();
485            } catch (IllegalStateException e) {
486                // TTS died; restart it.
487                mStarted = false;
488                initTts();
489            }
490        }
491    }
492
493
494    /**
495     * Sets the speech rate for the TTS engine.
496     *
497     * Note that the speech rate is not universally supported by all engines and
498     * will be treated as a hint. The TTS library will try to use the specified
499     * speech rate, but there is no guarantee.
500     * This has no effect on any pre-recorded speech.
501     *
502     * @param speechRate
503     *            The speech rate for the TTS engine. 1 is the normal speed,
504     *            lower values slow down the speech (0.5 is half the normal speech rate),
505     *            greater values accelerate it (2 is twice the normal speech rate).
506     */
507    public void setSpeechRate(float speechRate) {
508        synchronized (mStartLock) {
509            if (!mStarted) {
510                return;
511            }
512            try {
513                if (speechRate > 0) {
514                    mCachedRate = (int)(speechRate*100);
515                    updateCachedParamArray();
516                    mITts.setSpeechRate(mCachedRate);
517                }
518            } catch (RemoteException e) {
519                // TTS died; restart it.
520                mStarted = false;
521                initTts();
522            }
523        }
524    }
525
526
527    /**
528     * Sets the speech pitch for the TTS engine.
529     *
530     * Note that the pitch is not universally supported by all engines and
531     * will be treated as a hint. The TTS library will try to use the specified
532     * pitch, but there is no guarantee.
533     * This has no effect on any pre-recorded speech.
534     *
535     * @param pitch
536     *            The pitch for the TTS engine. 1 is the normal pitch,
537     *            lower values lower the tone of the synthesized voice,
538     *            greater values increase it.
539     */
540    public void setPitch(float pitch) {
541        synchronized (mStartLock) {
542            if (!mStarted) {
543                return;
544            }
545            try {
546                if (pitch > 0) {
547                    mITts.setPitch((int)(pitch*100));
548                }
549            } catch (RemoteException e) {
550                // TTS died; restart it.
551                mStarted = false;
552                initTts();
553            }
554        }
555    }
556
557
558    /**
559     * Sets the language for the TTS engine.
560     *
561     * Note that the language is not universally supported by all engines and
562     * will be treated as a hint. The TTS library will try to use the specified
563     * language as represented by the Locale, but there is no guarantee.
564     *
565     * @param loc
566     *            The locale describing the language to be used.
567     */
568    public void setLanguage(Locale loc) {
569        synchronized (mStartLock) {
570            if (!mStarted) {
571                return;
572            }
573            try {
574                mCachedLang = loc.getISO3Language();
575                mCachedCountry = loc.getISO3Country();
576                mCachedVariant = loc.getVariant();
577                updateCachedParamArray();
578                mITts.setLanguage(mCachedLang, mCachedCountry, mCachedVariant);
579            } catch (RemoteException e) {
580                // TTS died; restart it.
581                mStarted = false;
582                initTts();
583            }
584        }
585    }
586
587    /**
588     * Checks if the specified language as represented by the locale is available.
589     *
590     * @param loc
591     *            The locale describing the language to be used.
592     * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
593               TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
594     */
595    public int isLanguageAvailable(Locale loc) {
596        //TODO: Implement isLanguageAvailable
597        return TTS_LANG_NOT_SUPPORTED;
598    }
599
600
601
602    /**
603     * Speaks the given text using the specified queueing mode and parameters.
604     *
605     * @param text
606     *            The String of text that should be synthesized
607     * @param params
608     *            A hashmap of parameters.
609     * @param filename
610     *            The string that gives the full output filename; it should be
611     *            something like "/sdcard/myappsounds/mysound.wav".
612     * @return A boolean that indicates if the synthesis succeeded
613     */
614    public boolean synthesizeToFile(String text, HashMap<String,String> params,
615            String filename) {
616        synchronized (mStartLock) {
617            if (!mStarted) {
618                return false;
619            }
620            try {
621                // TODO support extra parameters, passing null for the moment
622                return mITts.synthesizeToFile(text, null, filename);
623            } catch (RemoteException e) {
624                // TTS died; restart it.
625                mStarted = false;
626                initTts();
627            } catch (NullPointerException e) {
628                // TTS died; restart it.
629                mStarted = false;
630                initTts();
631            } catch (IllegalStateException e) {
632                // TTS died; restart it.
633                mStarted = false;
634                initTts();
635            }
636            return false;
637        }
638    }
639
640
641}
642