在语音进行时突出显示文本
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");
}
});
我正在开发一个应用程序,其中我的文本视图由字符串和两个按钮组成。当我单击说话按钮时,文本会转换为语音。但我想 在语音 运行 时突出显示该词。
请查看下方“我的应用”屏幕截图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");
}
});