【发布时间】:2011-06-25 14:34:08
【问题描述】:
我正在使用 android TTS 播放文本 - android.speech.tts.TextToSpeech
我使用:TextToSpeech.speak 说话,.stop 停止。有没有办法暂停文本?
【问题讨论】:
我正在使用 android TTS 播放文本 - android.speech.tts.TextToSpeech
我使用:TextToSpeech.speak 说话,.stop 停止。有没有办法暂停文本?
【问题讨论】:
据我所知,TTS SDK 没有任何暂停功能。但是您可以使用synthesizeToFile() 创建一个包含 TTS 输出的音频文件。然后,您将使用MediaPlayer 对象来播放、暂停和停止播放文件。根据文本字符串的长度,生成音频可能需要更长的时间,因为synthesizeToFile() 函数必须在播放之前完成整个文件,但这种延迟对于大多数应用程序来说应该是可以接受的。
【讨论】:
我使用了字符串拆分并使用了如下所示的playsilence():
public void speakSpeech(String speech) {
HashMap<String, String> myHash = new HashMap<String, String>();
myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "done");
String[] splitspeech = speech.split("\\.");
for (int i = 0; i < splitspeech.length; i++) {
if (i == 0) { // Use for the first splited text to flush on audio stream
textToSpeech.speak(splitspeech[i].toString().trim(),TextToSpeech.QUEUE_FLUSH, myHash);
} else { // add the new test on previous then play the TTS
textToSpeech.speak(splitspeech[i].toString().trim(), TextToSpeech.QUEUE_ADD,myHash);
}
textToSpeech.playSilence(750, TextToSpeech.QUEUE_ADD, null);
}
}
【讨论】:
您可以在句子之间或任何您想要的地方暂停 TTS,方法是添加最多三个句点(“.”),后面跟一个空格“”。下面的示例在开头有很长的停顿,在消息正文之前也是如此。我不确定那是你所追求的。
private final BroadcastReceiver SMScatcher = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(
"android.provider.Telephony.SMS_RECEIVED")) {
// if(message starts with SMStretcher recognize BYTE)
StringBuilder sb = new StringBuilder();
/*
* The SMS-Messages are 'hiding' within the extras of the
* Intent.
*/
Bundle bundle = intent.getExtras();
if (bundle != null) {
/* Get all messages contained in the Intent */
Object[] pdusObj = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdusObj.length];
for (int i = 0; i < pdusObj.length; i++) {
messages[i] = SmsMessage
.createFromPdu((byte[]) pdusObj[i]);
}
/* Feed the StringBuilder with all Messages found. */
for (SmsMessage currentMessage : messages) {
// periods are to pause
sb.append("... Message From: ");
/* Sender-Number */
sb.append(currentMessage.getDisplayOriginatingAddress());
sb.append(".. ");
/* Actual Message-Content */
sb.append(currentMessage.getDisplayMessageBody());
}
// Toast.makeText(application, sb.toString(),
// Toast.LENGTH_LONG).show();
if (mTtsReady) {
try {
mTts.speak(sb.toString(), TextToSpeech.QUEUE_ADD,
null);
} catch (Exception e) {
Toast.makeText(application, "TTS Not ready",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
}
}
}
};
如果您在最后一个句点之后省略空格,它将(或可能)无法按预期工作。
【讨论】:
在没有暂停选项的情况下,您可以在想要延迟 TTS 引擎讲话期间添加静音。这当然必须是预先确定的“暂停”,例如,包括暂停按钮的功能也无济于事。
对于 API public int playSilence (long durationInMs, int queueMode, HashMap params)
对于 > 21 :public int playSilentUtterance (long durationInMs, int queueMode, String utteranceId)
记得使用TextToSpeech.QUEUE_ADD而不是TextToSpeech.QUEUE_FLUSH,否则会清除之前启动的语音。
【讨论】:
我使用了不同的方法。
Kotlin 代码:
class VoiceService {
private lateinit var textToSpeech: TextToSpeech
var sentenceCounter: Int = 0
var myList: List<String> = ArrayList()
fun resume() {
sentenceCounter -= 1
speakText()
}
fun pause() {
textToSpeech.stop()
}
fun stop() {
sentenceCounter = 0
textToSpeech.stop()
}
fun speakText() {
var myText = "This is some text to speak. This is more text to speak."
myList =myText.split(".")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, null, utteranceId)
sentenceCounter++
} else {
var map: HashMap<String, String> = LinkedHashMap<String, String>()
map[TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID] = utteranceId
textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, map)
sentenceCounter++
}
}
override fun onDone(p0: String?) {
if (sentenceCounter < myList.size) {
speakText()
} else {
speakNextText()
}
}
}
【讨论】:
我还没有尝试过,但我需要做同样的事情。我的想法是首先将您的语音文本拆分为一组单词。
然后创建一个递归函数,在当前单词完成后播放下一个单词,同时保留当前单词的计数器。
【讨论】:
将messages 分成多个部分,并使用onutteranceprogress 侦听器侦听最后一个utterance
tts.playSilence(1250, TextToSpeech.QUEUE_ADD, null);
【讨论】:
似乎如果你在一个单词后面加上一个句点,并且下一个单词以大写字母开头,就像一个新句子一样,像这样:
我们回家后。我们吃了晚饭。
“家。我们”将在其中暂停。
【讨论】:
此外,转义的引号 (\") 似乎也会暂停 - 至少,如果你将它放在单词周围,它会在单词周围增加空格。
【讨论】:
这个解决方案并不完美,但@Aaron C 的解决方案的替代方案可能是创建一个自定义文本到语音类,如下所示。如果您的文本相对较短并且每分钟的口语对于您使用的语言来说足够准确,则此解决方案可能会运行良好。
private class CustomTextToSpeech extends TextToSpeech {
private static final double WORDS_PER_MS = (double)190/60/1000;
long startTimestamp = 0;
long pauseTimestamp = 0;
private Handler handler;
private Runnable speakRunnable;
StringBuilder textToSpeechBuilder;
private boolean isPaused = false;
public CustomTextToSpeech(Context context, OnInitListener initListener){
super(context, initListener);
setOnUtteranceProgressListener(new UtteranceProgressListener() {
@Override
public void onDone(String arg0) {
Log.d(TAG, "tts done. " + arg0);
startTimestamp = 0;
pauseTimestamp = 0;
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
@Override
public void onError(String arg0) {
Log.e(TAG, "tts error. " + arg0);
}
@Override
public void onStart(String arg0) {
Log.d(TAG, "tts start. " + arg0);
setStartTimestamp(System.currentTimeMillis());
}
});
handler = new Handler();
speakRunnable = new Runnable() {
@Override
public void run() {
speak();
}
};
textToSpeechBuilder = new StringBuilder(getResources().getString(R.string.talkback_tips));
}
public void setStartTimestamp(long timestamp) {
startTimestamp = timestamp;
}
public void setPauseTimestamp(long timestamp) {
pauseTimestamp = timestamp;
}
public boolean isPaused(){
return (startTimestamp > 0 && pauseTimestamp > 0);
}
public void resume(){
if(handler != null && isPaused){
if(startTimestamp > 0 && pauseTimestamp > 0){
handler.postDelayed(speakRunnable, TTS_SETUP_TIME_MS);
} else {
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
}
isPaused = false;
}
public void pause(){
isPaused = true;
if (handler != null) {
handler.removeCallbacks(speakRunnable);
handler.removeMessages(1);
}
if(isSpeaking()){
setPauseTimestamp(System.currentTimeMillis());
}
stop();
}
public void utter(){
if(handler != null){
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
}
public void speak(){
Log.d(TAG, "textToSpeechBuilder: " + textToSpeechBuilder.toString());
if(isPaused()){
String[] words = textToSpeechBuilder.toString().split(" ");
int wordsAlreadySpoken = (int)Math.round((pauseTimestamp - startTimestamp)*WORDS_PER_MS);
words = Arrays.copyOfRange(words, wordsAlreadySpoken-1, words.length);
textToSpeechBuilder = new StringBuilder();
for(String s : words){
textToSpeechBuilder.append(s);
textToSpeechBuilder.append(" ");
}
} else {
textToSpeechBuilder = new StringBuilder(getResources().getString(R.string.talkback_tips));
}
if (tts != null && languageAvailable)
speak(textToSpeechBuilder.toString(), TextToSpeech.QUEUE_FLUSH, new Bundle(), "utter");
}
}
【讨论】: