前端实现edge-tts能力,仅供学习参考

该方案参考 python包edge-tts 对逻辑进行前端实现

1、使用方法:

在需要的地方导入

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() {
  // 创建一个长度为 16 字节的 Uint8Array
  const randomBytes = new Uint8Array(16);
  // 填充数组的每个元素为一个随机的 0-255 之间的整数
  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) => {
      // 服务器会自动断开空闲超过30秒的连接
      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;
  }
}

2、方法说明

start方法:传递文本及语音配置信息
调用示例
const startSpeak = async() =>{
  let url = await start('欢迎来到中国联通智慧大脑展厅,接下来为各位来宾介绍政企大屏;',2,0,0);
}
返回示例:
blob:http://localhost:8080/f0cf900d-81ed-4e0f-bade-058726453781
入参字段描述
text需要转为语音的文字(必传)
voice转换为中文的人物(音色,传第三部分角色说明ShortName对应对象的key值)
rate语速,默认值为0
pitch音调,默认值为0
autoPlay是否自动播放,默认值true

3、角色说明

voice:声音 【0-5】

ShortName: 人物声音
[
    {
        "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"
    }
]
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值