自动发帖软件

标题: AIWROK软件自动文字转语音例子 [打印本页]

作者: 发帖软件    时间: 2 小时前
标题: AIWROK软件自动文字转语音例子
AIWROK软件自动文字转语音例子
AIWROK软件自动文字转语音例子 群发软件发帖工具
  1. /**
  2. * ============================================================================
  3. *  AIWROK TTS 文件⑤:text-to-speech.cn(微软 Azure 神经网络音色·免登录)
  4. * ============================================================================
  5. *  来源:https://www.text-to-speech.cn/
  6. *        网站后端代理微软 Azure TTS,音色和 Edge-TTS 完全一样
  7. *        但用 HTTP POST 代替 WebSocket,AIWROK 兼容性更好
  8. *
  9. *  音质:⭐⭐⭐⭐⭐  和豆包桃子一样自然,无机器感
  10. *
  11. *  中文女声推荐:
  12. *    zh-CN-XiaoxiaoNeural   晓晓  最自然(默认)
  13. *    zh-CN-XiaochenNeural   晓辰  年轻女声
  14. *    zh-CN-XiaorouNeural    晓柔  温柔
  15. *    zh-CN-XiaohanNeural    晓涵  温柔
  16. *    zh-CN-XiaomengNeural   晓梦  年轻女声
  17. *    zh-CN-XiaoyiNeural     晓伊  女童
  18. *  中文男声:
  19. *    zh-CN-YunxiNeural      云希  年轻男声
  20. *    zh-CN-YunjianNeural    云健  成年男声
  21. *
  22. *  用法:
  23. *    ttsWeb("你好呀");
  24. *    ttsWeb("你好呀", "zh-CN-XiaorouNeural");  // 晓柔
  25. *
  26. *  文件保存:/sdcard/Pictures/aiwrok_tts/
  27. * ============================================================================
  28. */

  29. importClass(Packages.android.media.MediaPlayer);
  30. importClass(Packages.java.io.File);
  31. importClass(Packages.java.io.FileInputStream);
  32. importClass(Packages.java.io.FileOutputStream);
  33. importClass(Packages.java.lang.Thread);
  34. importClass(Packages.javax.net.ssl.HttpsURLConnection);
  35. importClass(Packages.java.net.URL);
  36. importClass(Packages.java.io.BufferedReader);
  37. importClass(Packages.java.io.InputStreamReader);
  38. importClass(Packages.java.io.OutputStream);

  39. var WEB_CFG = {
  40.     cacheDir: "/sdcard/Pictures/aiwrok_tts/",
  41.     // 默认音色:晓晓(最自然)
  42.     voice: "zh-CN-XiaoxiaoNeural",
  43.     // 语言值必须是中文全称(网站要求)
  44.     language: "中文(普通话,简体)",
  45.     // 音质:audio-16khz-32kbitrate-mono-mp3 / audio-24khz-160kbitrate-mono-mp3 / audio-48khz-192kbitrate-mono-mp3
  46.     kbitrate: "audio-24khz-160kbitrate-mono-mp3",
  47.     // 语速 / 音调
  48.     rate: "0",
  49.     pitch: "0",
  50.     // 音量(0-100)
  51.     volume: "75",
  52.     // 情感:0=默认
  53.     style: "0",
  54.     styledegree: "1",
  55.     // 其他参数
  56.     role: "0",
  57.     silence: "",
  58.     predict: "0",
  59.     // 验证码(网站固定值)
  60.     yzm: "202410170001",
  61.     // 单次最大字数
  62.     maxLen: 200,
  63.     // 两个站点(互为备用)
  64.     sites: [
  65.         {
  66.             page: "https://www.text-to-speech.cn/",
  67.             api: "https://www.text-to-speech.cn/getSpeek.php"
  68.         },
  69.         {
  70.             page: "http://new.text-to-speech.cn/tts/",
  71.             api: "http://new.text-to-speech.cn/wp-content/plugins/speech-tts/getSpeek.php"
  72.         }
  73.     ]
  74. };

  75. function ensureDir() {
  76.     var d = new File(WEB_CFG.cacheDir);
  77.     if (!d.exists()) d.mkdirs();
  78. }

  79. function isValidAudio(p) {
  80.     try {
  81.         var f = new File(p);
  82.         if (!f.exists() || f.length() < 512) return false;
  83.         var fis = new FileInputStream(p);
  84.         var h = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 3);
  85.         var r = fis.read(h);
  86.         fis.close();
  87.         if (r < 2) return false;
  88.         var b0 = h[0] & 0xFF, b1 = h[1] & 0xFF;
  89.         if (b0 == 0x49 && b1 == 0x44) return true;
  90.         if (b0 == 0xFF && (b1 & 0xE0) == 0xE0) return true;
  91.         return false;
  92.     } catch (e) { return false; }
  93. }

  94. function splitText(text, maxLen) {
  95.     var segs = [];
  96.     if (!text) return segs;
  97.     text = String(text);
  98.     var parts = text.split(/([。!?!?,,;;\n\r])/);
  99.     var cur = "";
  100.     for (var i = 0; i < parts.length; i++) {
  101.         var p = parts[i];
  102.         if (p == null) continue;
  103.         if (cur.length + p.length > maxLen) {
  104.             if (cur.length > 0) { segs.push(cur); cur = ""; }
  105.             if (p.length > maxLen) {
  106.                 for (var j = 0; j < p.length; j += maxLen) {
  107.                     segs.push(p.substring(j, Math.min(j + maxLen, p.length)));
  108.                 }
  109.             } else { cur = p; }
  110.         } else { cur += p; }
  111.     }
  112.     if (cur.length > 0) segs.push(cur);
  113.     return segs;
  114. }

  115. /**
  116. * 从页面 HTML 中提取 nonce 和 token
  117. */
  118. function getPageAuth(pageUrl) {
  119.     var conn = null;
  120.     var reader = null;
  121.     try {
  122.         var url = new URL(pageUrl);
  123.         conn = url.openConnection();
  124.         conn.setRequestMethod("GET");
  125.         conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
  126.         conn.setConnectTimeout(15000);
  127.         conn.setReadTimeout(30000);
  128.         conn.setUseCaches(false);

  129.         var code = conn.getResponseCode();
  130.         if (code != 200) return null;

  131.         reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
  132.         var sb = new java.lang.StringBuilder();
  133.         var line;
  134.         while ((line = reader.readLine()) != null) {
  135.             sb.append(line).append("\n");
  136.         }

  137.         var html = String(sb.toString());

  138.         // 提取 token(网站在 script 标签里用 const token = 'xxx' 定义)
  139.         var token = "";
  140.         var tokenPatterns = [
  141.             /const\s+token\s*=\s*['"]([a-zA-Z0-9:_\-]+)['"]/i,
  142.             /var\s+token\s*=\s*['"]([a-zA-Z0-9:_\-]+)['"]/i,
  143.             /["']token["']\s*[:=]\s*["']([a-zA-Z0-9:_\-]+)["']/i
  144.         ];
  145.         for (var i = 0; i < tokenPatterns.length; i++) {
  146.             var m = html.match(tokenPatterns[i]);
  147.             if (m && m[1]) { token = m[1]; break; }
  148.         }

  149.         // 站点2可能需要 nonce(WordPress)
  150.         var nonce = "";
  151.         var noncePatterns = [
  152.             /["']nonce["']\s*[:=]\s*["']([a-f0-9]{6,32})["']/i,
  153.             /nonce\s*[:=]\s*["']([a-f0-9]{6,32})["']/i
  154.         ];
  155.         for (var i = 0; i < noncePatterns.length; i++) {
  156.             var m = html.match(noncePatterns[i]);
  157.             if (m && m[1]) { nonce = m[1]; break; }
  158.         }

  159.         return { nonce: nonce, token: token };
  160.     } catch (e) {
  161.         printl("  ↳ 获取页面认证信息失败: " + e.message);
  162.         return null;
  163.     } finally {
  164.         if (reader != null) { try { reader.close(); } catch (e) {} }
  165.         if (conn != null) { try { conn.disconnect(); } catch (e) {} }
  166.     }
  167. }

  168. /**
  169. * POST 到 getSpeek.php 获取 TTS 音频
  170. */
  171. function ttsApiCall(apiUrl, text, voice, auth) {
  172.     var conn = null;
  173.     var reader = null;
  174.     var os = null;
  175.     try {
  176.         // 构建表单数据(参数名和值必须与网站完全一致)
  177.         var params = "language=" + encodeURIComponent(WEB_CFG.language) +
  178.                      "&voice=" + encodeURIComponent(voice) +
  179.                      "&text=" + encodeURIComponent(text) +
  180.                      "&role=" + WEB_CFG.role +
  181.                      "&style=" + WEB_CFG.style +
  182.                      "&rate=" + WEB_CFG.rate +
  183.                      "&pitch=" + WEB_CFG.pitch +
  184.                      "&kbitrate=" + encodeURIComponent(WEB_CFG.kbitrate) +
  185.                      "&silence=" + WEB_CFG.silence +
  186.                      "&styledegree=" + WEB_CFG.styledegree +
  187.                      "&volume=" + WEB_CFG.volume +
  188.                      "&predict=" + WEB_CFG.predict +
  189.                      "&user_id=" +
  190.                      "&yzm=" + WEB_CFG.yzm +
  191.                      "&replice=1";

  192.         if (auth && auth.nonce) params += "&nonce=" + auth.nonce;
  193.         if (auth && auth.token) params += "&token=" + encodeURIComponent(auth.token);

  194.         var url = new URL(apiUrl);
  195.         conn = url.openConnection();
  196.         conn.setRequestMethod("POST");
  197.         conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
  198.         conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
  199.         conn.setRequestProperty("Accept", "application/json, text/plain, */*");
  200.         conn.setRequestProperty("Referer", apiUrl.replace("/getSpeek.php", "/"));
  201.         conn.setDoOutput(true);
  202.         conn.setDoInput(true);
  203.         conn.setConnectTimeout(15000);
  204.         conn.setReadTimeout(30000);
  205.         conn.setUseCaches(false);

  206.         // 发送请求体(Rhino 的 JS String 没有 getBytes,要用 java.lang.String)
  207.         os = conn.getOutputStream();
  208.         var jParams = new java.lang.String(params);
  209.         os.write(jParams.getBytes("UTF-8"));
  210.         os.flush();
  211.         os.close();
  212.         os = null;

  213.         var code = conn.getResponseCode();
  214.         var inputStream;
  215.         if (code >= 400) {
  216.             inputStream = conn.getErrorStream();
  217.         } else {
  218.             inputStream = conn.getInputStream();
  219.         }

  220.         if (inputStream == null) return null;

  221.         reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
  222.         var sb = new java.lang.StringBuilder();
  223.         var line;
  224.         while ((line = reader.readLine()) != null) {
  225.             sb.append(line);
  226.         }

  227.         var response = String(sb.toString());
  228.         return { code: code, body: response };
  229.     } catch (e) {
  230.         printl("  ↳ API 请求失败: " + e.message);
  231.         return null;
  232.     } finally {
  233.         if (os != null) { try { os.close(); } catch (e) {} }
  234.         if (reader != null) { try { reader.close(); } catch (e) {} }
  235.         if (conn != null) { try { conn.disconnect(); } catch (e) {} }
  236.     }
  237. }

  238. /**
  239. * 从 JSON 响应中提取下载 URL
  240. */
  241. function parseDownloadUrl(jsonStr) {
  242.     try {
  243.         // Rhino 的 JSON.parse 可能不可用,用正则提取
  244.         // 格式: {"code":200,"download":"https://...mp3","srt":""}
  245.         var codeMatch = jsonStr.match(/"code"\s*:\s*(\d+)/);
  246.         if (!codeMatch || codeMatch[1] != "200") {
  247.             printl("  ↳ API 返回错误: " + jsonStr.substring(0, 200));
  248.             return null;
  249.         }

  250.         var dlMatch = jsonStr.match(/"download"\s*:\s*"([^"]+)"/);
  251.         if (dlMatch && dlMatch[1]) return dlMatch[1];

  252.         return null;
  253.     } catch (e) {
  254.         printl("  ↳ 解析响应失败: " + e.message);
  255.         return null;
  256.     }
  257. }

  258. /**
  259. * 下载 MP3 文件(带重试)
  260. */
  261. function downloadMp3(url, savePath) {
  262.     for (var retry = 0; retry < 3; retry++) {
  263.         try {
  264.             new File(savePath)["delete"]();
  265.             var client = new okHttp();
  266.             client.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
  267.             client.downloadFile(url, savePath);
  268.             if (isValidAudio(savePath)) return true;
  269.         } catch (e) {
  270.             Thread.sleep(1000);
  271.         }
  272.         if (retry < 2) Thread.sleep(1000);
  273.     }
  274.     return false;
  275. }

  276. /**
  277. * 合成单段文字(多次重试,401时等待后刷新token)
  278. */
  279. function synthSegment(text, voice, savePath) {
  280.     for (var retry = 0; retry < 4; retry++) {
  281.         for (var s = 0; s < WEB_CFG.sites.length; s++) {
  282.             var site = WEB_CFG.sites[s];

  283.             // 1. 每次都重新获取 token(token 会过期)
  284.             var auth = getPageAuth(site.page);
  285.             if (!auth) {
  286.                 continue;
  287.             }

  288.             // 2. 调用 API
  289.             var resp = ttsApiCall(site.api, text, voice, auth);
  290.             if (!resp || resp.code != 200) {
  291.                 continue;
  292.             }

  293.             // 3. 解析下载 URL
  294.             var dlUrl = parseDownloadUrl(resp.body);
  295.             if (!dlUrl) {
  296.                 // 401 错误:token 过期,等 3 秒再试
  297.                 if (resp.body && resp.body.indexOf("401") >= 0) {
  298.                     printl("  ↳ token 过期,等待 3 秒后重试...");
  299.                     Thread.sleep(3000);
  300.                 }
  301.                 continue;
  302.             }

  303.             // 4. 下载 MP3
  304.             if (downloadMp3(dlUrl, savePath)) {
  305.                 return true;
  306.             }
  307.         }
  308.         // 重试前等待
  309.         if (retry < 3) {
  310.             var wait = (retry + 1) * 2000; // 2s, 4s, 6s 递增
  311.             printl("  ↳ 第 " + (retry + 1) + " 次重试(等待 " + (wait / 1000) + "s)...");
  312.             Thread.sleep(wait);
  313.         }
  314.     }

  315.     return false;
  316. }

  317. /**
  318. * 文字转语音(主函数)
  319. * @param text  文本
  320. * @param voice 音色名(默认晓晓)
  321. * @return 成功段数
  322. */
  323. function ttsWeb(text, voice) {
  324.     voice = voice || WEB_CFG.voice;
  325.     if (!text || String(text).length === 0) { printl("⚠️ 文本为空"); return 0; }
  326.     ensureDir();

  327.     var segs = splitText(text, WEB_CFG.maxLen);
  328.     printl("&#128266; [text-to-speech.cn] 音色:" + voice + " 共 " + segs.length + " 段");

  329.     var okCount = 0;
  330.     for (var i = 0; i < segs.length; i++) {
  331.         var seg = segs[i];
  332.         var tag = seg.substring(0, 10).replace(/[\\/:*?"<>|]/g, "_");
  333.         var savePath = WEB_CFG.cacheDir + "web_" + voice.replace(/[^a-zA-Z0-9]/g, "").substring(0, 15) +
  334.                        "_" + (i + 1) + "_" + Date.now() + ".mp3";

  335.         printl("[" + (i + 1) + "/" + segs.length + "] " + seg.substring(0, 25) + "...");

  336.         if (synthSegment(seg, voice, savePath)) {
  337.             var f = new File(savePath);
  338.             printl("  ✅ " + f.length() + " bytes");
  339.             okCount++;
  340.         } else {
  341.             printl("  ❌ 合成失败");
  342.         }

  343.         if (i < segs.length - 1) Thread.sleep(1500);
  344.     }

  345.     printl("✅ 完成 (" + okCount + "/" + segs.length + " 段)");
  346.     return okCount;
  347. }

  348. // ======================== 演示 ========================

  349. function demo() {
  350.     printl("========================================");
  351.     printl("  ⑤ text-to-speech.cn TTS 演示");
  352.     printl("  微软 Azure 神经网络音色·免登录");
  353.     printl("========================================");

  354.     // 测试短句
  355.     printl("\n【晓晓 - 默认】");
  356.     ttsWeb("你好呀,我是晓晓,一个温暖文静的声音。今天的风很温柔,适合慢慢聊天。");

  357.     Thread.sleep(500);

  358.     printl("\n【晓柔 - 温柔】");
  359.     ttsWeb("你好,我是晓柔,说话轻轻的,像在你耳边讲故事一样。", "zh-CN-XiaorouNeural");

  360.     Thread.sleep(500);

  361.     printl("\n【晓辰 - 年轻女声】");
  362.     ttsWeb("你好,我是晓辰,一个阳光的年轻女孩声音,很高兴认识你哦。", "zh-CN-XiaochenNeural");

  363.     Thread.sleep(500);

  364.     printl("\n【云希 - 年轻男声】");
  365.     ttsWeb("你好,我是云希,一个年轻男声,希望你喜欢。", "zh-CN-YunxiNeural");

  366.     printl("\n演示结束。MP3 都在 /sdcard/Pictures/aiwrok_tts/ 目录下。");
  367. }

  368. // ======================== 裕泰茶馆全文朗读 ========================

  369. function readArticle() {
  370.     var ARTICLE = [
  371.         "光绪末年,北京的裕泰茶馆的秋阳总斜着扫过八仙桌,茶碗里的茉莉浮浮沉沉。掌柜王利发攥着布巾擦柜台,眼睛瞅着门,耳朵却撒得满茶馆都是,这地方,三教九流聚聚着,闲话里藏着买卖,叹气里裹着门路。",
  372.         "这天晌午,绸缎庄的秦二爷一屁股坐下,茶没端就叹气:晦气,新到的苏杭绸缎压了半仓,这年头兵荒马乱的,谁还有闲钱做什么新衣裳呀,再撂俩月,怕是要生霉喂虫子去了。",
  373.         "话音刚落,跑码头的李把头掀帘子进来,灌了大碗凉茶:您那愁?我从南边运来了百斤好药材,找不着下家,码头堆一天扣一天的钱,再耗下去,我得把裤腰带当出去算了。",
  374.         "角落开饭庄的张胖子也拍着大腿搭腔:都别喊苦了,我那饭庄刚换了金字招牌,想给伙计做身整齐的衣裳,还想寻点正经好羊肉做招牌菜,跑了半月,没着落呀,眼看就要开张了,急得一满嘴是泡。",
  375.         "王利发手里擦着茶碗,慢悠悠的插了话:唉,我当多大的难处,秦二爷,您那绸缎,张掌柜正愁没处买,张掌柜您要的羊肉,街西口卖肉的马大哥,昨儿还在我这念叨囤了好货出不去,李把头您那药材,对过德仁掌柜,前儿还托我打听南边的新货哪有。",
  376.         "三句话落地,仨人先是愣了神,紧跟着眼睛都亮了,当场就凑到一桌子,您让几分利,我担点运费,没半个时辰,三桩买卖全说妥了,秦二爷清了库存,李把头卸包袱,张胖子凑了开张的家当,个个喜颜开,临走每人都塞给王利发一块碎银子,说是喝茶的钱。",
  377.         "靠窗坐着的孙秀才,穿得打补丁长衫,天天来茶馆蹭座位抄书,张嘴就是寒窗苦读,凭真才实学立身,这会儿他推了推破眼镜,酸溜溜的说,张掌柜,您这算是什么本事?不过是耍耍嘴皮子牵线,投机取巧罢了,哪像我,日日苦读,靠的是真本事吃饭,才踏实呀。",
  378.         "王利发也不恼,给他添了半碗热茶,笑着说:孙先生,您努力是真的,可您细想哦,这天下的本事,您学得完吗?您既不会织绸缎,也不会跑码头,边挑羊肉都不会,总不能样样都自己攥在手里吧?",
  379.         "我这小茶馆,赚得不光是茶钱,也是一个方便的钱,谁手里有闲货,谁心里有难处,我听着记着,到时候两头凑到一块儿,大家都省了力气,我也落个中间费,您天天闷头抄书,总想着自己成天大的事儿,可连街对面的药铺缺啥药材都不知道,这努力有啥用,不就走歪了跑。",
  380.         "孙秀才张了张嘴,半天没说出话来。",
  381.         "转眼过了半年,秦二爷的绸缎庄开了分号,李把头的货栈雇了仨伙计,张胖子的饭庄成了京城小有名气的馆子。仨人逢年过节都来给王利发送礼,裕泰茶馆也翻修了门面,比原先敞亮不少。",
  382.         "只有孙秀才,还是天天抱着旧书抄,嘴里念叨着怀才不遇,长衫上的补丁反倒又多了两块。",
  383.         "后来有人跟王利发讨教发财的诀窍,他总摆着手笑:哪有什么诀窍?我既没金山也没银矿,就是认得人多,知道谁有啥、谁缺啥,给人搭个桥罢了。这世上的钱,从来不是攥在自己手里才叫钱。你要是能成了那搭桥的人,各色价值都得从你这儿过,财富哪里还用追?它自然就往你这儿流。",
  384.         "茶客们听了,有的点头,有的撇嘴,茶碗盖一掀,茉莉花香又飘满了一屋子。"
  385.     ];

  386.     printl("========================================");
  387.     printl("  裕泰茶馆的搭桥经 · text-to-speech.cn");
  388.     printl("  音色:晓晓(微软 Azure 神经网络)");
  389.     printl("========================================");
  390.     printl("文章共 " + ARTICLE.length + " 段");

  391.     ensureDir();
  392.     var okCount = 0;
  393.     var voice = "zh-CN-XiaoxiaoNeural";
  394.     var failed = []; // 记录失败的段落

  395.     for (var i = 0; i < ARTICLE.length; i++) {
  396.         var seg = ARTICLE[i];
  397.         var savePath = WEB_CFG.cacheDir + "web_xiaoxiao_seg" + (i + 1) + ".mp3";

  398.         // 已下载的跳过
  399.         var existFile = new File(savePath);
  400.         if (existFile.exists() && existFile.length() > 512) {
  401.             printl("\n[" + (i + 1) + "/" + ARTICLE.length + "] 已存在,跳过 (" + existFile.length() + " bytes)");
  402.             okCount++;
  403.             continue;
  404.         }

  405.         printl("\n[" + (i + 1) + "/" + ARTICLE.length + "] " + seg.substring(0, 25) + "...");

  406.         if (synthSegment(seg, voice, savePath)) {
  407.             var f = new File(savePath);
  408.             printl("  ✅ " + f.length() + " bytes");
  409.             okCount++;
  410.         } else {
  411.             printl("  ❌ 合成失败,稍后重试");
  412.             failed.push(i);
  413.         }

  414.         // 段间间隔 2 秒(避免频繁请求导致 token 过期)
  415.         if (i < ARTICLE.length - 1) Thread.sleep(2000);
  416.     }

  417.     // 第二轮:重试失败的段落
  418.     if (failed.length > 0) {
  419.         printl("\n========================================");
  420.         printl("  第二轮:重试 " + failed.length + " 个失败段落");
  421.         printl("========================================");

  422.         for (var r = 0; r < failed.length; r++) {
  423.             var idx = failed[r];
  424.             var seg = ARTICLE[idx];
  425.             var savePath = WEB_CFG.cacheDir + "web_xiaoxiao_seg" + (idx + 1) + ".mp3";

  426.             printl("\n[重试 " + (r + 1) + "/" + failed.length + "] 段落 " + (idx + 1) + ": " + seg.substring(0, 25) + "...");

  427.             // 等待 5 秒让 token 冷却
  428.             Thread.sleep(5000);

  429.             if (synthSegment(seg, voice, savePath)) {
  430.                 var f = new File(savePath);
  431.                 printl("  ✅ " + f.length() + " bytes");
  432.                 okCount++;
  433.             } else {
  434.                 printl("  ❌ 仍然失败");
  435.             }
  436.         }
  437.     }

  438.     printl("\n========================================");
  439.     printl("  完成 (" + okCount + "/" + ARTICLE.length + " 段)");
  440.     printl("  MP3 保存在: " + WEB_CFG.cacheDir);
  441.     printl("========================================");
  442. }

  443. // 运行:取消注释你想执行的函数

  444. // demo();           // 演示 4 种音色
  445. readArticle();       // 朗读裕泰茶馆全文
复制代码







欢迎光临 自动发帖软件 (http://www.fatiegongju.com/) Powered by Discuz! X3.2