当前位置: 首页 > news >正文

前端 vue3 对接科大讯飞的语音在线合成API

主要的功能就是将文本转为语音,可以播放。

看了看官方提供的demo,嗯....没看懂。最后还是去网上找的。

网上提供的案例,很多都是有局限性的,我找的那个他只能读取第一段数据,剩下的不读取。

科大讯飞的接口,返回的是一个数组,因为需要合成的文本多,所以将数据切割成多份,然后返回的。

例子:

封装了个方法,直接调用方法就可以了。

import CryptoJS from 'crypto-js';
import { Base64 } from 'js-base64';
import { message } from 'ant-design-vue';let APPID = '';
let API_SECRET = '';
let API_KEY = '';// 正确的URL
function getWebSocketUrl(apiKey, apiSecret) {let url = 'wss://tts-api.xfyun.cn/v2/tts';const host = 'tts-api.xfyun.cn';const date = new Date().toGMTString();const algorithm = 'hmac-sha256';const headers = 'host date request-line';const signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/tts HTTP/1.1`;const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);const signature = CryptoJS.enc.Base64.stringify(signatureSha);const authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;const authorization = btoa(authorizationOrigin);url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;return url;
}// 文本编码
function encodeText(text, encoding) {switch (encoding) {case 'utf16le': {const buf = new ArrayBuffer(text.length * 4);const bufView = new Uint16Array(buf);// eslint-disable-next-line no-plusplusfor (let i = 0, strlen = text.length; i < strlen; i++) {bufView[i] = text.charCodeAt(i);}return buf;}case 'buffer2Base64': {let binary = '';const bytes = new Uint8Array(text);const len = bytes.byteLength;// eslint-disable-next-line no-plusplusfor (let i = 0; i < len; i++) {binary += String.fromCharCode(bytes[i]);}return window.btoa(binary);}case 'base64&utf16le': {return this.encodeText(this.encodeText(text, 'utf16le'), 'buffer2Base64');}default: {return Base64.encode(text);}}
}// eslint-disable-next-line no-shadow
function TextToSpeechConfig(APPID, vcn, speed, volume, pitch, tte, text) {// 私有方法:生成参数对象function generateParams() {return {common: {app_id: APPID,},business: {aue: 'lame',auf: 'audio/L16;rate=16000',sfl: 1,vcn,speed,volume,pitch,bgs: 1,tte,},data: {status: 2,text: encodeText(text, tte === 'unicode' ? 'base64&utf16le' : ''), // 假设 encodeText 是一个已定义的函数},};}// 公共方法,暴露给外部调用以获取参数对象return generateParams();
}export default class TTSWSS {static _instance; // 使用下划线表示这是一个内部使用的属性text = '';vcn = '';speed = '';volume = '';pitch = '';tte = 'UTF8';ttsWS = null;static getInstance(text, vcn, speed, volume, pitch) { // 单例模式// if (!TTSWSS._instance) {//   TTSWSS._instance = new TTSWSS(text, vcn, speed, volume, pitch);// }TTSWSS._instance = new TTSWSS(text, vcn, speed, volume, pitch);return TTSWSS._instance;}constructor(text, vcn, speed, volume, pitch) {this.text = text;this.vcn = vcn;this.speed = speed;this.volume = volume;this.pitch = pitch;const url = getWebSocketUrl(API_KEY, API_SECRET);if ('WebSocket' in window) { // 构造函数时就创建websocket对象this.ttsWS = new WebSocket(url);} else if ('MozWebSocket' in window) {this.ttsWS = new WebSocket(url);} else {// alert('浏览器不支持WebSocket');message.error('浏览器不支持WebSocket');}}setText(text) {this.text = text;}setTextVCN(vcn) {this.vcn = vcn;}setSpeed(speed) {this.speed = speed;}setVolume(volume) {this.volume = volume;}// setTte(istte=false){//   this.tte = istte==true ? "unicode" : "UTF8"// }connectWebSocket() {this.ttsWS.onopen = () => {// console.log(TextToSpeechConfig(APPID, this.vcn, this.speed, this.volume, this.pitch, this.tte, this.text), '请求参数');this.ttsWS.send(JSON.stringify(TextToSpeechConfig(APPID, this.vcn, this.speed, this.volume, this.pitch, this.tte, this.text)));};this.ttsWS.onerror = () => {// console.error(e);};this.ttsWS.onclose = () => {// console.log(e);};}disconnectWebSocket() {TTSWSS._instance = null;this.ttsWS.close(); // 关闭 WebSocket 连接this.ttsWS = null; // 清空 WebSocket 对象// console.log('WebSocket disconnected');}send_newMessage = text => {const params = {common: {app_id: APPID,},business: {aue: 'lame',sfl: 1,auf: 'audio/L16;rate=16000',vcn: this.vcn,speed: this.speed,volume: this.volume,pitch: this.pitch,bgs: 1,tte: 'UTF8',},data: {status: 2,text: encodeText(text, this.tte === 'unicode' ? 'base64&utf16le' : ''),},};this.ttsWS.send(JSON.stringify(params));};getMessage() {const that = this.ttsWS;const messages = []; // 用于存储所有消息return new Promise((resolve, reject) => {that.onmessage = e => {const jsonData = JSON.parse(e.data);// 合成失败if (jsonData.code !== 0) {// eslint-disable-next-line prefer-promise-reject-errorsreject({ message: '失败', data: jsonData });return; // 退出当前处理}// 存储成功的消息messages.push({message: '成功',type: 'base64',data: jsonData.data.audio,isLastData: jsonData.data.status === 2,});// 如果接收到最后一条数据,解析所有消息并关闭连接if (jsonData.data.status === 2) {that.close();resolve(messages); // 返回所有消息}};});}TTS_close_reset() {this.ttsWS?.close();// audioPlayer.reset();}static resetInstance() {TTSWSS._instance = null; // 清空实例// console.log('TTSWSS instance has been reset.');}
}
export function setConfig(params) {APPID = params?.APPID;API_SECRET = params?.APISecret;API_KEY = params?.APIKey;
}

 使用:

import TTWss from '@/utils/voice/index.js';const audio_url = ref('');
const ttsinstance = ref(null); // 初始化为 null
const voiceLoading = ref(false); // 加载音频中function playVoice() {voiceLoading.value = true;ttsinstance?.value?.disconnectWebSocket();ttsinstance.value = null;audio_url.value = null; // 清空音频 URLconst { text } = props; // 这里是要转成语音的文字,我这个是写在组件里面的用props接收的,所以要这样写,到时候替换成自己要合成的文字就行// 创建 TTS 实例ttsinstance.value = TTWss.getInstance(text, 'xiaoyan', 50, 50, 50);// 连接 WebSocketttsinstance.value.connectWebSocket();// 获取消息ttsinstance.value.getMessage().then(result => {// 这里需要特殊处理,因为返回的数据是数组,所以要先将数组中的数据拿出来,放在每项里面的data中,然将不要先拼接,而是要先解码,然后将解码后的数据在拼接起来,这样就是一整段完整的录音文件了。const allData = result.map(it => atob(it.data));const binaryString = allData.join('');const len = binaryString.length;const bytes = new Uint8Array(len);for (let i = 0; i < len; i++) {bytes[i] = binaryString.charCodeAt(i);}const blob = new Blob([bytes], { type: 'audio/mp3' }); // 根据音频格式修改MIME类型const url = URL.createObjectURL(blob);audio_url.value = url; // 将生成的 URL 赋值给 audio_url// 这里展开后可以直接下载// const aTag = document.createElement('a');// aTag.href = url;// aTag.download = 'audio_file_name.mp3'; // 设置文件名// aTag.style.display = 'none';// document.body.appendChild(aTag);// aTag.click();// document.body.removeChild(aTag);// 播放音频playItem(url);voiceLoading.value = false;}).catch(err => {// console.log('失败', err);message.error(err);voiceLoading.value = false;});
}let currentAudio = null;
function playItem(url) {// 如果当前有音频在播放,则停止它if (currentAudio) {currentAudio.pause();currentAudio.currentTime = 0; // 可选:重置播放时间}currentAudio = new Audio(url);currentAudio.play().then(() => {// console.log('音频播放开始');}).catch(error => {// console.error('音频播放失败', error);message.error(error);});// 释放对象URL(可选)currentAudio.addEventListener('ended', () => {URL.revokeObjectURL(url);currentAudio = null; // 音频结束后清空实例});
}

HTML:

<img:src="PlayVoice"alt=""class="icon-img"@click.stop="playVoice"/>

相关文章:

前端 vue3 对接科大讯飞的语音在线合成API

主要的功能就是将文本转为语音&#xff0c;可以播放。 看了看官方提供的demo&#xff0c;嗯....没看懂。最后还是去网上找的。 网上提供的案例&#xff0c;很多都是有局限性的&#xff0c;我找的那个他只能读取第一段数据&#xff0c;剩下的不读取。 科大讯飞的接口&#xf…...

缺省参数

一、概念 在声明或定义函数时为函数的参数指定一个默认值&#xff0c;调用时&#xff0c;如果对应参数没有传参&#xff0c;则使用其默认值&#xff0c;否则使用指定的实参 void TestFunc(int a 0) {cout<<a<<endl; }int main() {TestFunc(); // 没有传参&am…...

Stable Diffusion绘画 | 来训练属于自己的模型:炼丹启动

经过前面几轮辛苦的准备工作之后&#xff0c;现在开始进入终篇的炼丹环节。 在「上传素材」页面&#xff0c;点击「开始训练」&#xff1a; 可以在「查看进度-进度」中&#xff0c;查看模型训练的整体进度&#xff1a; 求助&#xff01;&#xff01;&#xff01;操作「开始训练…...

08_OpenCV文字图片绘制

import cv2 import numpy as npimg cv2.imread(image0.jpg,1) font cv2.FONT_HERSHEY_SIMPLEXcv2.rectangle(img,(500,400),(200,100),(0,255,0),20) # 1 dst 2 文字内容 3 坐标 4 5 字体大小 6 color 7 粗细 8 line type cv2.putText(img,flower,(200,50),font,1,(0,0,250)…...

【笔记】选择题笔记+数据结构笔记

文章目录 2014 41方法一先序遍历方法二 连通分量是极大连通子图 一个连通图的生成树是一个极小连通子图 无向图的邻接表中&#xff0c;第i个顶点的度为第i个链表中的结点数 邻接表和邻接矩阵对不同的操作各有优势。 最短路径算法: 单源最短路径 已知图G(V,E)&#xff0c;我们…...

浅谈汽车智能座舱如何实现多通道音频

一、引言 随着汽车智能座舱的功能迭代发展&#xff0c;传统的 4 通道、6 通道、8 通道等音响系统难以在满足驾驶场景的需求&#xff0c;未来对于智能座舱音频质量和通道数会越来越高。接下来本文将浅析目前智能座舱如何实现音频功放&#xff0c;以及如何实现多路音频功放方案。…...

系统架构设计师教程 第13章 13.1层次式体系结构概述 笔记

13.1 层次式体系结构概述 分层式体系结构是一种最常见的架构设计方法&#xff0c;能有效地使设计简化&#xff0c;使设计的系统机构清晰&#xff0c;便于提高复用能力和产品维护能力。 层次式体系结构设计是将系统组成一个层次结构&#xff0c;每一层为上层服务&#xff0c;并…...

cnn突破一(先搞定三层反馈神经网络bpnet,c#实现)

惦记cnn很久了&#xff0c;一直搞机器视觉&#xff0c;走不出来&#xff0c;现在megauging已经实现&#xff0c;说明书也写了不少&#xff0c;该突破的突破了&#xff0c;该改进的也改进了&#xff0c;一个心病治好了&#xff0c;有空把人工智能在机器视觉上的延伸&#xff0c;…...

如何创建一个docker,给它命名,且下次重新打开它

1.创建一个新的docker并同时命名 docker run -it --name one ubuntu:18.04 /bin/bash 这时候我们已经创建了一个docker,并且命名为"one" 2.关闭当前docker exit 3.这时docker已经终止了&#xff0c;我们需要使用它要重新启动 docker start one 4.现在可以重新打…...

【D3.js in Action 3 精译_025】3.4 让 D3 数据适应屏幕(中)—— 线性比例尺的用法

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…...

Python的多线程与多进程:并发编程基础与实战

随着计算机硬件的不断发展,现代计算机通常配备多核处理器,使得在程序中同时处理多个任务成为可能。并发编程是提升程序性能、充分利用多核处理器能力的重要技术之一。在Python中,并发编程的实现主要包括多线程、多进程以及异步编程(如asyncio)。然而,由于Python的全局解释…...

HarmonyOS Next应用开发——响应式布局之媒体查询

响应式布局之媒体查询 媒体查询作为响应式设计的核心&#xff0c;在移动设备上应用十分广泛。媒体查询可根据不同设备类型或同设备不同状态修改应用的样式&#xff0c;常用于多屏幕的应用适配。媒体查询常用于下面两种场景&#xff1a; 针对设备和应用的属性信息&#xff08;…...

240 搜索二维矩阵 II

解题思路&#xff1a; \qquad 解这道题最重要的是如何利用从左到右、从上到下为升序的性质&#xff0c;快速找到目标元素。 \qquad 如果从左上角开始查找&#xff0c;如果当前matrix[i][[j] < target&#xff0c;可以向右、向下扩展元素都是升序&#xff0c;但选择哪个方向…...

jenkins微服务

如果vim进去某个文件里&#xff0c;可以按键盘的向下键查阅其它部分 记得每天备份虚拟机的项目 一.在linux安装jenkins 1.上传文件 我们采用安装包的方式安装。 先用SShclient在/usr/local/下创建jenkins文件夹&#xff0c;然后向其中导入两个包 2.安装jenkins 再在控制…...

【Kotlin基于selenium实现自动化测试】初识selenium以及搭建项目基本骨架(1)

导读大纲 1.1 Java: Selenium 首选语言1.2 配置一个强大的开发环境 1.1 Java: Selenium 首选语言 Java 是开发人员和测试人员进行自动化 Web 测试的首选 Java 和 Selenium 之间的协同作用受到各种因素的驱动,从而提高它们的有效性 为什么Java经常被认为是Selenium的首选语言 广…...

汽车追尾为什么是后车的责任?

简单点说&#xff1a;因为人后面没有长眼睛。 结论 在汽车追尾事故中&#xff0c;通常情况下后车被认为是责任方的原因在于交通法规对驾驶安全标准的约定和实践中的责任识别原则。虽然追尾事故常见地被归责于后车&#xff0c;但具体判断并不是绝对的&#xff0c;仍需综合多种…...

[运维]4.bookinfo无法部署的问题

为了拉取镜像&#xff0c;搭建了阿里云镜像仓库&#xff0c;教程见&#xff1a;K8S中基于NFS-Subdir-External-Provisioner存储组件实现的StorageClass-CSDN博客 但是bookinfo的ratings和productpage无法运行&#xff0c;部署后显示crashLoopBackOff [rootmaster ~]# kubectl…...

ACT调试pycharm报错

在运行ACT 代码时&#xff0c;根据官方readme使用命令行需要在wandb选择的时候输入3 但是&#xff0c;使用pycharm运行的时候会报错 wandb.errors.UsageError: api_key not configured (no-tty). call wandb.login(key[your_api_key]) 网上搜索都是说要注册什么key&#xf…...

记一次控件提升后,运行却不显示的Bug

.h文件 #ifndef VOLUMETOOLBTN_H #define VOLUMETOOLBTN_H#include <QToolButton> #include <memory>class VolumeToolBtn : public QToolButton { Q_OBJECTpublic:explicit VolumeToolBtn(QWidget *parent nullptr);~VolumeToolBtn() override;void initUi(); p…...

关于深度学习torch的环境配置问题

已经下好了torch在虚拟环境中&#xff0c;结果在ipynb文件中无法运行 后来在终端直接用python语句编译 发现没有问题 在编辑测试py文件 发现runcode有问题 原来是插件默认base环境 具体操作参考VS Code插件Code Runner使用python虚拟环境_coderunner怎么在虚拟环境中使用-CSD…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter

java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用&#xff08;Math::max&#xff09; 2 函数接口…...

[拓扑优化] 1.概述

常见的拓扑优化方法有&#xff1a;均匀化法、变密度法、渐进结构优化法、水平集法、移动可变形组件法等。 常见的数值计算方法有&#xff1a;有限元法、有限差分法、边界元法、离散元法、无网格法、扩展有限元法、等几何分析等。 将上述数值计算方法与拓扑优化方法结合&#…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...