我在使用科大讯飞时,遇到的问题大概就是,那个讯飞的文档真的不好理解(个人观点),,哈哈哈 弄了真的挺久搞出来 ,希望其他人给提供一点灵感吧 !
小白勿喷!!!
接下来是我的代码
需要pnpm install 两个包 分别是 :js-base64 和 crypto-js
const APPID = "你的appID";
const API_SECRET = "你的API_SECRET ";
const API_KEY = "你的API_KEY ";
import CryptoJS from 'crypto-js';
import { Base64 } from 'js-base64';
//正确的URL
function getWebSocketUrl(apiKey, apiSecret) {
var url = "wss://tts-api.xfyun.cn/v2/tts";
var host = 'tts-api.xfyun.cn'
var 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}`;
return url;
}
// 文本编码
function 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.byteLength
for (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)
}
}
}
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: vcn,
speed: speed,
volume: volume,
pitch: pitch,
bgs: 1,
tte: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);
}
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 MozWebSocket(url);
} else {
alert("浏览器不支持WebSocket");
return;
}
}
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 = (e) => {
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 = (e) => {
console.error(e);
};
this.ttsWS.onclose = (e) => {
console.log(e);
};
}
send_newMessage = (text) => {
var 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(){
let that =this.ttsWS
return new Promise((resolve, reject) => {
that.onmessage = (e) => {
let jsonData = JSON.parse(e.data);
// 合成失败
if (jsonData.code !== 0) {
reject({message:"失败",data:jsonData})
}
resolve({
message: "成功",
type: "base64",
data: jsonData.data.audio,
isLastData: jsonData.data.status === 2,
})
if (jsonData.code === 0 && jsonData.data.status === 2) {
that.close();
}
};
});
}
TTS_close_reset() {
ttsWS?.close();
// audioPlayer.reset();
}
}
调用代码是:
import TTWss from '../assets/index.js'
let audio_url = ref('');
const ttsinstance:any = ref(null)
ttsinstance.value = TTWss.getInstance('hello world', 'xiaoyan', 50, 50, 50)
ttsinstance.value.connectWebSocket();
ttsinstance.value.getMessage().then((result:any) => {
console.log("成功",result);//测试
const binaryString = atob(result.data);
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
// 这里展开后可以直接下载
// let 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);
}).catch((err:any) => {
console.log("失败",err);
});