VUE利用一句话复刻实现变声功能

实现思路:利用语音听写实现语音输入---拿到文字后自动调用一句话复刻实现声音输出。最终效果是A输入语音能够转换成B的语音输出。

<template>
  <div class="One-container">
    <div>
      <hr/>
      <!--发音音色列表展示-->
      <el-row :gutter="10" style="border-bottom: 1px dashed grey">
        <el-col :span="12" v-for="item in itemList">
          <el-card style="height: 112px;">
            <img src="../css_assets/7.png" style="width: 15%">
            <div style="display: inline-block;vertical-align: top;margin-left: 20px;width: 120px;">
              {{ item.time.substring(item.time.length - 8) }}<br>
              <span
                  style="padding-left: 5px;padding-right: 5px;  font-size: 12px;background-color: dodgerblue;color: white">
                一句复刻
              </span>
              <span style="margin-left: 10px;font-size: 12px;color: #409EFF;">个性音色</span>
              <br>
              <el-radio v-model="radio" :label=item.number>使用</el-radio>
              <el-popconfirm
                  confirm-button-text="确定"
                  cancel-button-text="取消"
                  icon="el-icon-info"
                  icon-color="red"
                  title="确认要删除吗?"
                  @confirm="confirmDelete(item.id)">
              <span slot="reference"
                    style="background-color: red;color: white;font-size: 12px;padding-left: 5px;padding-right: 5px;cursor: pointer">
                删除
              </span>
              </el-popconfirm>
            </div>
          </el-card>
        </el-col>
      </el-row>
      <!-- 合成界面-->
      <!--  合成文本区域  -->
      <div>
        <el-input type="textarea" v-model="ttsText" rows="12"
                  style="font-family: 'Microsoft YaHei';font-size: medium;font-weight: bold"
                  :maxlength="2000" show-word-limit placeholder="请输入不超过2000个汉字的文本进行合成">
        </el-input>
      </div>
      <br>
      <el-button type="primary" size="medium" @click="voiceSend"><i class="el-icon-microphone"></i>语音输入
      </el-button>
      <el-button type="primary" size="medium" @click="clickTts" style="margin-left:10px;">合成与播放</el-button>
      <el-button type="success" size="medium" @click="clickWav">下载并保存</el-button>

    </div>
  </div>
</template>

<script>
import router from "@/js_router/router";
import * as base64 from 'js-base64'
import CryptoJS from '../js_util/crypto-js/crypto-js.js'
import AudioPlayer from '../../public/player/index.umd.js'
// 初始化录音工具,注意目录
let recorder = new Recorder("../../recorder")
recorder.onStart = () => {
  console.log("开始录音了")
}
recorder.onStop = () => {
  console.log("结束录音了")
}
// 发送中间帧和最后一帧
recorder.onFrameRecorded = ({isLastFrame, frameBuffer}) => {
  if (!isLastFrame && wsFlag) { // 发送中间帧
    const params = {
      data: {
        status: 1,
        format: "audio/L16;rate=16000",
        encoding: "raw",
        audio: toBase64(frameBuffer),
      },
    };
    wsTask.send(JSON.stringify(params)) // 执行发送
  } else {
    if (wsFlag) {
      const params = {
        data: {
          status: 2,
          format: "audio/L16;rate=16000",
          encoding: "raw",
          audio: "",
        },
      };
      console.log("发送最后一帧", params, wsFlag)
      wsTask.send(JSON.stringify(params)) // 执行发送
    }
  }
}

