preface

The company recently asked for a novel text reading feature on a radio advertising app. My first reaction was to access the voice SDK of IFlytek or other platforms, but the product said that the budget was limited, and those platforms needed to charge, and the price was not low, so I thought of other ways to achieve it.

Later, baidu Google found that android native provides TextToSpeech to process the function of TextToSpeech.

Problems with TextToSpeech:

Currently, only English, French, Italian, German and Spanish are supported. Chinese is not currently supported

test

I ran the test demo of TextToSpeech on mi mobile phone, and found that it can broadcast Chinese. After checking the system Settings of Mi mobile phone, I found that its default TTS is Xiao Ai Classmate engine.

Later, I tested huawei, Vivo and other domestic mobile phone models, and found that they can play Chinese characters normally. Since I don’t have a Google Nexus device on hand, I didn’t test it, but there’s no way to play it.

Confirm detailed requirements

Later, when determining the detailed requirements with the product, I found that his requirements were roughly that he wanted to make a novel reader player, which could drag the playing progress, have the total length, the current playing length, pause, start, play the next chapter, the previous chapter text, and time off and other functions.

Begin to implement

  • Let’s start with a text from the web that uses textToSpeech
public class MainActivity extends AppCompatActivity implements View.OnClickListener, TextToSpeech.OnInitListener { private Button speechBtn; // Button control to start speech with Private EditText; Private TextToSpeech TextToSpeech; @override public void onCreate(Bundle savedInstanceState) {super.oncreate (savedInstanceState);setContentView(R.layout.activity_main); speechBtn = (Button) findViewById(R.id.btn_read); speechBtn.setOnClickListener(this); speechTxt = (EditText) findViewById(R.id.editText); textToSpeech = new TextToSpeech(this, this); / / parameters of the Context, TextToSpeech. OnInitListener} / * * * * status: used to initialize TextToSpeech engine * SUCCESS or ERROR this two valuessetTextToSpeech.LANG_MISSING_DATA: indicates data loss for the Language. LANG_NOT_SUPPORTED: not supported */ @override public void onInit(int status) {if (status == TextToSpeech.SUCCESS) {
            int result = textToSpeech.setLanguage(Locale.CHINA);
            if (result == TextToSpeech.LANG_MISSING_DATA
                    || result == TextToSpeech.LANG_NOT_SUPPORTED) {
                Toast.makeText(this, "Data lost or unsupported", Toast.LENGTH_SHORT).show();
            }
        }
    }
    @Override
    public void onClick(View v) {
        if(textToSpeech ! = null && ! Texttospeech.isspeaking ()) {// Set the pitch, the higher the pitch, the higher the pitch will be (girls), the lower the pitch will be (boys),1.0 is the normal textToSpeech. / / set the speed, the default 1.0 speed textToSpeech. SetSpeechRate (1.5 f); // Add the added parametersinAPI Level 4 Added four parametersin API level 21
            textToSpeech.speak(speechTxt.getText().toString(), TextToSpeech.QUEUE_FLUSH, null);
        }
    }
    @Override
    protected void onStop() { super.onStop(); textToSpeech.stop(); Texttospeech. shutdown(); // Close, release resources}}Copy the code
  • The main methods are:
/** * text text that needs to be converted to voice * queueMode queueMode: * QUEUE_ADD: this content is broadcast only after the previous voice task has been played * QUEUE_FLUSH: * params Sets the TTS parameter, which can be null. * KEY_PARAM_STREAM: specifies the audio channel, including STREAM_MUSIC, STREAM_NOTIFICATION, STREAM_RING, etc. * KEY_PARAM_VOLUME: specifies the volume, 0-1f * utteranceId: Id */ textToSpeech. Speak (Content, textToSpeech. QUEUE_FLUSH, NULL, I +""); Texttospeech.stop (); // Close and release the resource textTospeech.shutdown (); Texttospeech.setpitch (0.5f); textToSpeech setPitch(0.5f); textToSpeech setPitch(0.5f); / / set the speed, the default 1.0 speed textToSpeech. SetSpeechRate (1.5 f);Copy the code
  • Call after I get the content text
textToSpeech.speak(content, TextToSpeech.QUEUE_FLUSH, null,i+"");
Copy the code

The sound didn’t play properly. After comparing the previous audio playback demo, it was found that the content was the same except for the text. After testing, it was found that when the length of the text exceeded a certain number, it would not play. Thanks for the comment, TTS does have a maximum length limit of 4000 words.

for (int i = 0; i < readContentList.size(); i++) {
    textToSpeech.speak(readContentList.get(i), TextToSpeech.QUEUE_ADD, null,i+"");
}
Copy the code

Split the long text code as follows:

Public static List<String> splitContent(String Content){//[u4e00-u9fa5] iS unicode2 Pattern.compile("[^\u4E00-\u9FA5]");
        Matcher matcher = pattern.matcher(content);
        content = matcher.replaceAll(""); Int startIndex = 0; int contentLength = 10; List<String> contentList = new ArrayList<>();while(startIndex<content.length()-1){
            if (startIndex + contentLength > content.length()){
                contentLength = content.length()-startIndex;
            }
            String contentTemp = content.substring(startIndex,startIndex+contentLength);
            contentList.add(contentTemp);
            startIndex = startIndex + contentLength;
        }
        return contentList;
    }
Copy the code

I personally break the text into 10-word paragraphs.

  • To sum up: the text reading function is basically finished now. All you need to do is break down the novel text into multiple pieces and add them to TextToSpeech. The remaining difficulties, I think, mainly lie in the player. The function points of the player are as follows:
  1. Play/pause button
  2. The text of the previous/next chapter
  3. Draggable progress bar
  4. Timing closure

Let’s start with the process bit by bit. Since it is a company project, we may mainly record the logical processing ideas in our development process:

First of all, the following code is a reading listener method for TextToSpeech, According to onStart(String utteranceId) and onDone(String utteranceId), we can determine which utteranceId is played. Is the last parameter to set.

We can record in onStart(String utteranceId) which sound is currently played

textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {@override public void onStart(String utteranceId) {// TODO: 2019/8/15 UtteranceId is textToSpeech. Speak ("".""Override public void onDone(String utteranceId) {// TODO: Override public void onError(String utteranceId) {}});Copy the code
  • Player play/pause button

Called when clicking pause:

 if(textToSpeech! =null){ textToSpeech.stop(); // Exit loop or stop broadcast}Copy the code

Call again when the play button is clicked:

// progressIndex is the current text progress recorded in onStart(String utteranceId){}for (int i = progressIndex; i < readContentList.size(); i++) {
    textToSpeech.speak(readContentList.get(i), TextToSpeech.QUEUE_ADD, null,i+"");
 }

Copy the code

There is a problem in resuming the playback. For example, when the last paragraph of text is read to the eighth word, I click pause and then read it again, and the first word will be read again. You can solve this problem by breaking each paragraph of text into smaller pieces, or even one paragraph per word (I’ve tried that, but reading it out loud feels like a stutter).

  • After clearing the old text data, split the new text and call textToSpeech.speak () again

  • Draggable Progress Bar As mentioned earlier, the total length of the progress bar can be set based on the length of the readContentList, which splits a chapter into multiple sections of text

seekbar.setMax(readContentList.size());
Copy the code

The length of the progress bar can be measured by the time it takes to read each paragraph * the length of the text

long seekbarTime = readTime * readContentList.size();
Copy the code

The time required to read each paragraph of text can be determined according to the two onStart(…) methods in the listener. Make a time difference to calculate how long it takes to read a passage of text. However, according to my calculation, the first reading takes a lot of time, which is about 2800 milliseconds for every 10 words.

Drag the progress bar each time to reposition the playback position according to its progress.

  • Timed closing reopens a child thread to count down and then executes textToSpeech.stop ()