科大讯飞webAPI文字转语音

可能会遇到的坑

原文链接

自行了解 js webWorker线程

我的目录结构

 TTS.js代码

// 科大讯飞 文字->语音
import {downloadPCM, downloadWAV} from '@/common/download.js'
import CryptoJS from 'crypto-js'
import { Base64 } from 'js-base64'
var transWorker = new Worker('../common/transcode.worker.js')
//测试完成后需要改成 var transWorker = new Worker('transcode.worker.js')
//APPID,APISecret,APIKey在控制台-我的应用-语音合成(流式版)页面获取
const APPID = ''
const API_SECRET = ''
const API_KEY = ''
function getWebsocketUrl() {return new Promise((resolve, reject) => {var apiKey = API_KEYvar apiSecret = API_SECRETvar url = 'wss://tts-api.xfyun.cn/v2/tts'var host = location.hostvar date = new Date().toGMTString()var algorithm = 'hmac-sha256'var headers = 'host date request-line'var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/tts HTTP/1.1`var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)var signature = CryptoJS.enc.Base64.stringify(signatureSha)var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`var authorization = btoa(authorizationOrigin)url = `${url}?authorization=${authorization}&date=${date}&host=${host}`resolve(url)})
}
export default class TTSRecorder {constructor({speed = 20,voice = 50,pitch = 50,voiceName = 'xiaoyan',appId = APPID,text = '',tte = 'UTF8',defaultText = '请输入您要合成的文本',} = {}) {this.speed = speedthis.voice = voicethis.pitch = pitchthis.voiceName = voiceNamethis.text = textthis.tte = ttethis.defaultText = defaultTextthis.appId = appIdthis.audioData = []this.rawAudioData = []this.audioDataOffset = 0this.status = 'init'transWorker.onmessage = (e) => {this.audioData.push(...e.data.data)this.rawAudioData.push(...e.data.rawAudioData)}}// 修改录音听写状态setStatus(status) {this.onWillStatusChange && this.onWillStatusChange(this.status, status)this.status = status}// 设置合成相关参数setParams({ speed, voice, pitch, text, voiceName, tte }) {speed !== undefined && (this.speed = speed)voice !== undefined && (this.voice = voice)pitch !== undefined && (this.pitch = pitch)text && (this.text = text)tte && (this.tte = tte)voiceName && (this.voiceName = voiceName)this.resetAudio()}// 连接websocketconnectWebSocket() {this.setStatus('ttsing')return getWebsocketUrl().then(url => {let ttsWSif ('WebSocket' in window) {ttsWS = new WebSocket(url)} else if ('MozWebSocket' in window) {ttsWS = new MozWebSocket(url)} else {alert('浏览器不支持WebSocket')return}this.ttsWS = ttsWSttsWS.onopen = e => {this.webSocketSend()this.playTimeout = setTimeout(() => {this.audioPlay()}, 1000)}ttsWS.onmessage = e => {this.result(e.data)}ttsWS.onerror = e => {clearTimeout(this.playTimeout)this.setStatus('errorTTS')alert('WebSocket报错,请f12查看详情')console.error(`详情查看:${encodeURI(url.replace('wss:', 'https:'))}`)}ttsWS.onclose = e => {console.log(e)}})}// 处理音频数据transToAudioData(audioData) {}// websocket发送数据webSocketSend() {var params = {common: {app_id: this.appId, // APPID},business: {aue: 'raw',auf: 'audio/L16;rate=16000',vcn: this.voiceName,speed: this.speed,volume: this.voice,pitch: this.pitch,bgs: 1,tte: this.tte,},data: {status: 2,text: this.encodeText(this.text || this.defaultText,this.tte === 'unicode' ? 'base64&utf16le' : '')},}this.ttsWS.send(JSON.stringify(params))}encodeText (text, encoding) {switch (encoding) {case 'utf16le' : {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)}return buf}case 'buffer2Base64': {let binary = ''let bytes = new Uint8Array(text)let len = bytes.byteLengthfor (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)}}}// websocket接收数据的处理result(resultData) {let jsonData = JSON.parse(resultData)// 合成失败if (jsonData.code !== 0) {alert(`合成失败: ${jsonData.code}:${jsonData.message}`)console.error(`${jsonData.code}:${jsonData.message}`)this.resetAudio()return}transWorker.postMessage(jsonData.data.audio)// window.postMessage(jsonData.data.audio)if (jsonData.code === 0 && jsonData.data.status === 2) {this.ttsWS.close()}}// 重置音频数据resetAudio() {this.audioStop()this.setStatus('init')this.audioDataOffset = 0this.audioData = []this.rawAudioData = []this.ttsWS && this.ttsWS.close()clearTimeout(this.playTimeout)}// 音频初始化audioInit() {let AudioContext = window.AudioContext || window.webkitAudioContextif (AudioContext) {this.audioContext = new AudioContext()this.audioContext.resume()this.audioDataOffset = 0} }// 音频播放audioPlay() {this.setStatus('play')let audioData = this.audioData.slice(this.audioDataOffset)this.audioDataOffset += audioData.lengthlet audioBuffer = this.audioContext.createBuffer(1, audioData.length, 22050)let nowBuffering = audioBuffer.getChannelData(0)if (audioBuffer.copyToChannel) {audioBuffer.copyToChannel(new Float32Array(audioData), 0, 0)} else {for (let i = 0; i < audioData.length; i++) {nowBuffering[i] = audioData[i]}}let bufferSource = this.bufferSource = this.audioContext.createBufferSource()bufferSource.buffer = audioBufferbufferSource.connect(this.audioContext.destination)bufferSource.start()bufferSource.onended = event => {if (this.status !== 'play') {return}if (this.audioDataOffset < this.audioData.length) {this.audioPlay()} else {this.audioStop()}}}// 音频播放结束audioStop() {this.setStatus('endPlay')clearTimeout(this.playTimeout)this.audioDataOffset = 0if (this.bufferSource) {try {this.bufferSource.stop()} catch (e) {console.log(e)}}}start() {if(this.audioData.length) {this.audioPlay()} else {if (!this.audioContext) {this.audioInit()}if (!this.audioContext) {alert('该浏览器不支持webAudioApi相关接口')return}this.connectWebSocket()}}stop() {this.audioStop()}
}

