前端 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"/>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

监控告警功能详细介绍及操作演示:运维团队的智能保障

在当今这个信息化高速发展的时代&#xff0c;运维团队面临着前所未有的挑战。为了确保系统的稳定性和高效运维&#xff0c;监控告警功能成为了运维团队不可或缺的得力助手。本文将详细介绍我们的监控告警功能&#xff0c;并结合实际操作页面进行演示&#xff0c;帮助运维团队更…

25中国烟草校园招聘面试问题总结 烟草面试全流程及面试攻略

开头附上工作招聘面试必备问题噢~~包括综合面试题、无领导小组面试题资源文件免费&#xff01;全文干货。 工作招聘无领导小组面试全攻略最常见面试题&#xff08;第一部分&#xff09;共有17章可用于国企私企合资企业工作招聘面试面试必备心得面试总结资源-CSDN文库https://d…

springboot整合seata

一、准备 docker部署seata-server 1.5.2参考&#xff1a;docker安装各个组件的命令 二、springboot集成seata 2.1 引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>&…

sql 时间交集

任务&#xff08;取时间交集&#xff09; 前端输入开始时间和结束时间&#xff0c;通过sql筛选出活动开始时间和活动结束时间再开时时间和结束时间有交集的活动 想法&#xff1a; 前后一段时间内遇到了类似取交集的&#xff0c;从网上找到了两种写法&#xff0c;再结合GPT等…

【架构】NewSQL

文章目录 NewSQLTiDBTiDB 主要组件特点使用场景安装与部署 推荐阅读 NewSQL NewSQL是一种数据库管理系统(DBMS)的类别&#xff0c;它结合了NoSQL数据库的可扩展性和传统SQL数据库的事务一致性。具体来说&#xff0c;NewSQL数据库旨在解决传统关系型数据库在处理大规模并发事务…

C# C++ 笔记

第一阶段知识总结 lunix系统操作 1、基础命令 &#xff08;1&#xff09;cd cd /[目录名] 打开指定文件目录 cd .. 返回上一级目录 cd - 返回并显示上一次目录 cd ~ 切换到当前用户的家目录 &#xff08;2&#xff09;pwd pwd 查看当前所在目录路径 pwd -L 打印当前物理…

[大语言模型-论文精读] 利用多样性进行大型语言模型预训练中重要数据的选择

[大语言模型-论文精读] 利用多样性进行大型语言模型预训练中重要数据的选择 论文信息&#xff1a; Harnessing Diversity for Important Data Selection in Pretraining Large Language Models Authors: Chi Zhang, Huaping Zhong, Kuan Zhang, Chengliang Chai, Rui Wang, X…

Techpoint(科点)—TP2912-GB 视频编码器芯片详解

写在前面 本系列文章主要讲解Techpoint(科点)—TP2912-GB 视频编码器芯片的相关知识,希望能帮助更多的同学认识和了解Techpoint(科点)—TP2912-GB 视频编码器芯片。 若有相关问题,欢迎评论沟通,共同进步。(*^▽^*) 此次架构中TP2912-GB作为视频编码器使用,下面将详细…

软件架构设计师教程 第15章 15.1-2 SOA概念及历史 笔记

15.1 SOA的相关概念 15.1.1 SOA的定义 面向服务的体系结构 (Service-Oriented Architecture,SOA)&#xff0c; 从应用角度定义&#xff0c;SOA是一种应用框架&#xff0c;关注日常的业务应用&#xff0c;将它们划分为单独的业务功能和流程。 从软件的基本原理定义&#xff…

VIRTUOSO集成电路设计工具快捷键

VIRTUOSO集成电路设计工具快捷键 原理图设计中的快捷键 i 插入元件q 显示元件编辑窗口e 进入下层ctrl e 回到上层w 画连线t 插入网络名c 拷贝m 移动 版图设计中的快捷键 Z - 放大&#xff08;Zoom In&#xff09;&#xff1a;按下 Z 键&#xff0c;然后在布局窗口中点击并拖…

