index.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // import Base64 from "crypto-js/enc-base64";
  2. import CryptoJS from "crypto-js";
  3. import AudioPlayer from "./audioPlayer.esm";
  4. import Base64 from "./base64";
  5. export default class TtsVoice {
  6. appId = "";
  7. apiSecret = "";
  8. apiKey = "";
  9. ttsWS = null;
  10. status = "UNDEFINED";
  11. statusStr = "";
  12. audioPlayer = null;
  13. text = "";
  14. blob = "";
  15. cb = () => {};
  16. blobDoneCb = () => {};
  17. constructor(params) {
  18. this.appId = params.appId;
  19. this.apiSecret = params.apiSecret;
  20. this.apiKey = params.apiKey;
  21. if (params.cb) {
  22. this.cb = params.cb;
  23. }
  24. if (params.blobDoneCb) {
  25. this.blobDoneCb = params.blobDoneCb;
  26. }
  27. this.audioPlayer = new AudioPlayer();
  28. this.audioPlayer.onPlay = () => {
  29. this.changeBtnStatus("PLAY");
  30. };
  31. this.audioPlayer.onStop = (audioDatas) => {
  32. console.log(audioDatas);
  33. this.status === "PLAY" && this.changeBtnStatus("STOP");
  34. };
  35. }
  36. setText(text) {
  37. this.text = text;
  38. }
  39. changeBtnStatus(status) {
  40. this.status = status;
  41. if (status === "UNDEFINED") {
  42. this.statusStr = "立即合成";
  43. } else if (status === "CONNECTING") {
  44. this.statusStr = "正在合成";
  45. } else if (status === "PLAY") {
  46. this.statusStr = "停止播放";
  47. } else if (status === "STOP") {
  48. this.statusStr = "重新播放";
  49. }
  50. const _this = this;
  51. this.cb({ status, statusStr: _this.statusStr });
  52. }
  53. encodeText(text, type) {
  54. if (type === "unicode") {
  55. let buf = new ArrayBuffer(text.length * 4);
  56. let bufView = new Uint16Array(buf);
  57. for (let i = 0, strlen = text.length; i < strlen; i++) {
  58. bufView[i] = text.charCodeAt(i);
  59. }
  60. let binary = "";
  61. let bytes = new Uint8Array(buf);
  62. let len = bytes.byteLength;
  63. for (let i = 0; i < len; i++) {
  64. binary += String.fromCharCode(bytes[i]);
  65. }
  66. return window.btoa(binary);
  67. } else {
  68. return Base64.encode(text);
  69. }
  70. }
  71. getWebSocketUrl() {
  72. let apiKey = this.apiKey;
  73. let apiSecret = this.apiSecret;
  74. var url = "wss://tts-api.xfyun.cn/v2/tts";
  75. var host = location.host;
  76. var date = new Date().toGMTString();
  77. var algorithm = "hmac-sha256";
  78. var headers = "host date request-line";
  79. var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/tts HTTP/1.1`;
  80. var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
  81. var signature = CryptoJS.enc.Base64.stringify(signatureSha);
  82. var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
  83. var authorization = btoa(authorizationOrigin);
  84. url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
  85. return url;
  86. }
  87. stopConnect() {
  88. this.changeBtnStatus("UNDEFINED");
  89. this.ttsWS?.close();
  90. this.audioPlayer.reset();
  91. }
  92. action(text) {
  93. if (text && typeof text === "string") {
  94. if (text !== this.text) {
  95. this.changeBtnStatus("UNDEFINED");
  96. }
  97. this.setText(text);
  98. }
  99. if (this.status === "UNDEFINED") {
  100. // 开始合成
  101. this.connect();
  102. } else if (this.status === "CONNECTING") {
  103. // 停止合成
  104. this.changeBtnStatus("UNDEFINED");
  105. this.ttsWS?.close();
  106. this.audioPlayer.reset();
  107. return;
  108. } else if (this.status === "PLAY") {
  109. this.audioPlayer.stop();
  110. } else if (this.status === "STOP") {
  111. this.audioPlayer.play();
  112. }
  113. }
  114. connect() {
  115. this.ttsWS?.close();
  116. this.blob = "";
  117. this.blobDoneCb && this.blobDoneCb(this.blob);
  118. const text = this.text || "请输入文字!";
  119. const appId = this.appId;
  120. const apiKey = this.apiKey;
  121. const apiSecret = this.apiSecret;
  122. const url = this.getWebSocketUrl(apiKey, apiSecret);
  123. if ("WebSocket" in window) {
  124. this.ttsWS = new WebSocket(url);
  125. } else if ("MozWebSocket" in window) {
  126. this.ttsWS = new window.MozWebSocket(url);
  127. } else {
  128. alert("浏览器不支持WebSocket");
  129. return;
  130. }
  131. this.changeBtnStatus("CONNECTING");
  132. const _this = this;
  133. this.ttsWS.onopen = () => {
  134. _this.audioPlayer.start({
  135. autoPlay: true,
  136. sampleRate: 16000,
  137. resumePlayDuration: 1000,
  138. });
  139. _this.changeBtnStatus("PLAY");
  140. var tte = "UTF8";
  141. var params = {
  142. common: {
  143. app_id: appId,
  144. },
  145. business: {
  146. aue: "raw",
  147. auf: "audio/L16;rate=16000",
  148. vcn: "50",
  149. speed: 50,
  150. volume: 50,
  151. pitch: 50,
  152. bgs: 1,
  153. tte,
  154. },
  155. data: {
  156. status: 2,
  157. text: _this.encodeText(text, tte),
  158. },
  159. };
  160. _this.ttsWS.send(JSON.stringify(params));
  161. };
  162. this.ttsWS.onmessage = (e) => {
  163. let jsonData = JSON.parse(e.data);
  164. // 合成失败
  165. if (jsonData.code !== 0) {
  166. console.error(jsonData);
  167. _this.changeBtnStatus("UNDEFINED");
  168. return;
  169. }
  170. _this.audioPlayer.postMessage({
  171. type: "base64",
  172. data: jsonData.data.audio,
  173. isLastData: jsonData.data.status === 2,
  174. });
  175. if (jsonData.code === 0 && jsonData.data.status === 2) {
  176. _this.ttsWS.close();
  177. setTimeout(() => {
  178. _this.blob = this.audioPlayer.getAudioDataBlob("wav");
  179. _this.blobDoneCb && _this.blobDoneCb(_this.blob);
  180. });
  181. }
  182. };
  183. _this.ttsWS.onerror = (e) => {
  184. console.error(e);
  185. };
  186. _this.ttsWS.onclose = (e) => {
  187. console.log(e);
  188. };
  189. }
  190. download() {
  191. const blob = this.audioPlayer.getAudioDataBlob("wav");
  192. if (!blob) {
  193. return;
  194. }
  195. let defaultName = new Date().getTime();
  196. let node = document.createElement("a");
  197. node.href = window.URL.createObjectURL(blob);
  198. node.download = `${defaultName}.wav`;
  199. node.click();
  200. node.remove();
  201. }
  202. reset() {
  203. this.ttsWS?.close();
  204. this.text = "";
  205. this.blob = "";
  206. this.changeBtnStatus("UNDEFINED");
  207. }
  208. }