function toBase64(buffer) {
  var binary = "";
  var bytes = new Uint8Array(buffer);
  var len = bytes.byteLength;
  for (var i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return window.btoa(binary);
};
let wsFlag = false;
let wsTask = {};
const audioPlayer = new AudioPlayer("../../player"); // 播放器
export default {
  name: "One",
  data() {
    return {
      radio: '', // 单选项
      ttsText: "欢迎使用一句话复刻功能,让你的文字发出自己的声音。",
      itemList: [],
      sendForm: {id: 0},
      loading: false,
      user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {},
      URL: 'wss://iat-api.xfyun.cn/v2/iat',
      resultText: "",
      resultTextTemp: "",
    }
  },
  created() {
    // 查询发音因素列表
    this.selectListPage()
  },
  methods: {
    voiceSend() { // 开始语音识别要做的动作
      // 首先要调用扣费API
      this.user.ability = "语音听写能力" // 标记能力
      this.$http.post("/big/consume_balance", this.user).then(res => {
        if (res.data.code === "200") {
          // 触发父级更新user方法
          this.$emit("person_fff_user", res.data.object)
          this.resultText = "";
          this.resultTextTemp = "";
          this.wsInit();
        } else {
          this.$message.error(res.data.message)
          return false // 这个必须要做
        }
      })
      // 调用扣费API结束
    },
    // 建立ws连接
    async wsInit() {
      //  this.iat = "";
      this.$message.success("请您说出提问内容~")
      let _this = this;
      if (typeof (WebSocket) == 'undefined') {
        console.log('您的浏览器不支持ws...')
      } else {
        console.log('您的浏览器支持ws!!!')
        let reqeustUrl = await _this.getWebSocketUrlIat()
        wsTask = new WebSocket(reqeustUrl);
        // ws的几个事件,在vue中定义
        wsTask.onopen = function () {
          console.log('ws已经打开...')
          wsFlag = true
          let params = { // 第一帧数据
            common: {
              app_id: atob(_this.user.appid),
            },
            business: {
              language: "zh_cn",
              domain: "iat",
              accent: "mandarin",
              vad_eos: 2000,
              dwa: "wpgs",
            },
            data: {
              status: 0,
              format: "audio/L16;rate=16000",
              encoding: "raw",
            },
          };
          console.log("发送第一帧数据...")
          wsTask.send(JSON.stringify(params)) // 执行发送
          // 下面就可以循环发送中间帧了
          // 开始录音
          console.log("开始录音")
          recorder.start({
            sampleRate: 16000,
            frameSize: 1280,
          });
        }
        wsTask.onmessage = function (message) { // 调用第二个API 自动把语音转成文本
          console.log('收到数据===' + message.data)
          let jsonData = JSON.parse(message.data);
          if (jsonData.data && jsonData.data.result) {
            let data = jsonData.data.result;
            let str = "";
            let ws = data.ws;
            for (let i = 0; i < ws.length; i++) {
              str = str + ws[i].cw[0].w;
            }
            if (data.pgs) {
              if (data.pgs === "apd") {
                // 将resultTextTemp同步给resultText
                _this.resultText = _this.resultTextTemp;
              }
              // 将结果存储在resultTextTemp中
              _this.resultTextTemp = _this.resultText + str;
            } else {
              _this.resultText = _this.resultText + str;
            }
            _this.ttsText = _this.resultTextTemp || _this.resultText || "";
          }
          // 检测到结束或异常关闭
          if (jsonData.code === 0 && jsonData.data.status === 2) { // 拿到最终的听写文本后,我们会调用大模型
            // alert("执行了")
            recorder.stop();
            _this.$message.success("检测到您2秒没说话,自动结束识别!")
            _this.clickTts();
            wsTask.close();
            wsFlag = false
          }
          if (jsonData.code !== 0) {
            wsTask.close();
            wsFlag = false
            console.error(jsonData);
          }
        }
        // 关闭事件
        wsTask.onclose = function () {
          console.log('ws已关闭...')
        }
        wsTask.onerror = function () {
          console.log('发生错误...')
        }
      }
    }
    ,
// 获取鉴权地址与参数
    getWebSocketUrlIat() {
      return new Promise((resolve, reject) => {
        // 请求地址根据语种不同变化
        var url = this.URL;
        var host = "iat-api.xfyun.cn";
        var apiKeyName = "api_key";
        var date = new Date().toGMTString();
        var algorithm = "hmac-sha256";
        var headers = "host date request-line";
        var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`;
        var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, atob(this.user.apisecret));
        var signature = CryptoJS.enc.Base64.stringify(signatureSha);
        var authorizationOrigin =
            `${apiKeyName}="${atob(this.user.apikey)}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
        var authorization = base64.encode(authorizationOrigin);
        url = `${url}?authorization=${authorization}&date=${encodeURI(date)}&host=${host}`;
        console.log(url)
        resolve(url); // 主要是返回地址
      });
    }
    ,
    clickTts() {
      // console.log(this.user)
      // 首先要调用扣费API
      this.user.ability = "一句话复刻合成" // 标记能力
      this.$http.post("/big/consume_balance", this.user).then(res => {
        if (res.data.code === "200") {
          // 触发父级更新user方法
          this.$emit("person_fff_user", res.data.object)
          this.doWsWork(); // 调用ws链接方法
        } else {
          this.$message.error(res.data.message)
          return false // 这个必须要做
        }
      })
      // 调用扣费API结束
    },
    doWsWork() {
      const url = this.getWebSocketUrl(atob(this.user.apikey), atob(this.user.apisecret));
      if ("WebSocket" in window) {
        this.ttsWS = new WebSocket(url);
      } else if ("MozWebSocket" in window) {
        this.ttsWS = new MozWebSocket(url);
      } else {
        alert("浏览器不支持WebSocket");
        return;
      }
      this.ttsWS.onopen = (e) => {
        console.log("链接成功...")
        audioPlayer.start({
          autoPlay: true,
          sampleRate: 16000,
          resumePlayDuration: 1000
        });
        let text = this.ttsText;
        let tte = document.getElementById("tte") ? "unicode" : "UTF8";
        let params = {
          "payload": {
            "text": {
              "compress": "raw",
              "format": "plain",
              "text": this.encodeText(text, tte),
              "encoding": "utf8",
              "seq": 0,
              "status": 2
            }
          },
          "parameter": {
            "tts": {
              "vcn": "x5_clone",
              "volume": 50,
              "rhy": 0,
              "pybuffer": 1,
              "pybuf": {
                "compress": "raw",
                "format": "plain",
                "encoding": "utf8"
              },
              "pitch": 50,
              "audio": {
                "sample_rate": 16000,
                "channels": 1,
                "encoding": "raw",
                "bit_depth": 16
              },
              "speed": 50
            }
          },
          "header": {
            "res_id": this.radio,
            "app_id": atob(this.user.appid),
            "status": 2
          }
        }
        this.ttsWS.send(JSON.stringify(params));
        console.log("发送成功...")
      };
      this.ttsWS.onmessage = (e) => {
        let jsonData = JSON.parse(e.data);
        console.log("合成返回的数据" + JSON.stringify(jsonData));
        // 合成失败
        if (jsonData.header.code !== 0) {
          console.error(jsonData);
          return;
        }
        if (jsonData.hasOwnProperty("payload")) {
          audioPlayer.postMessage({
            type: "base64",
            data: jsonData.payload.audio.audio,
            isLastData: jsonData.header.status === 2,
          });
        }

        if (jsonData.header.code === 0 && jsonData.header.status === 2) {
          this.ttsWS.close();
        }
      };
      this.ttsWS.onerror = (e) => {
        console.error(e);
      };
      this.ttsWS.onclose = (e) => {
        console.log(e + "链接已关闭");
      };
    },
    getWebSocketUrl(apiKey, apiSecret) {
      let url = "wss://cbm01.cn-huabei-1.xf-yun.com/v1/private/s06a6b848";
      let host = location.host;
      let date = new Date().toGMTString();
      let algorithm = "hmac-sha256";
      let headers = "host date request-line";
      let signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v1/private/s06a6b848 HTTP/1.1`;
      let signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
      let signature = CryptoJS.enc.Base64.stringify(signatureSha);
      let authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
      let authorization = btoa(authorizationOrigin);
      url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
      return url;
    },
    // 文本编码
    encodeText(text, type) {
      if (type === "unicode") {
        let buf = new ArrayBuffer(text.length * 4);
        let bufView = new Uint16Array(buf);
        for (let i = 0, strlen = text.length; i < strlen; i++) {
          bufView[i] = text.charCodeAt(i);
        }
        let binary = "";
        let bytes = new Uint8Array(buf);
        let len = bytes.byteLength;
        for (let i = 0; i < len; i++) {
          binary += String.fromCharCode(bytes[i]);
        }
        return window.btoa(binary);
      } else {
        return base64.encode(text);
      }
    },
    clickWav() {
      const blob = audioPlayer.getAudioDataBlob("wav")
      if (!blob) {
        return
      }
      let defaultName = new Date().getTime();
      let node = document.createElement("a");
      node.href = window.URL.createObjectURL(blob);
      node.download = `${defaultName}.wav`;
      node.click();
      node.remove();
    },
    /*确认删除*/
    confirmDelete(id) {
      // console.log(id)
      this.sendForm.id = id // 主要是这个id
      this.$http.post("/timbre/delete", this.sendForm).then(res => {
        if (res.data.code === "200") {
          this.$message.success('删除成功')
        } else {
          this.$message.error('删除失败,' + res.data.message)
        }
        this.selectListPage()
      })
    },
    selectListPage() {
      this.$http.post("/timbre/list_timbre", {name: this.user.name}).then(res => {
        if (res.data.code === "200") {
          // console.log(res)
          this.itemList = res.data.object
        } else {
          this.$message.error('查询失败,' + res.data.code)
          router.push("/login")
        }
      })
    }
  }
}
</script>

<style scoped>
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/938737.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Firefly: 大模型训练工具,命令行执行训练,没有界面

文章目录 GitHub地址参数说明训练命令 Firefly: 大模型训练工具&#xff0c;支持训练Qwen2.5、Qwen2、Yi1.5、Phi-3、Llama3、Gemma、MiniCPM、Yi、Deepseek、Orion、Xverse、Mixtral-8x7B、Zephyr、Mistral、Baichuan2、Llma2、Llama、Qwen、Baichuan、ChatGLM2、InternLM、Zi…

自动驾驶AVM环视算法--python版本的俯视碗型投影模式

c语言版本和算法原理的可以查看本人的其他文档。《自动驾驶AVM环视算法--3D碗型投影模式的exe测试工具》本文档进用于展示部分代码的视线&#xff0c;获取方式网盘自行获取&#xff08;非免费介意勿下载&#xff09;&#xff1a;链接: https://pan.baidu.com/s/1STjUd87_5wDk_C…

汽车供应链 “剧变”开始,“智能感知潜在龙头”诞生

智能汽车产业链“剧变”已经开启&#xff0c;智能感知软硬件能力的权重正在不断被放大。 比如满足高阶泊车的第二代AK2超声波传感器、满足人机共驾场景需求的电子外后视镜&#xff08;CMS&#xff09;、iTOF 3D成像视觉感知&#xff08;用于舱内监控&#xff09;等新产品&…

计算机网络——期末复习(2)1-3章考试重点

第一章 概述 1、发送时延&#xff0c;其中数据帧长度为数据块大小1B8位 2、传播时延 3、 第二章 物理层 1、奈氏准则&#xff1a;理想低通信道的最高码元传输速率2W&#xff0c;W为理想低通信道的频率带宽 2、香农公式&#xff1a;&#xff0c;C为信道的极限信息传输速率&…

C++算法第九天

本篇文章我们继续学习c算法 目录 第一题 题目链接 题目展示 代码原理 暴力解法 二分解法 代码编写 第二题 题目链接 题目展示 代码原理 代码编写 重点回顾 朴素二分 非朴素二分 重点一 重点二 重点三 第一题 题目链接 153. 寻找旋转排序数组中的最小值 - 力…

HarmonyOS 实时监听与获取 Wi-Fi 信息

文章目录 摘要项目功能概述代码模块详细说明创建 Wi-Fi 状态保存对象Wi-Fi 状态监听模块获取当前 Wi-Fi 信息整合主模块 运行效果展示性能分析总结 摘要 本文展示了如何使用 HarmonyOS 框架开发一个 Demo&#xff0c;用于监听手机的 Wi-Fi 状态变化并实时获取连接的 Wi-Fi 信息…

gpu硬件架构

1.简介 NVIDIA在视觉计算和人工智能&#xff08;AI&#xff09;领域处于领先地位&#xff1b;其旗舰GPU已成为解决包括高性能计算和人工智能在内的各个领域复杂计算挑战所不可或缺的。虽然它们的规格经常被讨论&#xff0c;但很难掌握各种组件的清晰完整的图景。 这些GPU的高性…

【Qt】显示类控件:QLabel、QLCDNumber、QProgressBar、QCalendarWidget

目录 QLabel QFrame 例子&#xff1a; textFormat pixmap、scaledContents alignment wordWrap、indent、margin buddy QLCDNumber 例子&#xff1a; QTimer QProgressBar 例子&#xff1a; QCalendarWidget 例子&#xff1a; QLabel 标签控件&#xff0c;用来显示…

0001.基于springmvc简易酒店管理系统后台

一.系统架构 springmvcjsplayuimysql 二.功能特性 简单易学习&#xff0c;虽然版本比较老但是部署方便&#xff0c;tomcat环境即可启用&#xff1b;代码简洁&#xff0c;前后端代码提供可统一学习&#xff1b;祝愿您能成尽快为一位合格的程序员&#xff0c;愿世界没有BUG; …

Wallpaper壁纸制作学习记录12

角色表 创建人偶变形动画的更高级方法可以使用角色表来完成。角色表要求您使用角色的切割版本&#xff0c;将您的角色分成不同肢体/部分。这允许创建更复杂、更准确的动画&#xff0c;因为部分可以自由移动和重叠&#xff0c;而不会使图像失真。使用操控变形不一定能获得良好的…

【Python项目】基于Django的语音和背景音乐分离系统

【Python项目】基于Django的语音和背景音乐分离系统 技术简介&#xff1a;采用Python技术、Django框架、B/S结构&#xff0c;MYSQL数据库等实现。 系统简介&#xff1a;系统完成在线的音频上传&#xff0c;并且通过计算机的神经网络算法来对系统中的背景音乐和人声进行分离操作…

负载均衡oj项目:介绍

目录 项目介绍 项目演示 项目介绍 负载均衡oj是一个基于bs模式的项目。 用户使用浏览器向oj模块提交代码&#xff0c;oj模块会在所有在线的后端主机中选择一个负载情况最低的主机&#xff0c;将用户的代码提交给该主机&#xff0c;该主机进行编译运行&#xff0c;将结果返回…

【鸿睿创智开发板试用】移植OpenCV 4到OpenHarmony 4.1

目录 目录 引言 编译系统镜像 (1) 下载代码后解压SDK (2) 下载docker镜像   (3) 编译OH 编译OpenCV 下载OpenCV源代码 构建编译配置文件 执行编译命令 安装库和头文件 测试 结语 引言 最近有个需求是在基于RK3568的OpenHarmony 4.1系统中使用OpenCV&#xff0c…

【HarmonyOS之旅】HarmonyOS开发基础知识(一)

目录 1 -> 应用基础知识 1.1 -> 用户应用程序 1.2 -> 用户应用程序包结构 1.3 -> Ability 1.4 -> 库文件 1.5 -> 资源文件 1.6 -> 配置文件 1.7 -> pack.info 1.8 -> HAR 2 -> 配置文件简介 2.1 -> 配置文件的组成 3 -> 配置文…

【机器人】Graspness 端到端抓取点估计 | 环境搭建 | 模型推理测试

在复杂场景中实现抓取检测&#xff0c;Graspness是一种端到端的方法&#xff1b; 输入点云数据&#xff0c;输出抓取角度、抓取深度、夹具宽度等信息。 开源地址&#xff1a;https://github.com/rhett-chen/graspness_implementation?tabreadme-ov-file 论文地址&#xff1…

B站bilibili视频转文字字幕下载方法

本文将讲述介绍一种使用本地工具如何快速的下载B站的字幕为本地文本文件的方法。 通常获取B站字幕需要在浏览器中安装第三方插件&#xff0c;通过插件获取字幕。随着大模型&#xff0c;生成式AI&#xff0c;ChatGPT的应用&#xff0c;B站也提供了AI小助手对视频的内容进行总结…

CSS3 实现火焰-小火苗效果

创建 CSS3 火焰效果可以通过组合 CSS 动画、伪元素 和 渐变 来实现。以下是一个简单的实现步骤&#xff0c;展示如何制作动态火焰效果 1. HTML 结构 我们只需要一个简单的 div 容器&#xff1a; <div class"fire"></div>2. CSS 实现 基础样式 使用 …

新能源汽车充电需求攀升,智慧移动充电服务有哪些实际应用场景?

在新能源汽车行业迅猛发展的今天&#xff0c;智慧充电桩作为支持这一变革的关键基础设施&#xff0c;正在多个实际应用场景中发挥着重要作用。从公共停车场到高速公路服务区&#xff0c;从企业园区到住宅小区&#xff0c;智慧充电桩不仅提供了便捷的充电服务&#xff0c;还通过…

git remote -v(--verbose)显示你的 Git 仓库配置的远程仓库的详细信息

git remote -v 是一个 Git 命令&#xff0c;用于显示你的 Git 仓库配置的远程仓库的详细信息。 当你执行 git remote -v 命令时&#xff0c;你会看到类似以下的输出&#xff1a; origin https://github.com/your-username/your-repo.git (fetch) origin https://github.com…

Java Web项目部署教程简单实用

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c; 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把…