transcode.worker.js代码(科大讯飞demo里面的,但是稍作修改 语音合成(流式版)WebAPI 文档 | 讯飞开放平台文档中心)

/** @Autor: lycheng* @Date: 2020-01-13 16:12:22*/let minSampleRate = 22050self.onmessage = function(e) {transcode.transToAudioData(e.data)}var transcode = {transToAudioData(audioDataStr, fromRate = 16000, toRate = 22505) {let outputS16 = transcode.base64ToS16(audioDataStr)let output = transcode.transS16ToF32(outputS16)output = transcode.transSamplingRate(output, fromRate, toRate)output = Array.from(output)self.postMessage({data: output, rawAudioData: Array.from(outputS16)})},transSamplingRate(data, fromRate = 44100, toRate = 16000) {var fitCount = Math.round(data.length * (toRate / fromRate))var newData = new Float32Array(fitCount)var springFactor = (data.length - 1) / (fitCount - 1)newData[0] = data[0]for (let i = 1; i < fitCount - 1; i++) {var tmp = i * springFactorvar before = Math.floor(tmp).toFixed()var after = Math.ceil(tmp).toFixed()var atPoint = tmp - beforenewData[i] = data[before] + (data[after] - data[before]) * atPoint}newData[fitCount - 1] = data[data.length - 1]return newData},transS16ToF32(input) {var tmpData = []for (let i = 0; i < input.length; i++) {var d = input[i] < 0 ? input[i] / 0x8000 : input[i] / 0x7ffftmpData.push(d)}return new Float32Array(tmpData)},base64ToS16(base64AudioData) {base64AudioData = atob(base64AudioData)const outputArray = new Uint8Array(base64AudioData.length)for (let i = 0; i < base64AudioData.length; ++i) {outputArray[i] = base64AudioData.charCodeAt(i)}return new Int16Array(new DataView(outputArray.buffer).buffer)},}

index.vue代码 

