-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: New JavaScript api for TTS (#8812)
* New JavaScript api for TTS Since it is unlikely that a beginner will handle this api, I moved the functions on the android side to JavaScript with as little modification as possible. Since the function conversion rules have been simplified, the api can be extended with consistent rules even if other TTS functions are needed on the JavaScript side in the future. * Create LanguageUtils class Moved the localeFromStringIgnoringScriptAndExtensions function to the LanguageUtils class. * Improved JavaScript TTS initialization process Fixed a problem that null may be passed as an argument when initializing JavaScriptTTS. Changed to refer to context from within the class instead of the argument when initializing JavaScriptTTS.
- Loading branch information
1 parent
c6f5d3f
commit 5b9dab0
Showing
4 changed files
with
258 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
AnkiDroid/src/main/java/com/ichi2/anki/JavaScriptTTS.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
/**************************************************************************************** | ||
* Copyright (c) 2021 mikunimaru <[email protected]> * | ||
* | ||
* This program is free software; you can redistribute it and/or modify it under * | ||
* the terms of the GNU General Public License as published by the Free Software * | ||
* Foundation; either version 3 of the License, or (at your option) any later * | ||
* version. * | ||
* * | ||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY * | ||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * | ||
* PARTICULAR PURPOSE. See the GNU General Public License for more details. * | ||
* * | ||
* You should have received a copy of the GNU General Public License along with * | ||
* this program. If not, see <http://www.gnu.org/licenses/>. * | ||
****************************************************************************************/ | ||
|
||
package com.ichi2.anki; | ||
|
||
import android.content.Context; | ||
import android.os.Bundle; | ||
import android.speech.tts.TextToSpeech; | ||
|
||
import androidx.annotation.IntDef; | ||
import androidx.annotation.NonNull; | ||
|
||
/** | ||
* Since it is assumed that only advanced users will use the JavaScript api, | ||
* here, Android's TextToSpeech is converted for JavaScript almost as it is, giving priority to free behavior. | ||
* https://developer.android.com/reference/android/speech/tts/TextToSpeech | ||
*/ | ||
public class JavaScriptTTS implements TextToSpeech.OnInitListener { | ||
|
||
private static final int TTS_SUCCESS = TextToSpeech.SUCCESS; | ||
private static final int TTS_ERROR = TextToSpeech.ERROR; | ||
private static final int TTS_QUEUE_ADD = TextToSpeech.QUEUE_ADD; | ||
private static final int TTS_QUEUE_FLUSH = TextToSpeech.QUEUE_FLUSH; | ||
private static final int TTS_LANG_AVAILABLE = TextToSpeech.LANG_AVAILABLE; | ||
private static final int TTS_LANG_COUNTRY_AVAILABLE = TextToSpeech.LANG_COUNTRY_AVAILABLE; | ||
private static final int TTS_LANG_COUNTRY_VAR_AVAILABLE = TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE; | ||
private static final int TTS_LANG_MISSING_DATA = TextToSpeech.LANG_MISSING_DATA; | ||
private static final int TTS_LANG_NOT_SUPPORTED = TextToSpeech.LANG_NOT_SUPPORTED; | ||
|
||
@IntDef({TTS_SUCCESS, TTS_ERROR}) | ||
public @interface ErrorOrSuccess {} | ||
|
||
@IntDef({TTS_QUEUE_ADD, TTS_QUEUE_FLUSH}) | ||
public @interface QueueMode {} | ||
|
||
@IntDef({TTS_LANG_AVAILABLE, TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE, TTS_LANG_MISSING_DATA, TTS_LANG_NOT_SUPPORTED}) | ||
public @interface TTSLangResult {} | ||
|
||
@NonNull | ||
private static TextToSpeech mTts; | ||
private static boolean mTtsOk; | ||
private static final Bundle mTtsParams = new Bundle(); | ||
|
||
JavaScriptTTS() { | ||
Context mContext = AnkiDroidApp.getInstance().getApplicationContext(); | ||
mTts = new TextToSpeech(mContext, this); | ||
} | ||
|
||
/** OnInitListener method to receive the TTS engine status */ | ||
@Override | ||
public void onInit(@ErrorOrSuccess int status) { | ||
mTtsOk = status == TextToSpeech.SUCCESS; | ||
} | ||
|
||
/** | ||
* A method to speak something | ||
* @param text Content to speak | ||
* @param queueMode 1 for QUEUE_ADD and 0 for QUEUE_FLUSH. | ||
* @return ERROR(-1) SUCCESS(0) | ||
*/ | ||
@ErrorOrSuccess | ||
public int speak(String text, @QueueMode int queueMode) { | ||
return mTts.speak(text, queueMode, mTtsParams, "stringId"); | ||
} | ||
|
||
/** | ||
* If only a string is given, set QUEUE_FLUSH to the default behavior. | ||
* @param text Content to speak | ||
* @return ERROR(-1) SUCCESS(0) | ||
*/ | ||
@ErrorOrSuccess | ||
public int speak(String text) { | ||
return mTts.speak(text, TextToSpeech.QUEUE_FLUSH, mTtsParams, "stringId"); | ||
} | ||
|
||
/** | ||
* Sets the text-to-speech language. | ||
* The TTS engine will try to use the closest match to the specified language as represented by the Locale, but there is no guarantee that the exact same Locale will be used. | ||
* @param loc Specifying the language to speak | ||
* @return 0 Denotes the language is available for the language by the locale, but not the country and variant. | ||
* <li> 1 Denotes the language is available for the language and country specified by the locale, but not the variant. | ||
* <li> 2 Denotes the language is available exactly as specified by the locale. | ||
* <li> -1 Denotes the language data is missing. | ||
* <li> -2 Denotes the language is not supported. | ||
*/ | ||
@TTSLangResult | ||
public int setLanguage(String loc) { | ||
// The Int values will be returned | ||
// Code indicating the support status for the locale. See LANG_AVAILABLE, LANG_COUNTRY_AVAILABLE, LANG_COUNTRY_VAR_AVAILABLE, LANG_MISSING_DATA and LANG_NOT_SUPPORTED. | ||
return mTts.setLanguage(LanguageUtils.localeFromStringIgnoringScriptAndExtensions(loc)); | ||
} | ||
|
||
|
||
/** | ||
* Sets the speech pitch for the TextToSpeech engine. This has no effect on any pre-recorded speech. | ||
* @param pitch float: Speech pitch. 1.0 is the normal pitch, lower values lower the tone of the synthesized voice, greater values increase it. | ||
* @return ERROR(-1) SUCCESS(0) | ||
*/ | ||
@ErrorOrSuccess | ||
public int setPitch(float pitch) { | ||
// The following Int values will be returned | ||
// ERROR(-1) SUCCESS(0) | ||
return mTts.setPitch(pitch); | ||
} | ||
|
||
/** | ||
* | ||
* @param speechRate Sets the speech rate. 1.0 is the normal speech rate. This has no effect on any pre-recorded speech. | ||
* @return ERROR(-1) SUCCESS(0) | ||
*/ | ||
@ErrorOrSuccess | ||
public int setSpeechRate(float speechRate) { | ||
// The following Int values will be returned | ||
// ERROR(-1) SUCCESS(0) | ||
return mTts.setSpeechRate(speechRate); | ||
} | ||
|
||
/** | ||
* Checks whether the TTS engine is busy speaking. | ||
* Note that a speech item is considered complete once it's audio data has | ||
* been sent to the audio mixer, or written to a file. | ||
* | ||
*/ | ||
public boolean isSpeaking() { | ||
return mTts.isSpeaking(); | ||
} | ||
|
||
/** | ||
* Interrupts the current utterance (whether played or rendered to file) and discards other utterances in the queue. | ||
* @return ERROR(-1) SUCCESS(0) | ||
*/ | ||
@ErrorOrSuccess | ||
public int stop() { | ||
return mTts.stop(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/**************************************************************************************** | ||
* Copyright (c) 2021 mikunimaru <[email protected]> * | ||
* | ||
* This program is free software; you can redistribute it and/or modify it under * | ||
* the terms of the GNU General Public License as published by the Free Software * | ||
* Foundation; either version 3 of the License, or (at your option) any later * | ||
* version. * | ||
* * | ||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY * | ||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * | ||
* PARTICULAR PURPOSE. See the GNU General Public License for more details. * | ||
* * | ||
* You should have received a copy of the GNU General Public License along with * | ||
* this program. If not, see <http://www.gnu.org/licenses/>. * | ||
****************************************************************************************/ | ||
|
||
package com.ichi2.anki; | ||
|
||
import java.util.Locale; | ||
|
||
public class LanguageUtils { | ||
|
||
/** | ||
* Convert a string representation of a locale, in the format returned by Locale.toString(), | ||
* into a Locale object, disregarding any script and extensions fields (i.e. using solely the | ||
* language, country and variant fields). | ||
* <p> | ||
* Returns a Locale object constructed from an empty string if the input string is null, empty | ||
* or contains more than 3 fields separated by underscores. | ||
*/ | ||
public static Locale localeFromStringIgnoringScriptAndExtensions(String localeCode) { | ||
if (localeCode == null) { | ||
return new Locale(""); | ||
} | ||
|
||
localeCode = stripScriptAndExtensions(localeCode); | ||
|
||
String[] fields = localeCode.split("_"); | ||
switch (fields.length) { | ||
case 1: | ||
return new Locale(fields[0]); | ||
case 2: | ||
return new Locale(fields[0], fields[1]); | ||
case 3: | ||
return new Locale(fields[0], fields[1], fields[2]); | ||
default: | ||
return new Locale(""); | ||
} | ||
} | ||
|
||
private static String stripScriptAndExtensions(String localeCode) { | ||
int hashPos = localeCode.indexOf('#'); | ||
if (hashPos >= 0) { | ||
localeCode = localeCode.substring(0, hashPos); | ||
} | ||
return localeCode; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters