Commit 9116195733e1ab6fbeba66ec8fcc0a2e6d708b01

Authored by xwenliang
1 parent fe1ead00

feat: 使用新语音接口

@@ -95,15 +95,90 @@ App({ @@ -95,15 +95,90 @@ App({
95 // domain: 'http://localhost:3003', 95 // domain: 'http://localhost:3003',
96 domain: 'https://api.xwenliang.cn', 96 domain: 'https://api.xwenliang.cn',
97 getTypeList(){ 97 getTypeList(){
  98 + const globalData = this;
98 return [ 99 return [
99 { 100 {
100 - desc: '女声一',  
101 - createUrl(text){  
102 - return `https://api.oick.cn/txt/apiz.php?text=${text}&spd=10`; 101 + desc: 'Chelsie(女)',
  102 + voice: 'Chelsie',
  103 + async createUrl(text){
  104 + return await globalData.requestTTS(text, 'Chelsie');
  105 + }
  106 + },
  107 + {
  108 + desc: 'Cherry(女)',
  109 + voice: 'Cherry',
  110 + async createUrl(text){
  111 + return await globalData.requestTTS(text, 'Cherry');
  112 + }
  113 + },
  114 + {
  115 + desc: 'Ethan(男)',
  116 + voice: 'Ethan',
  117 + async createUrl(text){
  118 + return await globalData.requestTTS(text, 'Ethan');
  119 + }
  120 + },
  121 + {
  122 + desc: 'Serena(女)',
  123 + voice: 'Serena',
  124 + async createUrl(text){
  125 + return await globalData.requestTTS(text, 'Serena');
  126 + }
  127 + },
  128 + {
  129 + desc: 'Dylan(北京话-男)',
  130 + voice: 'Dylan',
  131 + async createUrl(text){
  132 + return await globalData.requestTTS(text, 'Dylan');
  133 + }
  134 + },
  135 + {
  136 + desc: 'Jada(吴语-女)',
  137 + voice: 'Jada',
  138 + async createUrl(text){
  139 + return await globalData.requestTTS(text, 'Jada');
  140 + }
  141 + },
  142 + {
  143 + desc: 'Sunny(四川话-女)',
  144 + voice: 'Sunny',
  145 + async createUrl(text){
  146 + return await globalData.requestTTS(text, 'Sunny');
103 } 147 }
104 } 148 }
105 ]; 149 ];
106 }, 150 },
  151 + // TTS接口请求方法
  152 + async requestTTS(text, voice) {
  153 + return new Promise((resolve, reject) => {
  154 + const app = getApp();
  155 + app.request({
  156 + url: `${this.domain}/open-api/wx330e54aa6000516d/tts`,
  157 + data: {
  158 + text,
  159 + voice
  160 + },
  161 + success: (resp) => {
  162 + if (resp.data.code === 2000000) {
  163 + resolve(resp.data.data.url);
  164 + } else {
  165 + wx.showToast({
  166 + title: resp.data.msg || '生成失败',
  167 + icon: 'none'
  168 + });
  169 + reject(new Error(resp.data.msg || '生成失败'));
  170 + }
  171 + },
  172 + fail: (error) => {
  173 + wx.showToast({
  174 + title: '网络错误',
  175 + icon: 'none'
  176 + });
  177 + reject(error);
  178 + }
  179 + });
  180 + });
  181 + },
107 getSelectedTypeIndex(){ 182 getSelectedTypeIndex(){
108 const storageKey = 'audioTypeselectedIndex'; 183 const storageKey = 'audioTypeselectedIndex';
109 return wx.getStorageSync(storageKey); 184 return wx.getStorageSync(storageKey);
pages/index/index.js
@@ -22,36 +22,37 @@ Page({ @@ -22,36 +22,37 @@ Page({
22 async play(){ 22 async play(){
23 const text = this.data.text || this.data.placeholder; 23 const text = this.data.text || this.data.placeholder;
24 const selectedType = this.data.typeList[this.data.selectedIndex]; 24 const selectedType = this.data.typeList[this.data.selectedIndex];
25 - const url = selectedType.createUrl(text);  
26 const type = selectedType.desc; 25 const type = selectedType.desc;
27 26
28 this.resetAudio(); 27 this.resetAudio();
29 audio = wx.createInnerAudioContext({useWebAudioImplement: true}); 28 audio = wx.createInnerAudioContext({useWebAudioImplement: true});
30 audio.autoplay = true; 29 audio.autoplay = true;
  30 +
31 // 检查是否播放过 31 // 检查是否播放过
32 const audioList = app.globalData.getAudioList(); 32 const audioList = app.globalData.getAudioList();
33 - const played = audioList.find(v => v.url === url);  
34 - // 播放失败要移除缓存内容  
35 - audio.onError(err => {  
36 - app.globalData.setAudioList('delete', played.path);  
37 - wx.showModal({  
38 - title: '提示',  
39 - content: '该语音播放过但缓存失效,点击确定重新播放',  
40 - showCancel: false,  
41 - success: res => {  
42 - this.play();  
43 - }  
44 - });  
45 - });  
46 - // 播放过,使用本地文件减少请求,且从播放列表中移到最前 33 + let played = audioList.find(v => v.text === text && v.type === type);
  34 +
  35 + // 播放过,直接使用本地文件,无需检查合规性
47 if(played){ 36 if(played){
48 played.time = Date.now(); 37 played.time = Date.now();
49 app.globalData.setAudioList('delete', played.path); 38 app.globalData.setAudioList('delete', played.path);
50 app.globalData.setAudioList('add', played); 39 app.globalData.setAudioList('add', played);
  40 + // 播放失败要移除缓存内容
  41 + audio.onError(err => {
  42 + app.globalData.setAudioList('delete', played.path);
  43 + wx.showModal({
  44 + title: '提示',
  45 + content: '该语音播放过但缓存失效,点击确定重新播放',
  46 + showCancel: false,
  47 + success: res => {
  48 + this.play();
  49 + }
  50 + });
  51 + });
51 return audio.src = played.path; 52 return audio.src = played.path;
52 } 53 }
53 -  
54 - // 检查文本是否合规 54 +
  55 + // 未播放过,先检查文本是否合规
55 wx.showLoading({ 56 wx.showLoading({
56 title: '正在合成语音...', 57 title: '正在合成语音...',
57 mask: true 58 mask: true
@@ -82,27 +83,41 @@ Page({ @@ -82,27 +83,41 @@ Page({
82 duration: 2000 83 duration: 2000
83 }); 84 });
84 } 85 }
85 -  
86 - // 未播放过,先下载再播放  
87 - wx.downloadFile({  
88 - url,  
89 - success (res) {  
90 - wx.hideLoading();  
91 - audio.src = res.tempFilePath;  
92 - app.globalData.setAudioList('add', {  
93 - // 音频文本  
94 - text,  
95 - // 音频音效  
96 - type,  
97 - // 音频网络地址  
98 - url,  
99 - // 音频本地地址  
100 - path: res.tempFilePath,  
101 - // 音频创建时间  
102 - time: Date.now()  
103 - });  
104 - }  
105 - }); 86 + // 未播放过,调用TTS接口生成语音
  87 + try {
  88 + const url = await selectedType.createUrl(text);
  89 + // 下载并播放
  90 + wx.downloadFile({
  91 + url,
  92 + success (res) {
  93 + wx.hideLoading();
  94 + audio.src = res.tempFilePath;
  95 + app.globalData.setAudioList('add', {
  96 + // 音频文本
  97 + text,
  98 + // 音频音效
  99 + type,
  100 + // 音频网络地址
  101 + url,
  102 + // 音频本地地址
  103 + path: res.tempFilePath,
  104 + // 音频创建时间
  105 + time: Date.now()
  106 + });
  107 + },
  108 + fail(err) {
  109 + console.log(err);
  110 + wx.hideLoading();
  111 + wx.showToast({
  112 + title: '下载失败',
  113 + icon: 'none'
  114 + });
  115 + }
  116 + });
  117 + } catch (error) {
  118 + wx.hideLoading();
  119 + // TTS接口调用失败的错误已经在app.js中处理了
  120 + }
106 }, 121 },
107 clear(){ 122 clear(){
108 this.resetAudio(); 123 this.resetAudio();
project.config.json
1 -{  
2 - "description": "项目配置文件。",  
3 - "setting": {  
4 - "urlCheck": true,  
5 - "es6": true,  
6 - "enhance": true,  
7 - "postcss": true,  
8 - "preloadBackgroundData": false,  
9 - "minified": true,  
10 - "newFeature": true,  
11 - "coverView": true,  
12 - "nodeModules": false,  
13 - "autoAudits": false,  
14 - "showShadowRootInWxmlPanel": true,  
15 - "scopeDataCheck": false,  
16 - "uglifyFileName": false,  
17 - "checkInvalidKey": true,  
18 - "checkSiteMap": true,  
19 - "uploadWithSourceMap": true,  
20 - "compileHotReLoad": false,  
21 - "lazyloadPlaceholderEnable": false,  
22 - "useMultiFrameRuntime": true,  
23 - "useApiHook": true,  
24 - "useApiHostProcess": true,  
25 - "babelSetting": {  
26 - "ignore": [],  
27 - "disablePlugins": [],  
28 - "outputPath": ""  
29 - },  
30 - "useIsolateContext": false,  
31 - "userConfirmedBundleSwitch": false,  
32 - "packNpmManually": false,  
33 - "packNpmRelationList": [],  
34 - "minifyWXSS": true,  
35 - "disableUseStrict": false,  
36 - "minifyWXML": true,  
37 - "showES6CompileOption": false,  
38 - "useCompilerPlugins": false,  
39 - "ignoreUploadUnusedFiles": true  
40 - },  
41 - "compileType": "miniprogram",  
42 - "libVersion": "2.21.3",  
43 - "appid": "wx330e54aa6000516d",  
44 - "projectname": "text-to-audio",  
45 - "packOptions": {  
46 - "ignore": [],  
47 - "include": []  
48 - },  
49 - "editorSetting": {  
50 - "tabIndent": "insertSpaces",  
51 - "tabSize": 2  
52 - },  
53 - "condition": {} 1 +{
  2 + "description": "项目配置文件。",
  3 + "setting": {
  4 + "urlCheck": true,
  5 + "es6": true,
  6 + "enhance": true,
  7 + "postcss": true,
  8 + "preloadBackgroundData": false,
  9 + "minified": true,
  10 + "newFeature": true,
  11 + "coverView": true,
  12 + "nodeModules": false,
  13 + "autoAudits": false,
  14 + "showShadowRootInWxmlPanel": true,
  15 + "scopeDataCheck": false,
  16 + "uglifyFileName": false,
  17 + "checkInvalidKey": true,
  18 + "checkSiteMap": true,
  19 + "uploadWithSourceMap": true,
  20 + "compileHotReLoad": false,
  21 + "lazyloadPlaceholderEnable": false,
  22 + "useMultiFrameRuntime": true,
  23 + "useApiHook": true,
  24 + "useApiHostProcess": true,
  25 + "babelSetting": {
  26 + "ignore": [],
  27 + "disablePlugins": [],
  28 + "outputPath": ""
  29 + },
  30 + "useIsolateContext": false,
  31 + "userConfirmedBundleSwitch": false,
  32 + "packNpmManually": false,
  33 + "packNpmRelationList": [],
  34 + "minifyWXSS": true,
  35 + "disableUseStrict": false,
  36 + "minifyWXML": true,
  37 + "showES6CompileOption": false,
  38 + "useCompilerPlugins": false,
  39 + "ignoreUploadUnusedFiles": true
  40 + },
  41 + "compileType": "miniprogram",
  42 + "libVersion": "2.21.3",
  43 + "appid": "wx330e54aa6000516d",
  44 + "projectname": "text-to-audio",
  45 + "packOptions": {
  46 + "ignore": [],
  47 + "include": []
  48 + },
  49 + "editorSetting": {
  50 + "tabIndent": "insertSpaces",
  51 + "tabSize": 2
  52 + },
  53 + "condition": {}
54 } 54 }
55 \ No newline at end of file 55 \ No newline at end of file
project.private.config.json 0 → 100644
  1 +{
  2 + "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
  3 + "projectname": "text-to-audio",
  4 + "setting": {
  5 + "compileHotReLoad": true,
  6 + "urlCheck": false
  7 + },
  8 + "libVersion": "3.3.5"
  9 +}
0 \ No newline at end of file 10 \ No newline at end of file