TextToSpeech.java revision 87c9684fd0fa31fd6ad7f7e9f4cfedddc4fdc4b0
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        // TODO implement, already present in TTS service
414    }
415
416
417    /**
418     * Returns whether or not the TTS is busy speaking.
419     *
420     * @return Whether or not the TTS is busy speaking.
421     */
422    public boolean isSpeaking() {
423        synchronized (mStartLock) {
424            if (!mStarted) {
425                return false;
426            }
427            try {
428                return mITts.isSpeaking();
429            } catch (RemoteException e) {
430                // TTS died; restart it.
431                mStarted = false;
432                initTts();
433            } catch (NullPointerException e) {
434                // TTS died; restart it.
435                mStarted = false;
436                initTts();
437            } catch (IllegalStateException e) {
438                // TTS died; restart it.
439                mStarted = false;
440                initTts();
441            }
442            return false;
443        }
444    }
445
446
447    /**
448     * Stops speech from the TTS.
449     */
450    public void stop() {
451        synchronized (mStartLock) {
452            if (!mStarted) {
453                return;
454            }
455            try {
456                mITts.stop();
457            } catch (RemoteException e) {
458                // TTS died; restart it.
459                mStarted = false;
460                initTts();
461            } catch (NullPointerException e) {
462                // TTS died; restart it.
463                mStarted = false;
464                initTts();
465            } catch (IllegalStateException e) {
466                // TTS died; restart it.
467                mStarted = false;
468                initTts();
469            }
470        }
471    }
472
473
474    /**
475     * Sets the speech rate for the TTS engine.
476     *
477     * Note that the speech rate is not universally supported by all engines and
478     * will be treated as a hint. The TTS library will try to use the specified
479     * speech rate, but there is no guarantee.
480     * This has no effect on any pre-recorded speech.
481     *
482     * @param speechRate
483     *            The speech rate for the TTS engine. 1 is the normal speed,
484     *            lower values slow down the speech (0.5 is half the normal speech rate),
485     *            greater values accelerate it (2 is twice the normal speech rate).
486     */
487    public void setSpeechRate(float speechRate) {
488        synchronized (mStartLock) {
489            if (!mStarted) {
490                return;
491            }
492            try {
493                if (speechRate > 0) {
494                    mCachedRate = (int)(speechRate*100);
495                    updateCachedParamArray();
496                    mITts.setSpeechRate(mCachedRate);
497                }
498            } catch (RemoteException e) {
499                // TTS died; restart it.
500                mStarted = false;
501                initTts();
502            }
503        }
504    }
505
506
507    /**
508     * Sets the speech pitch for the TTS engine.
509     *
510     * Note that the pitch is not universally supported by all engines and
511     * will be treated as a hint. The TTS library will try to use the specified
512     * pitch, but there is no guarantee.
513     * This has no effect on any pre-recorded speech.
514     *
515     * @param pitch
516     *            The pitch for the TTS engine. 1 is the normal pitch,
517     *            lower values lower the tone of the synthesized voice,
518     *            greater values increase it.
519     */
520    public void setPitch(float pitch) {
521        synchronized (mStartLock) {
522            if (!mStarted) {
523                return;
524            }
525            try {
526                if (pitch > 0) {
527                    mITts.setPitch((int)(pitch*100));
528                }
529            } catch (RemoteException e) {
530                // TTS died; restart it.
531                mStarted = false;
532                initTts();
533            }
534        }
535    }
536
537
538    /**
539     * Sets the language for the TTS engine.
540     *
541     * Note that the language is not universally supported by all engines and
542     * will be treated as a hint. The TTS library will try to use the specified
543     * language as represented by the Locale, but there is no guarantee.
544     *
545     * @param loc
546     *            The locale describing the language to be used.
547     */
548    public void setLanguage(Locale loc) {
549        synchronized (mStartLock) {
550            if (!mStarted) {
551                return;
552            }
553            try {
554                mCachedLang = loc.getISO3Language();
555                mCachedCountry = loc.getISO3Country();
556                mCachedVariant = loc.getVariant();
557                updateCachedParamArray();
558                mITts.setLanguage(mCachedLang, mCachedCountry, mCachedVariant);
559            } catch (RemoteException e) {
560                // TTS died; restart it.
561                mStarted = false;
562                initTts();
563            }
564        }
565    }
566
567    /**
568     * Checks if the specified language as represented by the locale is available.
569     *
570     * @param loc
571     *            The locale describing the language to be used.
572     * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
573               TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
574     */
575    public int isLanguageAvailable(Locale loc) {
576        //TODO: Implement isLanguageAvailable
577        return TTS_LANG_NOT_SUPPORTED;
578    }
579
580
581
582    /**
583     * Speaks the given text using the specified queueing mode and parameters.
584     *
585     * @param text
586     *            The String of text that should be synthesized
587     * @param params
588     *            A hashmap of parameters.
589     * @param filename
590     *            The string that gives the full output filename; it should be
591     *            something like "/sdcard/myappsounds/mysound.wav".
592     * @return A boolean that indicates if the synthesis succeeded
593     */
594    public boolean synthesizeToFile(String text, HashMap<String,String> params,
595            String filename) {
596        synchronized (mStartLock) {
597            if (!mStarted) {
598                return false;
599            }
600            try {
601                // TODO support extra parameters, passing null for the moment
602                return mITts.synthesizeToFile(text, null, filename);
603            } catch (RemoteException e) {
604                // TTS died; restart it.
605                mStarted = false;
606                initTts();
607            } catch (NullPointerException e) {
608                // TTS died; restart it.
609                mStarted = false;
610                initTts();
611            } catch (IllegalStateException e) {
612                // TTS died; restart it.
613                mStarted = false;
614                initTts();
615            }
616            return false;
617        }
618    }
619
620
621}
622