以前公司下达了一个需求,要求用语音做广播,因为广播里面包含姓名,所以不能难以直接录制,需要使用合成语音。
一开始考虑到时间以及优先级问题,这边使用了百度的合成语音。
但是百度语音相对僵硬,而且没有感情,效果不甚满意。
不久前时间相对充沛,这边就使用了觉得最完美的方案——使用微软云的人工智能合成语音。
因为这个语音可以设置感情,没有机器合成那种僵硬的感觉,可以说是比真人还真人。
微软云合成语音demo网站:https://azure.microsoft.com/zh-cn/services/cognitive-services/text-to-speech/
感觉确实有需要的话,接下来就应该创建微软云账号,开始使用了,可以参考这个视频:https://www.bilibili.com/video/av670457802
不过要注意一点,创建微软云账号,需要绑定一张visa卡。如果没有的话,可以淘宝或者申请微软云学生账号。
申请微软云学生账号:https://www.daniao.org/6693.html
创建好微软云合成语音服务之后,就可以直接使用了。
文档地址:https://docs.microsoft.com/zh-cn/azure/cognitive-services/speech-service/get-started-text-to-speech?tabs=script%2Cwindowsinstall&pivots=programming-language-javascript
收费:
每个月都有25万字的合成额度,如果需求量不大的话,已经够用了。如果需求量大,可以将通用的语音保存到服务器,将不通用项,如姓名,以单字的形式,请求后再存入服务器。每次访问时,先检查服务器存不存在这个字,如没有再请求api接口。
下面是简单使用的具体代码:
<script type="text/javascript">
const sdk = SpeechSDK
var synthesizer = null;
/**
* 初始化微软语音
*/
function synthesizeMsSpeech() {
const speechConfig = sdk.SpeechConfig.fromSubscription("你的密匙", "你的地区,如 eastus 不能输入中文");
synthesizer = new sdk.SpeechSynthesizer(speechConfig);
}
/**
* 使用微软语音说话
* @param {Object} word
*/
function msSpeek(text) {
//微软人工智能语音,需要使用xml字符串做配置
let ssml = `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">
<voice name="zh-cn-XiaoxiaoNeural">
<mstts:express-as style="cheerful">
${text}
</mstts:express-as>
</voice>
</speak>`
synthesizer.speakSsmlAsync(
ssml,
result => {
if (result.errorDetails) {
console.error(result.errorDetails);
} else {
// console.log(JSON.stringify(result));
}
synthesizer.close();
},
error => {
console.log(error);
synthesizer.close();
});
}
synthesizeMsSpeech();
msSpeek("你好,我是你的优乐美!")
</script>
</body>
密匙就不解释了,微软云控制台可以看到,地区可以选eastus eastus2,以及另外几个, 具体看控制台上显示的地区
另外为方便理解,附带一个带界面的语音合成网页:
欢快 闲聊 冷静 生气 紧张 哭泣 轻蔑 严肃 亲切 温和 伤感 客服 数字助理 新闻
</select>
<br>
<br>
<button type="button" onclick="clickPlayBtn()">播放</button>
<button type="button" onclick="clickDownBtn()">下载</button>
</div>
<!-- 去除语音直接播放的sdk。在sdi.min.js,搜索play(),去除第二个play,这样不会输入到流的时候,依然先自动播放语音,形成干扰 -->
<script src="static/js/microsoft.speech.sdk.1.js"></script>
<script type="text/javascript">
const sdk = SpeechSDK
var synthesizer = null;
//还有很多种
const STYLE = {
cheerful: "cheerful", //欢快
chat: "chat", //闲聊
calm: "calm", //冷静
angry: "angry", //生气
fearful: "fearful" //紧张
}
/**
* 初始化微软语音
*/
function synthesizeMsSpeech() {
const speechConfig = sdk.SpeechConfig.fromSubscription("你的微软云密匙", "你的地区,如eastus");
synthesizer = new sdk.SpeechSynthesizer(speechConfig);
}
/**
* 请求微软语音
* @param {Object} text
*/
function requestMsAudio(text, style="cheerful") {
return new Promise((resolve, reject) => {
let xmlObj = `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">
<voice name="zh-cn-XiaoxiaoNeural">
<mstts:express-as style="${style}">
${text}
</mstts:express-as>
</voice>
</speak>`
const ssml = xmlObj;
synthesizer.speakSsmlAsync(
ssml,
result => {
// synthesizer.close(); //关闭后,会只获取一轮
if (result.errorDetails) {
console.error(result.errorDetails);
reject(result)
} else {
let arrayBuffer = result.audioData
let blob = new Blob([arrayBuffer], {
type: 'autio/wave'
});
var src = URL.createObjectURL(blob);
resolve(src)
}
},
error => {
console.log(error);
reject(error)
// synthesizer.close();
});
})
}
/**
* 使用微软语音说话
* @param {Object} text
*/
function msSpeek(text, style="cheerful") {
return new Promise((resolve, reject) => {
requestMsAudio(text, style).then((src) => {
var mp3 = new Audio(src);
//播放完之后,执行事件
mp3.addEventListener("ended", () => {
resolve();
})
mp3.play().catch(playError); //播放语音
});
})
}
/**
* 合并播放,先播放完一个,再播放另一个
* @param {Object} src 资源链接
* @param {Object} text 要生成的文字
*/
function mergeSpeek(src, text, style="cheerful"){
return new Promise((resolve, reject) => {
requestMsAudio(text, style).then((msSrc) => {
playAudio(src).then(()=>{
var mp3 = new Audio(msSrc);
//播放完之后,执行事件
mp3.addEventListener("ended", () => {resolve();})
mp3.play().catch(playError); //播放语音
})
});
})
}
/**
* 下载微软语音
* @param {Object} text
*/
function msDownload(text, style) {
return new Promise((resolve, reject) => {
requestMsAudio(text, style).then((src) => {
const pattern=/[`~!@#$^&*()=|{}':;',\\\[\]\.<>\/?~!@#¥……&*()——|{}【】';:""'。,、?\s]/g;
let name = text.replace(pattern," ");
_download(src, name +".wav")
});
})
};
/**
* 播放音频,then为播放完后的回调
* @param {Object} src
*/
function playAudio(src) {
return new Promise((resolve, reject) => {
var mp3 = new Audio(src);
//播放完之后,执行事件
mp3.addEventListener("ended", () => {
resolve();
})
mp3.play().catch(playError); //播放语音
})
}
/**
* 播放失败后的统一回调
*/
const playError = function(){
console.error("播放失败,播放前,请先点击页面,以允许播放。")
}
// 下载
function _download(src, filename) {
var eleLink = document.createElement('a');
eleLink.download = filename;
eleLink.style.display = 'none';
// 字符内容转变成blob地址
eleLink.href = src;
// 自动触发点击
document.body.appendChild(eleLink);
eleLink.click();
// 然后移除
document.body.removeChild(eleLink);
};
synthesizeMsSpeech();
/**
* 点击下载后触发
*/
function clickPlayBtn(){
let text = document.getElementById("mainTxt").value;
let style = document.getElementById("style").value
if(text.length > 0){
msSpeek(text, style)
}
}
/**
* 点击下载后触发
*/
function clickDownBtn(){
let text = document.getElementById("mainTxt").value;
let style = document.getElementById("style").value
if(text.length > 0){
msDownload(text, style)
}
}
</script>
</body>