linux下sudo执行的程序会有一个额外的进程的问题

当我们执行一个可执行文件时&#xff0c;有可能需要一些更高的权限&#xff0c;为此我们会用sudo ./test的方法执行&#xff0c;这时候我们通过ps aux | grep ./test去查看进程&#xff0c;会发现多出来一个 sudo ./test 的进程&#xff0c;该进程被杀死后&#xff0c;发现目标…

二叉树的迭代遍历

二叉树的迭代遍历指的是使用循环&#xff08;迭代&#xff09;的方法&#xff0c;而不是递归&#xff0c;来遍历二叉树的节点。迭代遍历通常需要使用辅助数据结构&#xff08;如栈或队列&#xff09;来帮助控制遍历的顺序。以下是几种常见的二叉树迭代遍历方法&#xff1a; 前…

【新闻转载】Storm-0501:勒索软件攻击扩展到混合云环境

icrosoft发出警告&#xff0c;勒索软件团伙Storm-0501近期调整了攻击策略&#xff0c;目前正将目标瞄准混合云环境&#xff0c;旨在全面破坏受害者的资产。 该威胁行为者自2021年首次露面&#xff0c;起初作为Sabbath勒索软件行动的分支。随后&#xff0c;他们开始分发来自Hive…

Linux中find命令详解

记录linux中find命令的详细用法。 文章目录 find命令简介基本语法常用选项-name-iname-type-size-mtime,-atime,-ctime-perm-user-group-delete-exec-printand or find --help find命令简介 find 是一个搜索目录树以查找一个文件或一组文件的程序。它遍历目录树并报告与用户规…

测试用例_边界值介绍(需求自动化生成用例方法论)

测试方法论之边界值测试&#xff1a;深入探索与实践 在软件开发过程中&#xff0c;测试是确保软件质量、稳定性和用户满意度的关键环节。在众多测试方法中&#xff0c;边界值测试&#xff08;Boundary Value Testing, BVT&#xff09;以其独特的视角和高效的覆盖率&#xff0c…

MySQL | excel数据输出insert语句

需求 在日常生产运维过程中&#xff0c;有很多需要进行人工梳理的excel数据&#xff0c;到了研发这一侧需要转为sql语句进行数据修正&#xff0c;如何输出insert插入语句&#xff1f; 方案 在空白列插入&#xff0c;选择需要的列 "INSERT INTO tab_name1 (name, desc) …

慢病中医药膳养生食疗管理微信小程序、基于微信小程序的慢病中医药膳养生食疗管理系统设计与实现、中医药膳养生食疗管理微信小程序的开发与应用(源码+文档+定制)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Ubuntu环境下字体安装

本文介绍Ubuntu环境下字体安装。 软件&#xff08;如Qt应用软件&#xff09;开发过程中经常会涉及到字体的选择&#xff0c;有时候Ubuntu环境下并没有我们想要的字体&#xff0c;本文介绍常用字体及在Ubuntu环境下如何安装。 1.常用开源字体 有些字体商用并不是免费的&#…

支持语音方式问答,支持使用重排模型进行多路召回,MaxKB知识库问答系统v1.6版本发布

2024年9月29日&#xff0c;MaxKB开源知识库问答系统正式发布v1.6版本。 在v1.6社区版中&#xff0c;应用方面&#xff0c;MaxKB新增支持语音方式问答&#xff0c;支持使用重排模型进行多路召回&#xff0c;支持自定义全局变量&#xff0c;支持OpenAI Compatible API调用格式&a…

Mac通过ssh连接工具远程登录服务器( Royal TSX安装及使用)

一、Royal TSX软件下载地址 Royal Apps 二、Royal TSX 汉化 汉化包地址&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 三、基础配置 Royal TSX 是一款基于插件的应用&#xff0c;刚安装时还不具备使用条件&#xff0c;需要进行一些基础配置 1 安装基础插件…