当前位置:   article > 正文

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

ttsedge

该方案参考 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;
  }
}

  • 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

2、方法说明

start方法:传递文本及语音配置信息
调用示例
const startSpeak = async() =>{
  let url = await start('欢迎来到中国联通智慧大脑展厅,接下来为各位来宾介绍政企大屏;',2,0,0);
}
返回示例:
blob:http://localhost:8080/f0cf900d-81ed-4e0f-bade-058726453781
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
入参字段描述
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"
    }
]
  • 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
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/364364
推荐阅读
  

闽ICP备14008679号