Я играю текст с андроидом TTS - android.speech.tts.TextToSpeech
Я использую: TextToSpeech.speak
, чтобы говорить, и .stop
для остановки. Есть ли способ также приостановить текст?
Я играю текст с андроидом TTS - android.speech.tts.TextToSpeech
Я использую: TextToSpeech.speak
, чтобы говорить, и .stop
для остановки. Есть ли способ также приостановить текст?
У TTS SDK нет никаких функций паузы, о которых я знаю. Но вы можете использовать synthesizeToFile()
для создания аудиофайла, содержащего вывод TTS. Затем вы будете использовать объект MediaPlayer для воспроизведения, приостановки и остановки воспроизведения файла. В зависимости от длины текстовой строки может потребоваться немного больше времени для создания звука, потому что функция synthesizeToFile()
должна была бы завершить весь файл, прежде чем вы сможете его воспроизвести, но эта задержка должна быть приемлемой для большинства приложений.
Я использовал расщепление строки и используемую функцию playilence(), как показано ниже:
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 Engine. Это, конечно, должно быть предопределенной "паузой" и, например, не включало бы функциональность кнопки паузы.
Для API < 21: public int playSilence (long durationInMs, int queueMode, параметры HashMap)
Для > 21: public int playSilentUtterance (long durationInMs, int queueMode, String utteranceId)
Не забудьте использовать TextToSpeech.QUEUE_ADD, а не TextToSpeech.QUEUE_FLUSH в противном случае он очистит ранее запущенную речь.
Я еще этого не пробовал, но мне нужно сделать то же самое. Мое мышление состоит в том, чтобы сначала разделить текст речи на несколько слов.
Затем создайте рекурсивную функцию, которая воспроизводит следующее слово после завершения текущего слова, сохраняя при этом счетчик текущего слова.
разделите messages
на части и прослушайте последние utterance
, используя onutteranceprogress
прослушиватель
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");
}
}
Я использовал другой подход.
Код Котлина:
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()
}
}
}