Previous  | Next  | Home

Speak to Me


 

A very useful feature of the Android API (available in Android 1.6 and later) is a rich and easy to use set of text to speech (TTS) resources that allow conversion of text strings to spoken voice. These capabilites are implemented primarily through the TextToSpeech class and are discussed in the Using Text-To-Speech Android document. By employing methods and class constants of the Locale class, it is generally possible to customize both the language and dialect of the spoken voice (subject to availability of language data on the device). Since the device can be self-aware of position if it has access to Location services, it is even possible to automatically switch the default language to correspond to the present location of the device. In this project we give a concise introduction to the basics of text to speech conversion.

 

Creating the Project

Open Eclipse and create a project with the following specifications

where we note that the minimum SDK version has been set to 4 (corresponding to Android 1.6). Now we modify this skeleton project to accomplish the following.

  1. Query the device to see whether it has adequate language data installed. If it doesn't, send the device to try to get the required data. If it does have the language data, launch a new instance of TextToSpeech.

  2. Put an editable text field on the screen to accept input text strings, a Speak button to initiate text to speech conversion on the input string, and a Clear button to clear the field, but disable input until the text to speech (TTS) instance is initialized.

  3. Once the TextToSpeech instance is initialized, enable the input text fields and buttons, set language and properties of the TTS instance, and speak introductory instructions.

  4. Use the methods of the TextToSpeech class to speak input text strings entered by the user when the Speak button is pressed.

 

Strings and Layout

We begin by defining the main layout and some strings that we will need. Open res/values/strings.xml and edit it to read


    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="hello">SpeakToMe</string>
        <string name="app_name">Speak To Me</string>
        <string name="goLabel">Speak</string>
        <string name="clearLabel">Clear Text</string>
    </resources>

Then open res/layout/main.xml and implement the layout for an EditText field and two Buttons:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <EditText
        android:id="@+id/speak_input"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"

        android:hint="(Enter text)"
        android:inputType="text" />
	 
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button 
            android:id="@+id/speak_button"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:text="@string/goLabel" />
        <Button 
            android:id="@+id/clear_button"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:text="@string/clearLabel" />
    </LinearLayout>
	 
</LinearLayout>

Now we implement the Java code that will allow text entered in the text field to be converted to speech when the Speak button is pressed.

 

Implementing Text to Speech

Open the file SpeakToMe.java and edit it to read


package com.lightcone.speaktome;

import java.util.Locale;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;

// Class to test text-to-speech capabilities

public class SpeakToMe extends Activity implements OnInitListener, OnClickListener {
	
    private static final int CHECK_DATA = 0;
    private static final Locale defaultLocale = Locale.UK;   // Set language to British English
    private static final String logLabel = "TTS ++++++++++++++++++++";
    private TextToSpeech tts;
    private boolean isInit = false;
    private View speakButton;
    private View clearButton;
    private EditText speakText;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // Identify buttons and add click listeners
        speakButton = findViewById(R.id.speak_button);
        speakButton.setOnClickListener(this);
        clearButton = findViewById(R.id.clear_button);
        clearButton.setOnClickListener(this);
        
        // Identify EditText field
        speakText = (EditText) findViewById(R.id.speak_input);
        
        // Disable text field and speak button until text to speech initialization is done
        // (See method onInit() below)
        
        speakButton.setEnabled(false);
        speakText.setEnabled(false);
        
        // Use an Intent and startActivityForResult to check whether TTS data installed on the
        // device. Result returned and acted on in method onActivityResult(int, int, Intent) below.
        
