diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82b798cd..e42e2edc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,3 @@
-2022/05/26
+2022/08/16
-添加预览版语音,需要打开预览语音开关,当选择预览版语音时,如果卡住了,杀掉应用重进!!!
\ No newline at end of file
+普通模式不再支持风格调整
\ No newline at end of file
diff --git a/app/src/main/java/me/ag2s/tts/MainActivity.java b/app/src/main/java/me/ag2s/tts/MainActivity.java
index 7a6c424c..a22be18b 100644
--- a/app/src/main/java/me/ag2s/tts/MainActivity.java
+++ b/app/src/main/java/me/ag2s/tts/MainActivity.java
@@ -110,8 +110,12 @@ protected void onCreate(Bundle savedInstanceState) {
binding.switchUseDict.setChecked(APP.getBoolean(Constants.USE_DICT, false));
binding.switchUseDict.setOnCheckedChangeListener((buttonView, isChecked) -> APP.putBoolean(Constants.USE_DICT, isChecked));
+ showStyleView(APP.getBoolean(Constants.USE_PREVIEW, false));
binding.switchUsePreview.setChecked(APP.getBoolean(Constants.USE_PREVIEW, false));
- binding.switchUsePreview.setOnCheckedChangeListener(((buttonView, isChecked) -> APP.putBoolean(USE_PREVIEW, isChecked)));
+ binding.switchUsePreview.setOnCheckedChangeListener(((buttonView, isChecked) -> {
+ showStyleView(isChecked);
+ APP.putBoolean(USE_PREVIEW, isChecked);
+ }));
TtsActorAdapter actorAdapter = new TtsActorAdapter(TtsActorManger.getInstance().getActors());
@@ -186,6 +190,17 @@ private void connectToText2Speech() {
}
+ private void showStyleView(boolean show) {
+ if (show) {
+ binding.rvVoiceStyles.setVisibility(View.VISIBLE);
+ binding.ttsStyleDegreeParent.setVisibility(View.VISIBLE);
+ } else {
+ binding.rvVoiceStyles.setVisibility(View.GONE);
+ binding.ttsStyleDegreeParent.setVisibility(View.GONE);
+ }
+ }
+
+
@SuppressLint("SetTextI18n")
private void updateView() {
APP.putInt(Constants.VOICE_STYLE_DEGREE, styleDegree);
diff --git a/app/src/main/java/me/ag2s/tts/services/Constants.java b/app/src/main/java/me/ag2s/tts/services/Constants.java
index 7a192b19..78f24dfb 100644
--- a/app/src/main/java/me/ag2s/tts/services/Constants.java
+++ b/app/src/main/java/me/ag2s/tts/services/Constants.java
@@ -57,7 +57,7 @@ public final class Constants {
public static final String EDGE_ORIGIN = "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold";
- public static final String EDGE_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.47";
+ public static final String EDGE_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.124 Safari/537.36 Edg/102.0.1245.41";
public static final String EDGE_URL = "https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=6A5AA1D4EAFF4E9FB37E23D68491D6F4";
}
diff --git a/app/src/main/java/me/ag2s/tts/services/SSML.java b/app/src/main/java/me/ag2s/tts/services/SSML.java
index 7173c277..10048867 100644
--- a/app/src/main/java/me/ag2s/tts/services/SSML.java
+++ b/app/src/main/java/me/ag2s/tts/services/SSML.java
@@ -73,13 +73,16 @@ public class SSML {
private short pitch;
private short rate;
private boolean useDict;
+ //是否使用预览版
+ private boolean usePre;
private static SSML instance;
- private SSML(SynthesisRequest request, String name, TtsStyle ttsStyle, boolean useDict) {
+ private SSML(SynthesisRequest request, String name, TtsStyle ttsStyle, boolean useDict, boolean usePre) {
this.content = new StringBuilder(request.getCharSequenceText());
this.useDict = useDict;
+ this.usePre = usePre;
this.name = name;
this.style = new WeakReference<>(ttsStyle);
this.time = CommonTool.getTime();
@@ -97,19 +100,19 @@ private SSML(SynthesisRequest request, String name, TtsStyle ttsStyle, boolean u
}
- public static SSML getInstance(SynthesisRequest request, String name, TtsStyle ttsStyle, boolean useDict) {
+ public static SSML getInstance(SynthesisRequest request, String name, TtsStyle ttsStyle, boolean useDict, boolean usePre) {
if (instance == null) {
- instance = new SSML(request, name, ttsStyle, useDict);
+ instance = new SSML(request, name, ttsStyle, useDict, usePre);
} else {
- instance.content=new StringBuilder(request.getCharSequenceText());
+ instance.content = new StringBuilder(request.getCharSequenceText());
instance.useDict = useDict;
+ instance.usePre = usePre;
instance.name = name;
instance.style = new WeakReference<>(ttsStyle);
instance.time = CommonTool.getTime();
instance.pitch = (short) (request.getPitch() - 100);
instance.rate = (short) (request.getSpeechRate());
instance.id = CommonTool.getMD5String(request.getCharSequenceText() + "" + System.currentTimeMillis());
- instance.useDict = useDict;
instance.handleContent();
}
return instance;
@@ -128,7 +131,7 @@ private void handleContent() {
CommonTool.replace(content, ">", "<");
CommonTool.replace(content, "<", ">");
//是否分段
- if (APP.getBoolean(Constants.SPLIT_SENTENCE, false)) {
+ if (APP.getBoolean(Constants.SPLIT_SENTENCE, false) && usePre) {
String temp = content.toString();
temp = p0.matcher(temp).replaceAll("$1");//把常用的影响分句的重复符号合并
temp = p1.matcher(temp).replaceAll("$1
$2");//单字符断句符,排除后面有引号的情况
@@ -155,20 +158,45 @@ private void handleContent() {
@NonNull
@Override
public String toString() {
- String rateString =rate/100+"."+rate%100;
- String pitchString = pitch >= 0 ? "+" + pitch + "Hz" : pitch + "Hz";
- return "X-RequestId:" + id + "\r\n" +
- "Content-Type:application/ssml+xml\r\n" +
- "X-Timestamp:" + time + "Z\r\n" +
-
- "Path:ssml\r\n\r\n" +
- "" +
- "" +
- "" +
- "" +
- "" + content.toString() + "
" +
- "";
+ String rateString = rate / 100 + "." + rate % 100;
+// if (!usePre) {
+// return "Path: ssml" + "\r\n" +
+// "X-RequestId: " + id + "\r\n" +
+// "X-Timestamp: " + time + "Z" + "\r\n" +
+// "Content-Type: application/ssml+xml" + "\r\n\r\n" +
+// "" + content.toString() + "\r\n" +
+// "";
+// }
+ //String pitchString = pitch >= 0 ? "+" + pitch + "Hz" : pitch + "Hz";
+ StringBuilder sb = new StringBuilder()
+ .append("Path:ssml\r\n")
+ .append("X-RequestId:").append(id).append("\r\n")
+ .append("X-Timestamp:").append(time).append("Z\r\n")
+ .append("Content-Type:application/ssml+xml\r\n\r\n");
+
+
+ sb.append("");
+ sb.append("");
+ if (usePre) {
+ sb.append("");
+ }
+ sb.append("");
+
+ if (usePre) {
+ sb.append("").append(content.toString()).append("
");
+ } else {
+ sb.append("").append(content.toString()).append("");
+
+ }
+
+
+ sb.append("");
+ if (usePre) {
+ sb.append("");
+ }
+
+ sb.append("");
+
+ return sb.toString();
}
}
diff --git a/app/src/main/java/me/ag2s/tts/services/TTSService.java b/app/src/main/java/me/ag2s/tts/services/TTSService.java
index 74e0ea52..6d28d94d 100644
--- a/app/src/main/java/me/ag2s/tts/services/TTSService.java
+++ b/app/src/main/java/me/ag2s/tts/services/TTSService.java
@@ -68,6 +68,7 @@ public class TTSService extends TextToSpeechService {
private final OkHttpClient client;
@Nullable
private volatile WebSocket webSocket;
+ private volatile boolean isPreview = false;
private volatile boolean isSynthesizing = false;
//当前的生成格式
private volatile TtsOutputFormat currentFormat;
@@ -564,18 +565,29 @@ public WebSocket getOrCreateWs() {
synchronized (TTSService.class) {
if (this.webSocket == null) {
+ while (TokenHolder.token == null) {
+ try {
+ this.wait(500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
String url;
- String origin = Constants.EDGE_ORIGIN;
+ String origin;
if (TokenHolder.token != null && APP.getBoolean(Constants.USE_PREVIEW, false)) {
url = "wss://eastus.tts.speech.microsoft.com/cognitiveservices/websocket/v1?Authorization=bearer " + TokenHolder.token + "&X-ConnectionId=" + CommonTool.getMD5String(new Date().toString());
origin = "https://azure.microsoft.com";
+ isPreview = true;
} else {
- url = Constants.EDGE_URL + "&ConnectionId=" + CommonTool.getMD5String(new Date().toString());
+ url = Constants.EDGE_URL;
+ isPreview = false;
+ origin = Constants.EDGE_ORIGIN;
}
Request request = new Request.Builder()
.url(url)
- .header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
- .header("Accept-Encoding", "gzip, deflate")
+ //.header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
+ //.header("Accept-Encoding", "gzip, deflate")
.header("User-Agent", Constants.EDGE_UA)
.addHeader("Origin", origin)
.build();
@@ -595,7 +607,7 @@ public WebSocket getOrCreateWs() {
* 发送合成语音配置,更改格式需要重新发送
*/
private synchronized void sendConfig(WebSocket ws, TtsConfig ttsConfig) {
- String msg = "X-Timestamp:+" + getTime() + "Z\r\n" +
+ String msg = "X-Timestamp:+" + getTime() + "\r\n" +
"Content-Type:application/json; charset=utf-8\r\n" +
"Path:speech.config\r\n\r\n"
+ ttsConfig.toString();
@@ -637,14 +649,15 @@ public synchronized void sendText(SynthesisRequest request, SynthesisCallback ca
ttsStyle.setStyleDegree(APP.getInt(Constants.VOICE_STYLE_DEGREE, 100));
ttsStyle.setVolume(APP.getInt(Constants.VOICE_VOLUME, 100));
boolean useDict = APP.getBoolean(Constants.USE_DICT, false);
- SSML ssml = SSML.getInstance(request, name, ttsStyle, useDict);
- Log.e(TAG, ssml.toString());
+
//webSocket = webSocket == null ? getOrCreateWs() : webSocket;
if (oldFormatIndex != index) {
sendConfig(getOrCreateWs(), ttsConfig);
oldFormatIndex = index;
}
+ SSML ssml = SSML.getInstance(request, name, ttsStyle, useDict, isPreview);
+ Log.e(TAG, ssml.toString());
//在Google Play图书之类应用会闪退,应该及时调用该方法
callback.start(currentFormat.HZ,
currentFormat.BitRate, 1 /* Number of channels. */);
diff --git a/app/src/main/java/me/ag2s/tts/services/TtsActor.java b/app/src/main/java/me/ag2s/tts/services/TtsActor.java
index a027d7a4..7583671f 100644
--- a/app/src/main/java/me/ag2s/tts/services/TtsActor.java
+++ b/app/src/main/java/me/ag2s/tts/services/TtsActor.java
@@ -32,6 +32,7 @@ public class TtsActor {
*/
@Nullable
private String note;
+ private Locale tempLocate;
public TtsActor(@NonNull String name, @NonNull String shortName, @NonNull String locate, boolean gender, @Nullable String note) {
this.name = name;
@@ -53,10 +54,11 @@ public TtsActor(String shortName, boolean gender, @Nullable String note) {
}
//String[] temp = locale.split(tag);
- this.name = shortName.substring(shortName.lastIndexOf(tag) + 1).replace("Neural","");
+ this.name = shortName.substring(shortName.lastIndexOf(tag) + 1).replace("Neural", "");
this.locale = shortName.substring(0, shortName.lastIndexOf(tag));
}
+
@SuppressWarnings("unused")
public TtsActor(String name, String shortName, String locate, boolean gender) {
this(name, shortName, locate, gender, "");
@@ -92,6 +94,11 @@ public void setGender(boolean gender) {
}
public Locale getLocale() {
+ if (tempLocate != null) {
+ return tempLocate;
+ }
+
+
String tag = "-";
if (locale.contains("-")) {
tag = "-";
@@ -118,3 +125,5 @@ public void setNote(@Nullable String note) {
this.note = note;
}
}
+
+
diff --git a/app/src/main/java/me/ag2s/tts/services/TtsActorManger.java b/app/src/main/java/me/ag2s/tts/services/TtsActorManger.java
index 4fd2b6ea..68f8498e 100644
--- a/app/src/main/java/me/ag2s/tts/services/TtsActorManger.java
+++ b/app/src/main/java/me/ag2s/tts/services/TtsActorManger.java
@@ -1,5 +1,6 @@
package me.ag2s.tts.services;
+import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -508,48 +509,50 @@ private TtsActorManger() {
}
public List sortByLocale(List list, Locale locale) {
- Collections.sort(list, (o1, o2) -> {
- Locale loc1 = o1.getLocale();
- Locale loc2 = o2.getLocale();
- boolean b11 = loc1.getISO3Language().equals(locale.getISO3Language());
- boolean b12 = loc1.getISO3Country().equals(locale.getISO3Country());
- boolean b13 = loc1.getDisplayVariant(Locale.US).equals(locale.getDisplayVariant(Locale.US));
- boolean b21 = loc2.getISO3Language().equals(locale.getISO3Language());
- boolean b22 = loc2.getISO3Country().equals(locale.getISO3Country());
- boolean b23 = loc2.getDisplayVariant(Locale.US).equals(locale.getDisplayVariant(Locale.US));
- //语言不同
- if ((!b11) && (!b21)) {
- return 0;
- }
- //两个都相同
- if (b11 && b12 && b13 == b21 && b22 && b23) {
- return 0;
- }
- if (b11 && b12 && b13) {
- return -1;
- }
- if (b21 && b22 && b23) {
- return 1;
- }
-
- if ((b11 && b12 == b21 && b22)) {
- if (b13 == b23) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ Collections.sort(list, (o1, o2) -> {
+ Locale loc1 = o1.getLocale();
+ Locale loc2 = o2.getLocale();
+ boolean b11 = loc1.getISO3Language().equals(locale.getISO3Language());
+ boolean b12 = loc1.getISO3Country().equals(locale.getISO3Country());
+ boolean b13 = loc1.getDisplayVariant(Locale.US).equals(locale.getDisplayVariant(Locale.US));
+ boolean b21 = loc2.getISO3Language().equals(locale.getISO3Language());
+ boolean b22 = loc2.getISO3Country().equals(locale.getISO3Country());
+ boolean b23 = loc2.getDisplayVariant(Locale.US).equals(locale.getDisplayVariant(Locale.US));
+ //语言都不同
+ if ((!b11) && (!b21)) {
+ return 0;
+ }
+ //两个都相同
+ if (b11 && b12 && b13 == b21 && b22 && b23) {
return 0;
}
- if (b13) {
+ if (b11 && b12 && b13) {
return -1;
- } else {
+ }
+ if (b21 && b22 && b23) {
return 1;
}
- }
- if (b11 && b12) {
- return -1;
- }
- if (b21 && b22) {
- return 1;
- }
- return 0;
- });
+
+ if ((b11 && b12 == b21 && b22)) {
+ if (b13 == b23) {
+ return 0;
+ }
+ if (b13) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ if (b11 && b12) {
+ return -1;
+ }
+ if (b21 && b22) {
+ return 1;
+ }
+ return 0;
+ });
+ }
return list;
}
@@ -571,6 +574,7 @@ public TtsActor getByName(@NonNull String name) {
*/
@SuppressWarnings("unused")
public synchronized List getActors() {
+
return sortByLocale(this.actors, Locale.getDefault());
//return this.actors;
}
diff --git a/app/src/main/java/me/ag2s/tts/services/TtsConfig.java b/app/src/main/java/me/ag2s/tts/services/TtsConfig.java
index 01832994..fe9b940c 100644
--- a/app/src/main/java/me/ag2s/tts/services/TtsConfig.java
+++ b/app/src/main/java/me/ag2s/tts/services/TtsConfig.java
@@ -31,6 +31,12 @@ public TtsOutputFormat getFormat() {
@NotNull
@Override
public String toString() {
+ //X-Timestamp:Thu Jun 16 2022 19:13:55 GMT+0800 (中国标准时间)
+ //Content-Type:application/json; charset=utf-8
+ //Path:speech.config
+ //
+ //{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":"false","wordBoundaryEnabled":"true"},"outputFormat":"webm-24khz-16bit-mono-opus"}}}}
+
String msg = "{\"context\":{\"synthesis\":{\"audio\":{\"metadataoptions\":{\"sentenceBoundaryEnabled\":\"%s\",\"wordBoundaryEnabled\":\"%s\"},\"outputFormat\":\"%s\"}}}}";
msg = String.format(msg, sentenceBoundaryEnabled ? "true" : "false", "true", TtsFormatManger.getInstance().getFormat(index).value);
Log.d(TTSService.class.getSimpleName(), msg);
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 549c8788..8faf1afa 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -22,6 +22,13 @@
android:layout_height="wrap_content"
android:text="@string/set_tts" />
+
+
-
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0f4c38eb..5d5f44bb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -21,7 +21,7 @@
电池优化
更新词典
自定义
- 讲话风格(无效值将将忽略)
+ 讲话风格(无效值将将忽略,需要打开预览语音才能用)
风格强度与合成音量
测试
diff --git a/build.gradle b/build.gradle
index dbdd3686..ea18201c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,8 +2,8 @@
buildscript {
ext{
agp_version= '7.2.1'
- kotlin_version = '1.6.21'
- okhttp_version="5.0.0-alpha.7"
+ kotlin_version = '1.7.0'
+ okhttp_version= '5.0.0-alpha.8'
okio_version="3.1.0"
}
}
diff --git a/release/TTS_release_v0.2_202205261737.apk b/release/TTS_release_v0.2_202205261737.apk
deleted file mode 100644
index 1cc7afe9..00000000
Binary files a/release/TTS_release_v0.2_202205261737.apk and /dev/null differ
diff --git a/release/TTS_release_v0.2_202205261737.apk.idsig b/release/TTS_release_v0.2_202205261737.apk.idsig
deleted file mode 100644
index fb87054f..00000000
Binary files a/release/TTS_release_v0.2_202205261737.apk.idsig and /dev/null differ
diff --git a/release/output-metadata.json b/release/output-metadata.json
index 1035b80a..92225b21 100644
--- a/release/output-metadata.json
+++ b/release/output-metadata.json
@@ -12,8 +12,8 @@
"filters": [],
"attributes": [],
"versionCode": 1,
- "versionName": "0.2_202205261737",
- "outputFile": "TTS_release_v0.2_202205261737.apk"
+ "versionName": "0.2_202206162158",
+ "outputFile": "TTS_release_v0.2_202206162158.apk"
}
],
"elementType": "File"