1.功能場(chǎng)景
有時(shí)候需要在網(wǎng)頁上面播報(bào)一段語音,而這段語音是動(dòng)態(tài)的。例如收銀時(shí)播報(bào)請(qǐng)出示付款嗎,收錢成功后播報(bào)某某某為您收到金額XX元。
2.思路
第一種思路是前端不需要怎么動(dòng)手寫代碼的也是最容易實(shí)現(xiàn)的,調(diào)用語音合成api。但是api的局限性就在于免費(fèi)的沒有語音包,收錢的就有點(diǎn)貴了,不適用于重復(fù)調(diào)用(我們系統(tǒng)目前規(guī)模不大,但是每天也能產(chǎn)生1-2萬條成功的交易訂單)。
第二種思路是調(diào)用windows本地的tts語音合成服務(wù),這是能免費(fèi)使用且可以支持每次根據(jù)不同的內(nèi)容來合成不同的語音的一個(gè)功能。
第三種思路使用video元素直接組裝一些零散的文字來形成一段完整的音頻。
這里就講一下第二種跟第三種思路
3.實(shí)現(xiàn)
? ? ? ? 3.1windows本地的tts語音合成服務(wù)
? ? ? ? 這里使用的是SpeechSynthesisUtterance這個(gè)html5新的api,這個(gè)對(duì)象主要用來構(gòu)建語音合成實(shí)例,具體的屬性如下。
text
– 要合成的文字內(nèi)容:string。lang
– 使用的語言:string, 例如:"zh-cn"
voiceURI
– 指定希望使用的聲音和服務(wù):string。volume
– 聲音的音量:number,范圍是0-
1
,默認(rèn)11
。rate
– 語速:number,范圍是0.1-
10
,默認(rèn)1。
pitch
– 表示說話的音高:number,范圍是0
-2
。默認(rèn)為1
。
當(dāng)然,這個(gè)實(shí)例對(duì)象也包括一些方法
onstart
?– 合成開始的回調(diào)。onpause
?– 合成暫停的回調(diào)。onresume
?– 合成重新開始的回調(diào)。onend
?– 合成結(jié)束時(shí)的回調(diào)。
這是mdn上面對(duì)SpeechSynthesisUtterance這個(gè)對(duì)象的說明:SpeechSynthesisUtterance - Web API 接口參考 | MDN
?然后還有一個(gè)跟SpeechSynthesisUtterance搭配使用的SpeechSynthesis對(duì)象。該接口是語音服務(wù)的控制接口,它可以用于獲取設(shè)備上關(guān)于可用的合成聲音的信息,開始、暫停語音,或除此之外的其他命令。SpeechSynthesisUtterance - Web API 接口參考 | MDN
speak()
– 只能接收SpeechSynthesisUtterance
作為唯一的參數(shù),作用是讀合成的話語。
stop()
– 立即終止合成過程。
pause()
– 暫停合成過程。
resume()
– 重新開始合成過程。
getVoices
– 此方法不接受任何參數(shù),用來返回瀏覽器支持的語音包列表,是個(gè)數(shù)組。谷歌瀏覽器
getVoices獲取的聲音列表,國內(nèi)能使用的應(yīng)該就前三個(gè)
?文章來源:http://www.zghlxwxcb.cn/news/detail-416196.html
let synth;
let msg;
const initSpeak = () => {
synth = window.speechSynthesis
msg = new SpeechSynthesisUtterance()
msg.text = '收到新的訂單'
msg.lang = 'zh-CN'
msg.pitch = 1.1
msg.rate = 1.8
msg.volume = 10
//getVoices() 是一個(gè)異步的方法,需要使用一個(gè)定時(shí)器來保證每次都能獲取到值
setTimeout(()=>{
synth.getVoices().find(i=>i.lang=='zh-CN'&&i.localService==true)
},100)
}
initSpeak()
// 調(diào)用這個(gè)方法傳入一個(gè)你需要合成的文字的話就能開始使用瀏覽器來播報(bào)語音了
const handleSpeak = (message) => {
msg.text = message
synth.speak(msg)
}
到這里就完成了語音合成了,不過由于我們項(xiàng)目中有的客戶電腦使用的閹割版的windows或者沒有去安裝語音合成引擎,這個(gè)版本上線一周就被pass掉了,所以使用這個(gè)方案的前提是你能保證客戶機(jī)上面是安裝了語音合成引擎。
????????3.2 組裝一些零散的mp3片段來形成一段完整的音頻(偽語音播報(bào))
? ? ? ? 結(jié)合到實(shí)際的應(yīng)用場(chǎng)景項(xiàng)目中最終選擇了這種方式來實(shí)現(xiàn)語音合成,雖然方法很笨,但是至少所有客戶機(jī)都能滿足需求且可以做到客戶自定義語音包。
? ? ? ? 實(shí)現(xiàn):
1.需要一個(gè)將數(shù)字轉(zhuǎn)換成中文讀數(shù)的方法(網(wǎng)上找的)
2.再將這個(gè)中文讀數(shù)構(gòu)造成一個(gè)數(shù)組(urlList)
3.調(diào)用組件進(jìn)行播報(bào)
?數(shù)字轉(zhuǎn)換成中文讀數(shù)的方法
/**
* 數(shù)字轉(zhuǎn)成漢字
* @params num === 要轉(zhuǎn)換的數(shù)字
* @return 漢字
* @eg 例如,輸入0.41, 返回"零點(diǎn)四一元"
* */
const numToChines = (tranvalue) => {
if (typeof tranvalue == "number") {
tranvalue = tranvalue + ''
}
//拆分整數(shù)與小數(shù)
let splits = function (tranvalue) {
var value = new Array('', '');
temp = tranvalue.split(".");
for (var i = 0; i < temp.length; i++) {
value[i] = temp[i];
}
return value;
}
try {
var i = 1;
var dw2 = new Array("", "萬", "億");//大單位
var dw1 = new Array("十", "百", "千");//小單位
var dw = new Array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九");//整數(shù)部分用
//以下是小寫轉(zhuǎn)換成大寫顯示在合計(jì)大寫的文本框中
//分離整數(shù)與小數(shù)
var source = splits(tranvalue);
var num = source[0];
var dig = source[1];
//轉(zhuǎn)換整數(shù)部分
var k1 = 0;//計(jì)小單位
var k2 = 0;//計(jì)大單位
var sum = 0;
var str = "";
var len = source[0].length;//整數(shù)的長度
for (i = 1; i <= len; i++) {
var n = source[0].charAt(len - i);//取得某個(gè)位數(shù)上的數(shù)字
var bn = 0;
if (len - i - 1 >= 0) {
bn = source[0].charAt(len - i - 1);//取得某個(gè)位數(shù)前一位上的數(shù)字
}
sum = sum + Number(n);
if (sum != 0) {
str = dw[Number(n)].concat(str);//取得該數(shù)字對(duì)應(yīng)的大寫數(shù)字,并插入到str字符串的前面
if (n == '0') sum = 0;
}
if (len - i - 1 >= 0) {//在數(shù)字范圍內(nèi)
if (k1 != 3) {//加小單位
if (bn != 0) {
str = dw1[k1].concat(str);
}
k1++;
} else {//不加小單位,加大單位
k1 = 0;
var temp = str.charAt(0);
if (temp == "萬" || temp == "億")//若大單位前沒有數(shù)字則舍去大單位
str = str.substr(1, str.length - 1);
str = dw2[k2].concat(str);
sum = 0;
}
}
if (k1 == 3)//小單位到千則大單位進(jìn)一
{ k2++; }
}
//轉(zhuǎn)換小數(shù)部分
var strdig = "";
if (dig != "") {
var n = dig.charAt(0)
var nn = dig.charAt(1)
if (nn !== "") {
strdig = "點(diǎn)" + dw[Number(n)] + dw[Number(nn)]
} else {
strdig = "點(diǎn)" + dw[Number(n)]
}
}
if (str) {
str += strdig + "元";
} else {
str = "零" + strdig + "元"
}
} catch (e) {
return "零元";
}
return str;
}
?構(gòu)建待播報(bào)的數(shù)組
//將中文枚舉成對(duì)應(yīng)的英文路徑
const voiceEnum = {
"萬": "ten_thousand",
"十": "ten",
"百": "hundred",
"千": "thousand",
"零": "0",
"一": "1",
"二": "2",
"三": "3",
"四": "4",
"五": "5",
"六": "6",
"七": "7",
"八": "8",
"九": "9",
"點(diǎn)": "point",
"元": "element"
}
const speak = () => {
let list = []
let chineseNum = numToChines(0.41)
for (let i = 0; i < chineseNum.length; i++) {
const item = chineseNum[i];
//這里的地址拼接成你自己存放零散mp3片段的地址,voicePacket是語音包的配置,需要自己去定義
urlList.push(`https://oss.aliyuncs.com/voice/${voicePacket.value}/amount/${voiceEnum[item]}.mp3`)
}
//調(diào)用一個(gè)組件來播報(bào)這個(gè)數(shù)組形式的url
audioLoopRef.value && audioLoopRef.value.start(list)
}
?附上audioLoop這個(gè)組件代碼,構(gòu)建好urlList之后通過ref調(diào)用組件內(nèi)的方法即可
<template>
<div v-if="audioUrlList.length > 0">
<audio @ended="voiceEnded" id="voice">
<source :src="audioUrlList[currentIndex]" type="audio/mpeg">
您的瀏覽器不支持 audio 元素。
</audio>
</div>
</template>
<script setup>
import { ref, nextTick } from "vue"
// 用來保存?zhèn)鬟^來的需要播放的urlList:[]
const audioUrlList = ref([])
// 默認(rèn)從頭開始播報(bào)
const currentIndex = ref(0)
// 開始播報(bào)
const start = (urlList) => {
audioUrlList.value = urlList
nextTick(() => {
let dom = document.getElementById("voice")
dom.play()
})
}
// 停止播報(bào)
const stop = () => {
let dom = document.getElementById("voice")
nextTick(() => {
dom.pause()
dom.setAttribute("src", "xxxx")
currentIndex.value = 0
audioUrlList.value = []
})
}
// 播放完一個(gè)就繼續(xù)播放下一個(gè)
const voiceEnded = () => {
if (currentIndex.value == (audioUrlList.value.length - 1)) {
audioUrlList.value = []
currentIndex.value = 0
} else {
currentIndex.value++
let dom = document.getElementById("voice")
dom.setAttribute("src", audioUrlList.value[currentIndex.value])
dom.play()
}
}
defineExpose({
start, stop
})
</script>
?語音包的實(shí)現(xiàn)其實(shí)就是將相同的文字多錄制幾個(gè)語音包放在不同的oss目錄下面,到時(shí)候通過前端的配置動(dòng)態(tài)生成urlList時(shí)去對(duì)應(yīng)到不同的語音包。
?
?
到這里就完成了自己手動(dòng)合成一段語音并播報(bào)了。文章來源地址http://www.zghlxwxcb.cn/news/detail-416196.html
到了這里,關(guān)于前端文字轉(zhuǎn)語音(tts+mp3拼接)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!