        Intent checkIntent = new Intent();
        checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
        startActivityForResult(checkIntent, CHECK_DATA);
    }
    

    // Create the TTS instance if TextToSpeech language data are installed on device.  If not
    // installed, attempt to install it on the device.
    
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CHECK_DATA) {
            if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
            	
                // Success, so create the TTS instance.  But can't use it to speak until
            	// the onInit(status) callback defined below runs, indicating initialization.
            	
            	Log.i(logLabel, "Success, let's talk");
                tts = new TextToSpeech(this,  this);
                
                // Use static Locales method to list available locales on device
    	        Locale locales [] = Locale.getAvailableLocales();
    	        Log.i(logLabel,"Locales Available on Device:");
    	        for(int i=0; i<locales.length; i++){
                    String temp = "Locale "+i+": "+locales[i]+" Language="
                        +locales[i].getDisplayLanguage();
                    if(locales[i].getDisplayCountry() != "") {
                        temp += " Country="+locales[i].getDisplayCountry();
                    }
                    Log.i(logLabel, temp);
    	        }
    	        
            } else {
                // missing data, so install it on the device
            	Log.i(logLabel, "Missing Data; Install it");
                Intent installIntent = new Intent();
                installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
                startActivity(installIntent);
            }
        }
    }
    
    // Must wait for initialization before any speech can be synthesized.  The class
    // implements OnInitListener and the following callback has been overridden to
    // implement actions that will be executed once initialized.
    
    @Override
    public void onInit(int status) {
        if(status == TextToSpeech.SUCCESS){
            isInit = true;

            // Enable input text field and speak button now that we are initialized
            speakButton.setEnabled(true);
            speakText.setEnabled(true);
            
            // Set to a language locale after checking availability
            Log.i(logLabel, "available="+tts.isLanguageAvailable(Locale.UK));
            tts.setLanguage(defaultLocale);
            // Examples of voice controls.  Set to defaults of 1.0.
            tts.setPitch(1.0F);
            tts.setSpeechRate(1.0F);
            // Issue a greeting and instructions in the default language
            speakGreeting(defaultLocale.getDisplayLanguage());
        } else {
            isInit = false;
            Log.i(logLabel, "Failure: TextToSpeech instance tts was not properly initialized");
        }
    }
    
    // Method to issue greeting and instructions
    
    public void speakGreeting(String currentLanguage){
        String text1 = "Let's test text to speech in "+currentLanguage+". ";
        text1 += "Enter some "+currentLanguage+" text and press the speak button.";
        sayIt(text1, true);
    }
    
    // Implement text to speech for an arbitrary string entered in the EditText field
    // for the Speak button and text clear for the Clear button.
	
    @Override
    public void onClick(View v) {
    	switch(v.getId()){
            case R.id.speak_button:
                String speakString = speakText.getText().toString();
                sayIt(speakString,true);
                break;
            case R.id.clear_button:
                speakText.setText("");
                break;
    	}
    }
	
    // Method to speak a string.  The boolean flushQ determines whether the text is
    // appended to the queue (if false), or if the queue is flushed first (if true).
    
    public void sayIt(String text, boolean flushQ){
        if(isInit){
            if(flushQ){
                tts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
            } else {
                tts.speak(text, TextToSpeech.QUEUE_ADD, null);
            }
        } else {
            Log.i(logLabel, "Failure: TextToSpeech instance tts was not properly initialized");
        }
    }
	
    // Release TTS resources when finished
    @Override
    protected void onDestroy(){
    	super.onDestroy();
    	Log.i(logLabel,"Destroy");
    	tts.shutdown();
    }
}

If you now execute in a emulator or on a phone with TTS capability, you should see the following display





and the TTS syntesizer should speak brief instructions. If you enter some text and click Speak, the synthesizer should voice the text that you have entered.

 

How It Works

Let's summarize how SpeakToMe.java implements this functionality.


The onDestroy() method is generally used to perform any final cleanup before an activity is destroyed, either because someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. onDestroy() is not guaranteed to be called (the system may kill the activity's process directly without calling this method), so it is not a good place to save data. However, it is a good place to free resources like threads or TTS engines associated with an activity, so that a destroyed activity does not leave such things about while the rest of its application is still running. Note that Android generally requires a call through to the superclass method (super.onDestroy() in this case) when a lifecycle method is overridden.

This simple exercise illustrates many of the basic features of TextToSpeech and should serve as a starting template for more ambitious applications. Two possibilities are suggested below in the Exercises. A project implementing a Spinner permitting the user to choose a language (Exercise 1) may be found at this Exercise solution.


The complete project for the application described above is archived at the link SpeakToMe.



Exercises

1. Modify this project to provide a Spinner popup menu allowing the user to select the language for the TTS voice. See the Spinner Tutorial for how to deal with Spinner widgets and use the onInit callback of the OnInitListener interface to ensure that the TTS engine is not invoked before it is initialized when the language is selected. [Solution]

2. Extend Exercise 1 by using Location services to automatically determine where the device is and choose an appropriate language for the TTS voice.


Previous  | Next  | Home