在语音进行时突出显示文本

Highlighting the Text while Speech is Progressing

我正在开发一个应用程序,其中我的文本视图由字符串和两个按钮组成。当我单击说话按钮时,文本会转换为语音。但我想 在语音 运行 时突出显示该词。

请查看下方“我的应用”屏幕截图link。

这是我的文字转语音初始化:

textToSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {

        @Override
        public void onInit(int status) {

            if (status == TextToSpeech.SUCCESS) {
                result = textToSpeech.setLanguage(Locale.ENGLISH);
                textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
                    @Override
                    public void onStart(String utteranceId) {
                        Log.d(utteranceId, "TTS start");}

                    @Override
                    public void onDone(String utteranceId) {
                        Log.d(utteranceId, "TTS done");}

                    @Override
                    public void onError(String utteranceId) {
             });
            } else {
                Toast.makeText(getApplicationContext(), "Feature is not Available", Toast.LENGTH_SHORT).show();
            }
        }
    });

和其他代码:

private void speak() {
 if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
        Toast.makeText(getApplicationContext(), "Feature is not Available", Toast.LENGTH_SHORT).show();
    } else {
        textToSpeech.setPitch(1f);
        textToSpeech.setSpeechRate(0.8f);
        HashMap<String, String> params = new HashMap<>();
        params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "utteranceId");
        textToSpeech.speak(getString(R.string.storytxt), TextToSpeech.QUEUE_FLUSH, params);

    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (textToSpeech != null) {
        textToSpeech.shutdown();
    }
}

到这里为止我没有遇到任何问题。现在我想突出显示文本。我不知道该怎么做it.I到处都找遍了还是没有任何线索。

我将字符串存储在 String.xml 中。

对于 Android API 26 及以上和支持 onRangeStart 的 TTS 引擎(在本例中,Google TTS):

public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {

    TextToSpeech tts;

    String sentence = "The Quick Brown Fox Jumps Over The Lazy Dog.";

    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);
        textView.setText(sentence);
        tts = new TextToSpeech(this, this);

    }

    // TextToSpeech.OnInitListener (for our purposes, the "main method" of this activity)
    public void onInit(int status) {

        tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {

            @Override
            public void onStart(String utteranceId) {
                Log.i("XXX", "utterance started");
            }

            @Override
            public void onDone(String utteranceId) {
                Log.i("XXX", "utterance done");
            }

            @Override
            public void onError(String utteranceId) {
                Log.i("XXX", "utterance error");
            }

            @Override
            public void onRangeStart(String utteranceId,
                                     final int start,
                                     final int end,
                                     int frame) {
                Log.i("XXX", "onRangeStart() ... utteranceId: " + utteranceId + ", start: " + start
                        + ", end: " + end + ", frame: " + frame);

                // onRangeStart (and all UtteranceProgressListener callbacks) do not run on main thread
                // ... so we explicitly manipulate views on the main thread:
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                        Spannable textWithHighlights = new SpannableString(sentence);
                        textWithHighlights.setSpan(new ForegroundColorSpan(Color.YELLOW), start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                        textView.setText(textWithHighlights);

                    }
                });

            }

        });

    }

    public void startClicked(View ignored) {

        tts.speak(sentence, TextToSpeech.QUEUE_FLUSH, null, "doesn't matter yet");

    }

}

// -------------------------------------- ----------------------

Android API 25岁及以下:

从理论上讲,实现此目的最直观的方法是:

1) 将字符串分成

2) 检测每个片段何时有 been/is 被朗读

3) 相应地突出显示那部分

然而,不幸的是,当使用实时生成语音输出的 Android TextToSpeech class 时,您能够精确检测进度的最小语音单位 ( using UtteranceProgressListener) 是 utterance(无论您决定发送到 TTS 的字符串)——不一定是 word.

没有任何机制可以让您简单地将多词字符串作为话语发送,然后以某种方式准确检测每个词何时被说出。

因此,为了(轻松地)按顺序突出显示每个单词,您必须:

A) 将每个单词作为单个话语单独发送到 TTS(但这会导致发音不连贯),或者

B)改为逐句突出显示,将每个句子作为话语发送(最简单的方法,但不是您想要的行为)。

如果真的非要达到一个字一个字的高亮效果,我能想到的(使用Android TextToSpeech)的唯一方法就是用句子大小的话语,而是使用 speak(),使用 synthesizeToFile()...然后使用某种媒体播放器或声音播放器来播放语音...以某种方式根据第 n 个单词相对于音频文件的总长度。因此,例如,如果句子有 10 个词长,而文件已完成 30%,那么您将突出显示第 4 个词。这将是困难且不准确的,但理论上是可能的。

显然已经有应用程序和游戏可以做到这一点...像 Parappa the Rapper 或卡拉 OK 应用程序这样的游戏,但我认为他们这样做的方式是让 pre-recorded/static 带有标记的音频文件在触发亮点的确切时间编码。如果你的文本内容总是一样的,而且只有一种语言,那么你也可以这样做。

但是,如果语音文本是用户输入的或直到运行时未知,需要 TTS,那么我不知道有任何直接的解决方案。

如果您决定采用其中一种更狭窄的方法,那么我建议相应地发布一个新问题。

如果你想改变当前TTS语音段落的颜色

This code works in Google TTS, Samsung TTS and also other TTS engines

首先你必须实施TextToSpeech.OnInitListener Like (public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener)

这是这段代码中使用的所有必要对象和变量。

private String sentance = "";
private String typingString = "";
private int paragraphCount = 0;
private HashMap<String, String> map = new HashMap<>();
private ArrayList<String> stringArrayList = new ArrayList<>();

在您的 activity onCrreat() 方法中

tts = new TextToSpeech(this, this);
map = new HashMap<>();
map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "UniqueID")

将此方法粘贴到您的 activity 中,然后单击按钮即可调用此方法。

private void newPlayMethod() {
    if (paragraphCount == 0) {
        stringArrayList = new ArrayList<>(Arrays.asList("Your Document texts".split("\n")));
    }
    try {
        SpannableString spannableString = new SpannableString(tvData.getText().toString());
        spannableString.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimaryDark)),
                0, tvData.getText().toString().length(), 33);
        spannableString.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorAccent)),
                tvData.getText().toString().indexOf(stringArrayList.get(paragraphCount)),
                tvData.getText().toString().indexOf(stringArrayList.get(paragraphCount)) +
                        stringArrayList.get(paragraphCount).length(),
                33);

        tts.speak(stringArrayList.get(paragraphCount), TextToSpeech.QUEUE_FLUSH, map);

        tvData.setText(spannableString);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

将以下代码放入 @Override onInit 方法。

 tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {

        @Override
        public void onStart(String utteranceId) {
            Log.i("TTS", "utterance started");
        }

        @Override
        public void onDone(String utteranceId) {
            if (stringArrayList.size() != paragraphCount) {
                paragraphCount++;
                newPlayMethod();
            } else {
                paragraphCount = 0;
            }
            Log.i("TTS", "utterance done");
        }

        @Override
        public void onError(String utteranceId) {
            Log.i("TTS", "utterance error");
        }

    });