Text-to-Speech Unity Android Plugin

This post consists of the steps I had taken in the past to implement TTS functionality in my Unity Android Game.

 

We first need to create a Unity android plugin using android studio, which consists of an android library file.

 

This link here tells you how to do so.

As this was a project I worked on in the past, I don’t have a step by step sequence of how I managed to implement the TTS functionalities. I do plan on doing so for other projects in the future.

 

The code I used will be provided as a link as well as some troubleshooting steps I’ve done to get the code working.

 

You can find an example implementation of the project in this Github Repository.

   

Code for Java Library File


package com.java.ttslibrary;

import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.app.Activity;

import java.util.HashMap;
import java.util.Locale;
import java.util.Set;

import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.media.AudioManager;
import java.util.Locale;

public class TTSLib {
   static TextToSpeech tts;
   static Locale _locale = Locale.JAPANESE;
   static String packagetouse = "com.google.android.tts";

   static Bundle bundle = null;
   static HashMap<String, String> param;
   static Locale.Builder builder = new Locale.Builder();

   public static boolean IsRunning(){
       return (tts!=null);
   }

   public static void SetPitch(float pitch){
       if(tts!=null){
           tts.setPitch(pitch);
       }
   }

   public static void SetSpeedRate(float speechrate){
       if(tts!=null){
           tts.setSpeechRate(speechrate);
       }
   }

   public static boolean SetLocale(String locale, String script, String region){
       Locale locale_ = builder.setLanguage(locale).setScript(script).setRegion(region).build();
       if(tts==null){
           return false;
       }
       int result = tts.setLanguage(locale_);
       if (result == TextToSpeech.LANG_MISSING_DATA|| result == TextToSpeech.LANG_NOT_SUPPORTED) {
           return false;
       }
       _locale = locale_;
       return true;
   }

   public static String GetLocale(){
       return (tts!=null) ? tts.getLanguage().toString() : "";
   }

   public static String[] GetAvailableLocales(){
       Locale[] locales = Locale.getAvailableLocales();
       String[] temp = new String[locales.length];

       for(int i=0;i<locales.length;i++){
           temp[i] = locales[i].toString();
       }

       return temp;
   }

   public static boolean setEngineByPackageName(String packagename){

       if(tts!=null && tts.setEngineByPackageName(packagename)==TextToSpeech.SUCCESS){
           return true;
       }
       return false;
   }


   public static void StartTTS(final Activity activity)
   {
       activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);

       tts = new TextToSpeech(activity.getApplicationContext(), new TextToSpeech.OnInitListener() {
           @Override
           public void onInit(int status) {
               if (status == TextToSpeech.SUCCESS) {

                   int result = tts.setLanguage(Locale.JAPANESE);
                   if (result == TextToSpeech.LANG_MISSING_DATA
                           || result == TextToSpeech.LANG_NOT_SUPPORTED) {
                       Log.e("error", "Language Not supported");
                       DownloadTTSData(activity);
                   }
                   else{
                       Log.v("TTS","onInit succeeded");
                   }
               } else {
                   Log.e("error","Initialization Failed");
               }

           }
       },packagetouse);
   }

   public static void DownloadTTSData(Activity activity) {
       Intent installTTSIntent = new Intent();
       installTTSIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
       activity.startActivity(installTTSIntent);
   }

   public static boolean IsUttering() {
       if(tts!=null && tts.isSpeaking()){
           return true;
       }
       return false;
   }

   public static void StopUtterance(){
       if(tts!=null){
           tts.stop();
       }
   }

   public static boolean GetLanguageAvailability(Activity activity, String locale) {
       int result = tts.isLanguageAvailable(builder.setLanguage(locale).build());

       if(!(result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED)) {
           return true;
       }
       else {
           //DownloadTTSData(activity);
           return false;
       }
   }

   public static void Speak(String s){

       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
           if(bundle==null){
               Bundle bundle = new Bundle();
               bundle.putInt(TextToSpeech.Engine.KEY_PARAM_STREAM, AudioManager.STREAM_MUSIC);
           }
           tts.speak(s, TextToSpeech.QUEUE_FLUSH, bundle, null);
       } else {
           //Log.v(s, "Speak old API");
           if(param==null){
               param = new HashMap<>();
               param.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_MUSIC));
           }

           tts.speak(s, TextToSpeech.QUEUE_FLUSH, param);
       }
   }

   static protected void onDestroy() {
       // Don't forget to shutdown tts!
       if (tts != null) {
           Log.v("onDestroy()","onDestroy: shutdown TTS");
           tts.stop();
           tts.shutdown();
       }
   }

   public static void StopTTS() {
       onDestroy();
   }
}

 

Text-to-Speech Manager Script C#


using UnityEngine;

public static class TTSManager {

    private static AndroidJavaClass _mainactivity = null;
    private static AndroidJavaClass _libraryclass = null;

    const string _MainActivity = "com.unity3d.player.UnityPlayer";
    const string _LibraryClass = "com.java.ttslibrary.TTSLib";
    const string _MethodName = "Speak"; 
    const string _CheckAvailability = "GetLanguageAvailability"; 
    const string _getlocale = "GetLocale";
    const string _getavailablelocales = "GetAvailableLocales";
    const string _setlocale = "SetLocale";
    const string _setEngineByPackageName = "setEngineByPackageName"; 
    const string _downloadttsdata = "DownloadTTSData"; 
    const string _shutdowntts = "StopTTS"; 
    const string _uttering = "IsUttering"; 
    const string _setpitch = "SetPitch"; 
    const string _setspeed = "SetSpeedRate"; 
    const string _isbootedup = "IsRunning"; 
    const string _stopspeaking = "StopUtterance";
    const string _starttts = "StartTTS";

