1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
| var voiceList = [{"ShortName":"zh-CN-XiaoxiaoNeural","label":"Xiaoxiao"},{"ShortName":"zh-CN-XiaoyiNeural","label":"Xiaoyi"},{"ShortName":"zh-CN-YunjianNeural","label":"Yunjian"},{"ShortName":"zh-CN-YunxiNeural","label":"Yunxi"},{"ShortName":"zh-CN-YunxiaNeural","label":"Yunxia"},{"ShortName":"zh-CN-YunyangNeural","label":"Yunyang"}]; let ws = null; let blobs = []; let audioElement = document.createElement('audio');
function sendReq(ssml, format,connectionId){ let configData = { context: { synthesis: { audio: { metadataoptions: { sentenceBoundaryEnabled: "false", wordBoundaryEnabled: "false", }, outputFormat: format, }, }, }, }; let configMessage = `X-Timestamp:${Date()}\r\n` + "Content-Type:application/json; charset=utf-8\r\n" + "Path:speech.config\r\n\r\n" + JSON.stringify(configData); console.log(`配置请求发送:${configMessage}\n`); let ssmlMessage = `X-Timestamp:${Date()}\r\n` + `X-RequestId:${connectionId}\r\n` + `Content-Type:application/ssml+xml\r\n` + `Path:ssml\r\n\r\n` + ssml; console.log(`SSML消息发送:${ssmlMessage}\n`); ws.send(configMessage, (configError) => { if (configError) { console.log(`配置请求发送失败:${connectionId}\n`); } }); ws.send(ssmlMessage, (ssmlError) => { if (ssmlError) { console.log(`SSML消息发送失败:${connectionId}\n`); } }); }
function generateRandomHex() { const randomBytes = new Uint8Array(16); window.crypto.getRandomValues(randomBytes); const hexString = Array.from(randomBytes) .map(byte => byte.toString(16).padStart(2, '0')) .join('') .toLowerCase(); return hexString; }
async function connect(ssml, format,autpPlay) { return new Promise((resolve, reject) =>{ const connectionId = generateRandomHex(); let url = `wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=6A5AA1D4EAFF4E9FB37E23D68491D6F4&ConnectionId=${connectionId}`; ws = new window.WebSocket(url); ws.onopen = () => { console.log("wsOpen"); sendReq(ssml, format,connectionId) }; ws.onclose = (code, reason) => { ws = null; blobs = []; console.log(`连接已关闭: ${reason} ${code}`); }; ws.onmessage = (message) => { if (!(message.data instanceof Blob)) { let data = message.data.toString(); if (data.includes("Path:turn.start")) { } else if (data.includes("Path:turn.end")) { for(let i=0;i<blobs.length;i++){ let contentIndex = 130; if(i == blobs.length-1){ contentIndex = 105; } blobs[i] = blobs[i].slice(contentIndex) } let result = new Blob(blobs); let url = URL.createObjectURL(result); if(autpPlay){ audioElement.pause(); audioElement.src = url; audioElement.play(); } blobs = []; ws.close(); console.log(`传输完成:${url}`); resolve(url); } } else if (message.data instanceof Blob) { console.log("收到信号了b......",message.data) blobs.push(message.data) } }; ws.onerror = (error) => { console.log(`连接失败: ${error}`); }; }) }
export async function start(text,voice=1,rate = 0,pitch=0,autpPlay=true) { if(text){ let SSML = ""; console.log("text",text); console.log("voice",voiceList[voice].ShortName); console.log("rate",rate); console.log("pitch",pitch); SSML = ` <speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US"> <voice name="${voiceList[voice].ShortName}"> <prosody rate="${rate}%" pitch="${pitch}%"> ${text} </prosody> </voice> </speak>`; console.log(SSML); let format = "audio-24khz-48kbitrate-mono-mp3"; let result = await connect(SSML, format,autpPlay).then(result => { console.log('Received result:', result); return result; }); return result; } }
|