缘起
众所周知 科大讯飞的在线语音合成好使,但每天只有少得可怜的500次的配额, 想用但又不想出钱怎么办? 针对自己的特定业务场景, 同频词汇多,变化词汇少的情况. 将要请求的短句自己分词后分别获取wav 然后存于数据库. 是一个还不错的解决方案.
数据库设计
CREATE TABLE `tp_user_voice` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sName` varchar(32) DEFAULT NULL,
`sVoice` mediumtext,
PRIMARY KEY (`id`),
UNIQUE KEY `sName` (`sName`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
PHP 代码
<?php
use app\index\model\UserVoice;
class VoiceMixer
{
// 向科大讯飞发起语音合成请求
private static function _DownloadVoice($sWord)
{
$curtime = strval(time());
// 您在科大讯飞申请的APPID 和 APPKEY
$appid = '*********';
$appkey = '*******************************';
$xParam = base64_encode(json_encode([
'auf' => 'audio/L16;rate=16000',
'aue' => 'raw', // raw lame
'voice_name' => 'aisjinger',
'engine_type'=>'x'
]));
$headers = [
'X-CurTime: '. $curtime,
'X-Param: '. $xParam,
'X-Appid: '.$appid,
'X-CheckSum: '.md5($appkey.$curtime.$xParam),
'Content-Type: application/x-www-form-urlencoded; charset=utf-8'
];
$contents = ['text'=>$sWord];
return form_post('http://api.xfyun.cn/v1/service/v1/tts', $headers, $contents);
}
private static function _GetWord($sWord)
{
$record = UserVoice::where('sName','EQ',$sWord)->find();
if(is_object($record)){
if(!is_null($record->sVoice)){
return base64_decode($record->sVoice);
}else{
$record->sVoice = base64_encode(Self::_DownloadVoice($sWord));
$record->save();
return base64_decode($record->sVoice);
}
} else {
$record = new UserVoice([
'sName'=>$sWord,
'sVoice'=>base64_encode(Self::_DownloadVoice($sWord))
]);
$record->save();
return base64_decode($record->sVoice);
}
return false;
}
/*
传入一个单词数组, 混音成一个单词短句.
*/
static public function GetVoice($words)
{
$vocArray = [];
$index = 0;
//第一步 获取所有单词语音
foreach($words as $item){
$voc = Self::_GetWord($item);
if($index==0){
$vocArray [] = $voc;
}else{
$vocArray [] = substr($voc, 44);
}
$index++;
}
//第二步 混音.
$voc = implode('', $vocArray);
$len = strlen($voc) - 8;
$voc{4} = chr($len & 0xFF);
$voc{5} = chr(($len >> 8) & 0xFF);
$voc{6} = chr(($len >> 16) & 0xFF);
$voc{7} = chr(($len >> 24) & 0xFF);
$len -=36;
$voc{40} = chr($len & 0xFF);
$voc{41} = chr(($len >> 8) & 0xFF);
$voc{42} = chr(($len >> 16) & 0xFF);
$voc{43} = chr(($len >> 24) & 0xFF);
return $voc;
}
}