科大讯飞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
科大讯飞webAPI文字转语音相关推荐
- 基于ROS2和科大讯飞的文字转语音TTS入门教程
基于ROS2和科大讯飞的语音转文字入门教程 基于ROS2和科大讯飞的文字转语音TTS入门教程 1.环境搭建 2.创建工程 3.编译和执行 基于ROS2和科大讯飞的文字转语音TTS入门教程 本文将展示, ...
- python 语音转文字_python使用科大讯飞语音合成文字转语音
#讯飞文字转语音 import base64 import json import time import hashlib import urllib.request import urllib.pa ...
- 使用(科大讯飞)文字转语音播放
1.在科大讯飞上注册账号,创建项目,获取appid 2.添加对应的jar包 Msc.jar.Sunflower.jar 3.配置权限 <uses-permission android:name= ...
- 科大讯飞实现“文字转语音”和“语音转文字”
请在这里查看示例 ☞ iat示例 详细介绍 这里整合了科大讯飞官方示例,去除一些不必要的元素,便于开发者理解和应用相关功能 经测试,在chrome浏览器下不允许在http协议下使用html5的api ...
- 语音转写基于科大讯飞WebApi接口的安卓实现--上传录音音频翻译成文字
一.目的与实现过程 1.目的:将.wav/.mp3音频文件翻译成文字 2.方式:基于科大讯飞语音转写 WebApi的安卓实现 3.机制:采用自定义计时器轮询. 4.坑点1:科大讯飞当前暂无安卓文档/代 ...
- android 融云 + 科大讯飞 实现仿微信语音消息转换为文字(附DEMO源码)
融云SDK 使用很方便,简单配置就可以搭建即时通讯功能,配合科大讯飞的语音识别, 即可实现微信中语音消息转换为文字的功能 融云sdk的基本使用就不细说了, 网上很多资料 使用融云sdk自带的聊天会话界 ...
- 基于FlashWavRecorder实现IE11浏览器录音后用科大讯飞转文字
终于折腾好了(使用FlashWavRecorder实现IE11浏览器录音后用科大讯飞转文字,我的是Vue.js项目),对一个前端仔来说,听到要兼容IE,都是不情愿的,但是需求来了,那就折腾起来吧.(主 ...
- Android百度语音集成——文字转语音
项目涉及文字转语音的需求,用Android原生提供的TTS生成的语音太单调,机器声音太明显,故寻求第三方更好的支持,用科大讯飞的语音包收费,百度语音免费而且不限制调用次数,主页鲜明说永久免费的智能语音 ...
- Android 文字转语音2种方式
今天在开发中做了一个文字转为语音的功能,入了很多坑. 首先我采用的是科大讯飞的在线语音合成技术.写完之后他可以在低版本的手机上读出来,但是再高版本的7.0以上就读不出来,都听不懂再读什么. 下面贴出我 ...
最新文章
- arnold官方帮助文档_Baklib-随时编辑随时发布的在线帮助文档制作平台
- Nignx平滑升级(1.8.0-1.8.1)
- sap Status状态栏设计
- python做后端开发的优点_【后端开发】python语言的特点是什么
- jqgrid treegrid 重新加载数据
- Class类和Object类及用法(一)
- Android使用百度翻译api
- h5上下滑动时页面出现抖动问题解决
- Gbase相关学习总结
- live2d_Live2D | CubismSdkForUnity4r.1简介(下)
- Windows 10 下载官方正版ISO镜像文件
- Steam软件联网问题解决方案
- linux 硬盘开启apm,硬盘 APM 设置
- AUTOCAD——减少样条曲线控制点数、CAD进阶练习(三)
- 笔划码、五笔码、拼音码软键盘中文输入
- kylinserverv10部署dm8单实例命令行方式安装
- 福利群怎么引流?微信群引流技巧
- Java实现Apriori算法
- 卡尔曼滤波器、扩展卡尔曼滤波器、无向卡尔曼滤波器的详细推导
- vue中destroyed方法的使用