<template><view class="content"><view class="text-area"><text class="title">在线文字转语音</text></view><textarea type="text" v-model="txt" placeholder="请输入您要合成的文本"></textarea><button @click="startTrans">{{btnState[ttsStatus]}}</button><br/><view class="text-area"><text class="title">语音转文字</text></view><button>开始转写</button><button>结束转写</button></view>
</template><script>import TTSRecorder from "@/common/TTS.js"console.log(TTSRecorder,'TTSRecorder');// const transWorker = new Worker(new URL('../../common/transcode.worker.js', import.meta.url))let ttsRecorder = new TTSRecorder()export default {data() {return {title: 'Hello',txt: '',aTt: '',btnState: {init: '立即合成',ttsing: '正在合成',play: '停止播放',endPlay: '重新播放',errorTTS: '合成失败',},ttsStatus: 'init'}},onLoad() {const _this = thisttsRecorder.onWillStatusChange = function(oldStatus, status) {// 可以在这里进行页面中一些交互逻辑处理:按钮交互等_this.ttsStatus = status}},methods: {startTrans(){ttsRecorder.setParams({text: this.txt})console.log(ttsRecorder,'ttsRecorder');if (['init', 'endPlay', 'errorTTS'].indexOf(ttsRecorder.status) > -1) {console.log(111);ttsRecorder.start()} else {ttsRecorder.stop()}}}}
</script><style>.content {display: flex;flex-direction: column;align-items: center;justify-content: center;}.logo {height: 200rpx;width: 200rpx;margin-top: 200rpx;margin-left: auto;margin-right: auto;margin-bottom: 50rpx;}.text-area {display: flex;justify-content: center;}.title {font-size: 36rpx;color: #8f8f94;}
</style>

最后打包出来后,把transcode.worker.js放到根目录即可

 

关键点就是webWorker

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

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

相关文章

google语音 API vs 国产 科大讯飞

GOOGLE 语音 API 参考文章地址&#xff1a; http://blog.laobubu.net/546 iphone 4s 中的 siry google android 手机上面的 语音搜索。&#xff08;我用的很少。。。 衰&#xff09;&#xff0c; 前段时间 在微博上看到 baidu 也在 做语音。。。当时还提到国产的 “科大讯…

区块链软件公司:区块链技术三大主要特性的优势

到底什么是区块链?区块链真的有这么厉害吗?区块链能为世界带来什么样的改变?相信这是很多区块链新手的疑问。 今天整理了区块链的三个主要特性&#xff0c;带大家了解为何区块链如此特别。 大部分的区块链是分布式的丶去中心化的以及开源的系统&#xff0c;并且基本上是无从…

进军NFT,第三代公链GGC与NFT3.0技术平台BOBO、OKEx NFT深度联合

近期第三代公链GGC&#xff08;Global Game Chain&#xff09;即将为生态赋能&#xff0c;正式进军NFT及 GameFi 领域&#xff0c;在NFT3.0技术平台BOBO的技术支持下&#xff0c;GGC即将上线OKEx NFT板块&#xff0c;限量发售NFT。 NFT &#xff08;Non-Fungible Token&#xf…

区块链性能腾飞:基于DAG的并行交易执行引擎

大咖揭秘Java人都栽在了哪&#xff1f;点击免费领取《大厂面试清单》&#xff0c;攻克面试难关~>>> FISCO BCOS是完全开源的联盟区块链底层技术平台&#xff0c;由金融区块链合作联盟(深圳)(简称金链盟)成立开源工作组通力打造。开源工作组成员包括博彦科技、华为、深…

企业需要使用财务管理软件的3个原因

财务软件是一种专门为企业财务管理而设计的软件&#xff0c;它可以帮助企业管理财务数据、制定预算、进行财务分析和报告等。在当今商业环境中&#xff0c;财务软件已经成为企业管理中最重要的工具之一&#xff0c;因为它可以帮助企业提高效率并优化业务流程。 首先&#xff0…

业务:财务会计业务知识

一、引言 会计是以货币为主要计量单位&#xff0c;对企业、事业、机关、团体及其他经济组织的经济活动进行记录、计算、控制、分析、报告&#xff0c;以提供财务和管理信息的工作。会计的职能主要是反映和控制经济活动过程&#xff0c;保证会计信息的合法、真实、准确和完整&a…

【财务】三大报表

资产负债表 概念&#xff1a; 资产负债表反映企业在某一特定日期的财务状况的财务报表。 用于反映企业的资产、负债和所有者权益金额机器结构情况&#xff0c;帮助使用者评价企业资产的质量机器短期偿债能力、长期偿债能力、利润分配能力等。 作用&#xff1a; 1. 提供某一…

管理会计报告和财务报告的区别

财务会计报告是给投资人看的&#xff0c;可以反映公司总体的盈利能力。不过&#xff0c;我们回顾一下前 面“第一天”里面提到的问题。 如果你是公司的产品经理&#xff0c;目前有三个产品在你的管辖范围内。上级给你一笔新的资 金&#xff0c;这笔资金应该投到哪个产品上&…

用Python实现一个电影订票系统!

一、效果展示 通过Python实现一个电影订票系统&#xff0c;效果如下所示&#xff1a; 二、整体结构图 三、代码分解 3.1 infos.py 一部电影的详细信息适合用 字典 结构来存储&#xff0c;我们可以给字典里添加多个键值对来保存电影的名称、座位表和宣传时用的字符画&#xff0c…

详解Python文件: .py、.ipynb、.pyi、.pyc、​.pyd !

&#xff08;永久免费&#xff0c;扫码加入&#xff09; 来源丨麦叔编程 今天同事给我扔了一个.pyd文件&#xff0c;说让我跑个数据。然后我就傻了。。 不知道多少粉丝小伙伴会run .pyd代码文件&#xff1f;如果你也懵懵的&#xff0c;请继续往下读吧。。 今天科普下各类Python…

一位老程序员的忠告:别想着靠技术生存一辈子

&#xff08;永久免费&#xff0c;扫码加入&#xff09; 来源&#xff1a;https://segmentfault.com/a/1190000009745139 一、 在一个地方工作8小时就是“穷” 在国内&#xff0c;你千万不要因为学习技术&#xff0c;就可以换来稳定的生活和高的薪水待遇&#xff0c;你更不要认…

大模型技术发展展望

来自&#xff1a;大大的模型 进NLP群—>加入NLP交流群 &#xff08;题目是ChatGPT起的&#xff0c;封面图是SD画的 本文纯属个人观点&#xff0c;抛砖引玉&#xff0c;欢迎后台留言探讨。&#xff09; 从去年年底发布以来&#xff0c;ChatGPT 已经被普通民众和业界广泛接受&…

Python 扩展教程(1): 调用百度AI

关于AI 自有计算机以来&#xff0c;人们就想让计算机具有人的感知、意识、概念、思维、行为&#xff0c;代替人的工作。AI (Artificial Interligence)是计算机科学的一个分支&#xff0c;专注研究、开发、模拟、扩展人的智能的理论、方法、技术及应用。 从研究领域和方法上&…

去TM的领导:发烧请病假,不意味着在家睡大觉

往期热门文章&#xff1a; 1、5年半老程序员被System.out.println() 考懵逼了... 2、妙用Java 8中的 Function接口&#xff0c;消灭if...else&#xff08;非常新颖的写法&#xff09; 3、Controller中的请求方法&#xff0c;private和public有什么区别&#xff1f; 4、再见Jenk…

上海车展归来,聊聊“无人驾驶”:滴滴疯狂炒概念,百度默默降成本

观点| Mr.K 主笔| Wendy.L 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 最近一周在上海市最火热的是什么&#xff1f; 若是在国家会展中心举行的上海车展说是第二&#xff0c;恐怕没人再敢说第一。 汽车诞生百余年后&#xff0c;本次进入第二十届的上海车展…

百度,这下要歇菜了?

点关注公众号&#xff0c;回复“1024”获取2TB学习资源&#xff01; 谁也不会想到&#xff0c;一个非常小众的必应&#xff0c;居然把百度给"揍"了。 根据美国网站通讯流量监测机构 StatCounter 公布的数据显示&#xff0c;2023 年 4 月份&#xff0c;微软必应的市场…

5年半老程序员被System.out.println() 考懵逼了...

往期热门文章&#xff1a; 1、妙用Java 8中的 Function接口&#xff0c;消灭if...else&#xff08;非常新颖的写法&#xff09; 2、Controller中的请求方法&#xff0c;private和public有什么区别&#xff1f; 3、再见Jenkins&#xff01;一款更适合国人的自动化部署工具&#…

Controller中的请求方法,private和public有什么区别?

往期热门文章&#xff1a; 1、再见Jenkins&#xff01;一款更适合国人的自动化部署工具&#xff0c;贼带劲&#xff01;&#xff01; 2、MySQL中这14个小玩意&#xff0c;让人眼前一亮&#xff01;&#xff01;&#xff01; 3、Spring 官方证实&#xff01;大漏洞&#xff0c;J…

妙用Java 8中的 Function接口,消灭if...else(非常新颖的写法)

往期热门文章&#xff1a; 1、Controller中的请求方法&#xff0c;private和public有什么区别&#xff1f; 2、再见Jenkins&#xff01;一款更适合国人的自动化部署工具&#xff0c;贼带劲&#xff01;&#xff01; 3、MySQL中这14个小玩意&#xff0c;让人眼前一亮&#xff01…

SpringBoot + MDC 实现全链路调用日志跟踪

往期热门文章&#xff1a; 1、去TM的领导&#xff1a;发烧请病假&#xff0c;不意味着在家睡大觉 2、5年半老程序员被System.out.println() 考懵逼了... 3、妙用Java 8中的 Function接口&#xff0c;消灭if...else&#xff08;非常新颖的写法&#xff09; 4、Controller中的请求…