    public static void BootUpTTS() {
        #if UNITY_ANDROID && !UNITY_EDITOR
            _mainactivity = new AndroidJavaClass(_MainActivity);
            _libraryclass = new AndroidJavaClass(_LibraryClass);
            _libraryclass.CallStatic(_starttts, _mainactivity.GetStatic<AndroidJavaObject>("currentActivity"));
            SetSpeechRate(0.8f);
        #endif
    }

    public static void SetSpeechRate(float value) { // Sets the rate at which the TTS Engine speaks the words
        #if UNITY_ANDROID && !UNITY_EDITOR
        if (_libraryclass!=null) {
            _libraryclass.CallStatic(_setspeed, value);
        }
        #endif
    }

    public static void SetPitch(float value) { // Sets the pitch of the TTS Engine
        #if UNITY_ANDROID && !UNITY_EDITOR
        if (_libraryclass!=null) {
            _libraryclass.CallStatic(_setpitch, value);
        }
        #endif
    }

    public static bool GetLanguageAvailability(string locale) { //Java method call that checks to see whether or not the users phone has a particular language's TTS Support

        if (Application.internetReachability == NetworkReachability.NotReachable) {
            return false;
        }

        #if UNITY_ANDROID && !UNITY_EDITOR
            return (_libraryclass!=null)?_libraryclass.CallStatic<bool>(_CheckAvailability,_mainactivity.GetStatic<AndroidJavaObject>("currentActivity"), locale):false;
        #else 
            return false;
        #endif
    }

    public static void Speak(string sentence) { // A Generic method for accessing TTS engine to play words

        if (Application.internetReachability == NetworkReachability.NotReachable) {
            return;
        }

        #if UNITY_ANDROID && !UNITY_EDITOR
        if(_libraryclass==null){
            BootUpTTS();
        }
        if(_libraryclass!= null && !_libraryclass.CallStatic<bool>(_uttering)) {
            _libraryclass.CallStatic(_MethodName,sentence);
        }
        #endif
    }

    public static void DownloadTTSData() { // Creates an Intent which allows users to download TTS languages

        if (Application.internetReachability == NetworkReachability.NotReachable) {
            return;
        }
        #if UNITY_ANDROID && !UNITY_EDITOR
        if (_libraryclass!=null) {
            _libraryclass.CallStatic(_downloadttsdata, _mainactivity.GetStatic<AndroidJavaObject>("currentActivity"));
        }
        #endif
    }

    public static void StopTTS() { //Stop Running the TTS and free up resources
        #if UNITY_ANDROID && !UNITY_EDITOR
        if (_libraryclass!=null) {
            _libraryclass.CallStatic(_shutdowntts);
        }
        #endif
        _mainactivity = null;
        _libraryclass = null;
    }

    public static string[] GetAvailableLocales() { // Returns a list of all the available locales/languages as a string array
        #if UNITY_ANDROID && !UNITY_EDITOR
        return (_libraryclass!=null)?_libraryclass.CallStatic<string[]>(_getavailablelocales):null;
        #else
        return null;
        #endif
    }

    public static bool SetLocale(string locale, string script = "", string region = "") { //Refer to Documentation for more details on this method
        #if UNITY_ANDROID && !UNITY_EDITOR
        return (_libraryclass!=null) ? _libraryclass.CallStatic<bool>(_setlocale, locale,script,region) : false;
        #else
        return false;
        #endif
    }

    public static string GetLocale() {
        #if UNITY_ANDROID && !UNITY_EDITOR
        return (_libraryclass!=null) ? _libraryclass.CallStatic<string>(_getlocale) : "TTS Has not been initialized!";
        #else 
        return "";
        #endif
    }

    public static bool IsBootedUp() {
        #if UNITY_ANDROID && !UNITY_EDITOR
        return (_libraryclass!=null) ? _libraryclass.CallStatic<bool>(_isbootedup) : false;
        #else
        return false;
        #endif
    }

    public static bool IsUttering() {

        #if UNITY_ANDROID && !UNITY_EDITOR
        return (_libraryclass!=null)?_libraryclass.CallStatic<bool>(_uttering) : false;
        #else
        return false;
        #endif
    }

    public static bool SetEngineByPackageName(string packagename) { // The Default TTS Engine used is com.google.android.tts.
        #if UNITY_ANDROID && !UNITY_EDITOR
        return (_libraryclass != null) ? _libraryclass.CallStatic<bool>(_setEngineByPackageName, packagename) : false;
        #else
        return false;
        #endif
    }

    public static void StopSpeaking() { //Stops the current running utternance. Can be used to stop a sentence that has been spoken midway
        #if UNITY_ANDROID && !UNITY_EDITOR
        if(_libraryclass != null) {
            _libraryclass.CallStatic(_stopspeaking);
        }
        #endif
    }
}

Author: simplygamedev

Leave a Reply

Your email address will not be published. Required fields are marked *