Android TTS fails to speak large amount of text

2019-02-09 23:06发布

I am trying to speak out large amount of text using Android Text To Speech. I using default Google speech engine. Below is my code.

 public class Talk extends Activity implements TextToSpeech.OnInitListener {

        private ImageView playBtn;

        private EditText textField;

        private TextToSpeech tts;
        private boolean isSpeaking = false;

        private String finalText;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_talk);

            //Intialize the instance variables
            playBtn = (ImageView)findViewById(R.id.playBtn);

            textField = (EditText)findViewById(R.id.textField);


            //Resister the listeners
            playBtn.setOnClickListener(new PlayBtnAction());

            //Other things
            tts = new TextToSpeech(this,this);

            //Get the web page text if called from Share-Via
            if (Intent.ACTION_SEND.equals(getIntent().getAction())) 
            {

                   new GetWebText().execute("");

            }
        }


        //This class will execute the text from web pages
        private class GetWebText extends AsyncTask<String,Void,String>
        {

            @Override
            protected String doInBackground(String... params) {
                // TODO Auto-generated method stub
                String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
                String websiteText = "";


                 try {
                    //Create a URL for the desired page
                    URL url = new URL(text);
                    // Read all the text returned by the server
                    BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
                    String str;
                    StringBuffer strBuffer = new StringBuffer("");

                    while ((str = in.readLine()) != null) 
                    {
                        strBuffer.append(str+"\n"+"\n");
                    }


                    in.close();

                    String html = strBuffer.toString();

                    Document doc = Jsoup.parse(html); 
                    websiteText = doc.body().text(); // "An example link"
                    //Toast.makeText(this, websiteText, Toast.LENGTH_LONG).show();

                 }
                 catch(Exception e)
                 {
                     Log.e("web_error", "Error in getting web text",e);
                 }
                return websiteText;
            }

            @Override
            protected void onPostExecute(String result)
            {
                textField.setText(result);
            }

        }

        }

        //Class to speak the text
            private class PlayBtnAction implements OnClickListener
            {


                @Override
                public void onClick(View v) 
                {

                    // TODO Auto-generated method stub
                    if(!isSpeaking)
                    {
                        isSpeaking = true;
                        //speak(textField.getText().toString());
                        finalText = textField.getText().toString();
                        new SpeakTheText().execute(finalText);
                        isSpeaking = false;
                    }
                    else
                    {
                        isSpeaking = false;
                        tts.stop();
                    }



                }

            }



        @Override
        public void onInit(int status) {
            // TODO Auto-generated method stub
            if(status==TextToSpeech.SUCCESS)
            {
                int result = tts.setLanguage(Locale.UK);

                if(result==TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED)
                {
                    Toast.makeText(this, "Language Not Supported", Toast.LENGTH_LONG).show();
                }
            }

        }


        //This class will speak the text
        private class SpeakTheText extends AsyncTask<String,Void,String>
        {

            @Override
            protected String doInBackground(String... params) {
                // TODO Auto-generated method stub
                tts.speak(params[0], TextToSpeech.QUEUE_FLUSH, null);
                return null;
            }

        }


          @Override
            public void onDestroy()
            {
                if(tts!=null)
                {
                    tts.stop();
                    tts.shutdown();

                }
                super.onDestroy();
            }

    }

But the issue here is, when there is a large chunk of text (lets say you have extracted text from a web page) the TTS fails to read it. If I remove most of the text, then it will read it. Why is this happening?

When I am about to read the large text, the LogCat however display something like this

10-11 07:26:05.566: D/dalvikvm(2638): GC_CONCURRENT freed 362K, 44% free 3597K/6312K, paused 17ms+8ms, total 93ms

5条回答
Rolldiameter
2楼-- · 2019-02-09 23:56

The String length should not be longer than pre-defined length, from docs:

Parameters

text The string of text to be spoken. No longer than getMaxSpeechInputLength() characters.

Returned value by getMaxSpeechInputLength() may vary from device to device, but according to AOSP source that is whopping 4000:

/**
 * Limit of length of input string passed to speak and synthesizeToFile.
 *
 * @see #speak
 * @see #synthesizeToFile
 */
public static int getMaxSpeechInputLength() {
    return 4000;
}

Try not to exceed that limit: compare input text length with that value and split into separate parts if necessary.

查看更多
爷、活的狠高调
3楼-- · 2019-02-09 23:56

Use this code...Working for any file .. just send the string to speech function..

private void speech(String charSequence) {

    int position ;


    int sizeOfChar= charSequence.length();
    String testStri= charSequence.substring(position,sizeOfChar);


    int next = 20;
    int pos =0;
    while(true) {
        String temp="";
        Log.e("in loop", "" + pos);

        try {

      temp = testStri.substring(pos, next);
            HashMap<String, String> params = new HashMap<String, String>();
            params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, temp);
            engine.speak(temp, TextToSpeech.QUEUE_ADD, params);

            pos = pos + 20;
            next = next + 20;

        } catch (Exception e) {
            temp = testStri.substring(pos, testStri.length());
            engine.speak(temp, TextToSpeech.QUEUE_ADD, null);
            break;

        }

    }

}
查看更多
神经病院院长
4楼-- · 2019-02-09 23:57

In case someone might find this helpful. When you split the large text into strings, do not set the length of each string to the exact value of getMaxSpeechInputLength(). Subtract the string length by 1. Otherwise, only the last chunk of string could be read by TTS.

int length = toSpeech.getMaxSpeechInputLength() - 1;
Iterable<String> chunks = Splitter.fixedLength(length).split(largeText);
Lists.newArrayList(chunks);
查看更多
对你真心纯属浪费
5楼-- · 2019-02-10 00:02

If you follow ozbek's advice you should be fine. I too have large text files that I want spoken. I simply used the streamreader method and everything works fine. heres' PART of my code. it's the part that you should use. My code does a bit more than you want but it works for me and may work for you.

Dim sReader As StreamReader = New StreamReader(Story_file) Try

        Do Until EndOfStream '= True
            Dim line_to_speak As String = sReader.ReadLine
            Dim vc = Mid(line_to_speak, 1, 1) <- you dont need this

            Select Case vc  <- you dont need this
                Case Is = "/" <- you dont need this
                    voice_index = Val(Mid(line_to_speak, 2, 2)) <- you dont need this
                    srate = Val(Mid(line_to_speak, 5, 2)) <- you dont need this
                    edassistv.lstVoices.SelectedIndex = voice_index <- you dont need this
                    selected_voice = edassistv.lstVoices.SelectedItem <- you dont need this
                Case Else<- you dont need this
                    synth.SelectVoice(selected_voice)
                    synth.Speak(line_to_speak)
            End Select<- you dont need this

        Loop
    Catch ex As Exception
        GoTo finish
查看更多
何必那么认真
6楼-- · 2019-02-10 00:08

It is worse than the 4000 characters limit in practice on Android. There are some TTS engines that limit the input length a lot more. For example Nuance.tts and vocalizer.tts engines won't speak any string longer than about 512 characters (from my tests some time ago). Today I hit a limit of below 300 characters in es.codefactory.eloquencetts package, which simply crashes if the string I send to it is more than 256-300 characters. I divide the contents into sentences, and guard for sentences longer than the above limit, further sub-dividing them in my app code.

Greg

查看更多
登录 后发表回答