【问题标题】:Highlighting the Text while Speech is Progressing在语音进行时突出显示文本
【发布时间】:2020-04-16 17:31:38
【问题描述】:

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

请查看以下链接中的“我的应用”屏幕截图。

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

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();
    }
}

直到这里我没有遇到任何问题。现在我想突出显示文本。我不知道该怎么做。我到处搜索仍然没有这方面的线索。

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

【问题讨论】:

    标签: java android text-to-speech highlight


    【解决方案1】:

    如果您想更改当前 TTS 中的段落颜色。

    此代码适用于 Google TTS、Samsung TTS 以及其他 TTS 引擎

    首先你必须实现 TextToSpeech.OnInitListener 喜欢(公共类 MainActivity 扩展 AppCompatActivity 实现 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<>();
    

    在您的活动中 onCrreat() 方法

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

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

    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");
            }
    
        });
    

    【讨论】:

      【解决方案2】:

      对于 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) 检测每首乐曲已经/正在被朗读的时间

      3) 突出显示相应的部分

      但是,不幸的是,当使用实时生成语音输出的 Android TextToSpeech 类时,您能够精确检测(使用 UtteranceProgressListener)进度的最小语音单位是 utterance(您决定发送到 TTS 的任何字符串)——不一定是 单词。

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

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

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

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

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

      显然已经存在一些应用程序和游戏可以做到这一点......像说唱歌手 Parappa 或卡拉 OK 应用程序这样的游戏,但我认为他们这样做的方式是使用预先录制的/静态音频文件,其中标记编码为触发亮点的确切时间。如果您的文本内容总是相同的,并且只使用一种语言,那么您也可以这样做。

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

      如果您决定采用这些更缩小范围的方法之一,那么我建议您相应地发布一个新问题。

      【讨论】:

      • 感谢您回答我的问题。如你所说。突出句子是更好的选择。这对我来说很好。我将突出显示句子。但我需要你的一点帮助。你能告诉我你是怎么做到的吗?我对 UtterancesProgress 监听器了解不多。这将非常有帮助。
      • 不客气。我可以试一试,但我认为接受这个答案然后自己试一试会更合适,然后在遇到问题时提出一个新的、更准确的问题。
      • 你还在做这个吗?我编辑了答案。如果你喜欢这个答案,请接受它(和其他人)谢谢!
      • 对于 onRangeStart(),如果我要说的文本包含句号、问号和感叹号等标点符号,我将面临问题。如果文本是包含多行的段落,则会出现此问题。 onRangeStart() 一旦遇到上述任何标点符号就会停止发送开始、结束和帧数据,因此来自 onRangeStart() 方法的回调是不可靠的。
      猜你喜欢
      • 2014-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多