最初尝试使用讯飞语音合成方式,能获取到语音数据,但是数据是base64格式的,在安卓端无法播放,网上有说通过转成blob格式的url可以播放,但是uniapp不支持转换的api;于是后面又想其他办法,使用安卓插件播报原生安卓语音播报插件 - DCloud 插件市场
方案一(讯飞语音合成)
1.在讯飞后台注册登录获得APPID等信息 讯飞语音合成控制台
2.在项目根目录使用npm安装crypto-js
npm i crypto-js
3.新建xunfei.js文件,替换讯飞的APPID等3个配置
// 讯飞语音合成api文档 https://www.xfyun.cn/doc/tts/online_tts/API.html
import CryptoJS from 'crypto-js'
import {Base64} from './base64.js';
const APPID = "替换自己的APPID";
const API_SECRET = "替换自己的API_SECRET";
const API_KEY = "替换自己的API_KEY";
const URL = "wss://tts-api.xfyun.cn/v2/tts"
const HOST = "tts-api.xfyun.cn"
function getWssUrl(date) {
date = date||(new Date().toGMTString())
let signatureOrigin = `host: ${HOST}\ndate: ${date}\nGET /v2/tts HTTP/1.1`
let signatureSha = CryptoJS.HmacSHA256(signatureOrigin, API_SECRET)
let signature = CryptoJS.enc.Base64.stringify(signatureSha)
let authorizationOrigin = `api_key="${API_KEY}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"`
let authStr = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(authorizationOrigin))
return URL + "?authorization=" + authStr + "&date=" + date + "&host=" + HOST
}
const wssUrl=getWssUrl()
// console.log("讯飞语音合成wssUrl",wssUrl)
export const speak = (word) => {
if (!word) {return}
const socketTask=uni.connectSocket({
url: wssUrl,
success: res=> {
console.log("讯飞websocket连接成功",res);
},
fail: err=> {
console.log("讯飞websocket连接失败",err);
},
});
//连接建立完毕,读取数据识别
let buffs = ""
socketTask.onOpen(data => {
let params = {
"common": {
"app_id": APPID
},
"business": {
// aue: "raw",
aue: "lame",//mp3 (当aue=lame时需传参sfl=1)
sfl: 1,//开启流式返回mp3格式音频
auf: "audio/L16;rate=16000",
vcn: "xiaoyan",//aisjiuxu aisxping aisjinger aisbabyxu
tte: "UTF8",
speed:60,//默认50,可选0-100
volume:80,//默认50,可选0-100
},
"data": {
"text": Base64.encode(word),
"status": 2
}
}
socketTask.send({
data:JSON.stringify(params),
success: res=> {
console.log("讯飞websocket发送成功",res);
},
fail: err=> {
console.log("讯飞websocket发送失败",err);
},
})
socketTask.onMessage(res => {
let ds = JSON.parse(res.data)
console.log("接收到websocket消息:",ds)
buffs+=ds.data.audio//返回的是base64数据
if (ds.code === 0 && ds.data.status === 2) { //status为2表示合成完成
console.log("音频合成完成");
toPlay();
socketTask.close();
}
})
})
socketTask.onError(err=>{
console.log("讯飞websocket发生错误",err);
})
function toPlay() {
const base64data='data:audio/mp3;base64,' + buffs
let audioContext = uni.createInnerAudioContext();
audioContext.autoplay = true;
audioContext.src = base64data
audioContext.play()
audioContext.onEnded(() => {
console.log("播放完成");
audioContext.destroy()
audioContext=null
})
audioContext.onCanplay(() => {
console.log("可以播放音频了");
})
audioContext.onPlay(() => {
console.log('开始播放');
});
audioContext.onError((res) => {
console.log("播放失败",res);
});
}
}
上面引入的base64.js,也可以自己npm安装base64插件
export const Base64 = {
keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
encode(input) {
let output = '';
let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
let i = 0;
input = Base64.utf8Encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
Base64.keyStr.charAt(enc1) + Base64.keyStr.charAt(enc2) +
Base64.keyStr.charAt(enc3) + Base64.keyStr.charAt(enc4);
}
return output;
},
decode(input) {
let output = '';
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
input = input.replace(/[^A-Za-z0-9+/=]/g, '');
while (i < input.length) {
enc1 = Base64.keyStr.indexOf(input.charAt(i++));
enc2 = Base64.keyStr.indexOf(input.charAt(i++));
enc3 = Base64.keyStr.indexOf(input.charAt(i++));
enc4 = Base64.keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 !== 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 !== 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64.utf8Decode(output);
return output;
},
utf8Encode(string) {
string = string.replace(/\r\n/g, '\n');
let utfString = '';
for (let i = 0; i < string.length; i++) {
let c = string.charCodeAt(i);
if (c < 128) {
utfString += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utfString += String.fromCharCode((c >> 6) | 192);
utfString += String.fromCharCode((c & 63) | 128);
} else {
utfString += String.fromCharCode((c >> 12) | 224);
utfString += String.fromCharCode(((c >> 6) & 63) | 128);
utfString += String.fromCharCode((c & 63) | 128);
}
}
return utfString;
},
utf8Decode(utfString) {
let string = '';
let i = 0;
let c = 0;
let c2 = 0;
let c3 = 0;
while (i < utfString.length) {
c = utfString.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utfString.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utfString.charCodeAt(i + 1);
c3 = utfString.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
};
4.在main.js中引入
import {speak} from './xunfei.js'
Vue.prototype.$speak=speak
//后续直接在需要的地方通过this.$speak('需要播报的文字')
PS:该方式在H5端播放短文本语句可以正常播放,长文本的播放不了,可以尝试分段播放,将websocket收到的数据push到一个数组中,然后一段一段播放(会有卡顿现象);在安卓端直接播放不了;大家有需要的可以尝试将base64转为blob,再用URL.createObjectURL()转换成临时url地址,或者服务端转成mp3数据
方案二(原生安卓语音播报插件) 推荐此方式,没有播报条数限制,讯飞每天有500条免费额度
在插件市场购买(0元)云打包插件 插件地址 然后按下图步骤选择该插件,然后重新制作自定义调试基座
然后在main.js中导入插件
// #ifdef APP
// 原生安卓语音播报插件,手机端可用,PDA需要安装tts语音引擎
let androidTTSPlugin = uni.requireNativePlugin('Tellsea-AndroidTTSPlugin');
androidTTSPlugin.init((e) => {
let res = JSON.parse(e);
if (res.code == 200) {
console.log("初始化成功",res.msg);
} else {
console.log("初始化失败",res.msg);
}
});
androidTTSPlugin.testPlugin('测试插件', (e) => {
let res = JSON.parse(e);
if (res.code == 200) {
console.log('测试成功');
} else {
console.log("测试失败",res.msg);
}
});
Vue.prototype.$speak=text=>{
androidTTSPlugin.textToSpeech(text, (e) => {
let res = JSON.parse(e);
if (res.code == 200) {
console.log("播报成功",res.msg);
} else {
console.log("播报失败",res.msg);
}
});
}
// #endif
后面在页面中使用语音播报api
this.$speak("上班了,打卡成功")
PS:如果设备播报没有声音需要查看设备是否安装了TTS语音引擎,没有的话可以安装微软的TTS语音引擎,安装完成之后,在 设置->文字转语音输出->首选引擎 将安装的TTS设置为首选引擎,然后打开TTS引擎 配置电池优化为允许后台持续运行
下载地址:下载地址