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);
}
}
最大3つのピリオド( "。")を追加し、その後に単一のスペース ""を追加することで、文の間、または任意の場所に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 <21の場合: public int playSilence(long durationInMs、int queueMode、HashMap params)
> 21の場合: public int playSilentUtterance(long durationInMs、int queueMode、String utteranceId)
TextToSpeech.QUEUE_FLUSH ではなく TextToSpeech.QUEUE_ADD を使用することを忘れないでください。そうしないと、以前に開始された音声がクリアされます。
私はまだこれを試していませんが、同じことをする必要があります。私の考えは、最初にあなたのスピーチテキストを単語の配列に分割することです。
次に、現在のWordのカウンターを維持しながら、現在のWordの終了後に次のWordを再生する再帰関数を作成します。
messages
を部分に分割し、utterance
リスナーを使用して最後のonutteranceprogress
をリッスンします
tts.playSilence(1250, TextToSpeech.QUEUE_ADD, null);
私は別のアプローチを使用しました。
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()
}
}
}
次のように、単語の後にピリオドを入れて、次の単語を大文字で始めると、新しい文のように見えます。
帰宅後、夕食を食べました。
「家。私たち」はそこで一時停止します。
また、エスケープされた引用符(\ ")は、いくらか同様に一時停止しているように見えます。少なくとも、Wordの周りに配置すると、Wordの周りにスペースが追加されます。
このソリューションは完璧ではありませんが、@ Aaron Cのソリューションの代わりに、以下のようなカスタムのテキスト読み上げクラスを作成することもできます。このソリューションは、テキストが比較的短く、1分あたりの話し言葉が、使用している言語に対して十分に正確である場合に、十分に機能する可能性があります。
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");
}
}