freeswitch原生支持的tts功能中文一般是使用的ekho,但是那合成的效果简直惨不忍睹,于是我想自己做一个TTS服务器。

首先是找到比较满意的TTS引擎,科大讯飞的效果当然是没话说,但是价格不菲,其他商业的引擎中文合成也不是很流畅,偶然发现windows7自带的合成引擎还算过得去,windows10带的合成引擎就更好了(有兴趣的可以先测试一下,直接在windows控制面板中的语音设置里面有测试,但是测试的中英文混读很蛋疼)。

那么问题来了,怎么把这个引擎用到我的FS上边呢?

思路,debian上的freeswitch(后续简称de-FS)需要TTS的时候,通过桥接到另外一个windows版本的freeswitch(后续简称win-FS),在SIP消息X-header中附加上需要合成的文字,然后win-FS接收到文字后,通过lua脚本调用一个应用程序转换成语音文件,win-FS再播放出来,这样呼入原de-FS的主叫就听到了文本转换后的语音了。

折腾了一个星期,终于把这个TTS服务搭建好了,分享出来,顺便记一下,免得下次要用时找不到。

废话不多说了,直接上脚本(注:de-FS使用fusionpbx搭建的,数据源调用直接用了fusionpbx的方法,深究的可以搭一个fusionpbx查查里面的脚本)

1、de-FS端查数、外呼、转接脚本:

function createIconv(from,to,text)
  -- 本函数用于将utf8编码和gbk编码相互转换
  -- apt-get install lua-iconv #需要先安装lua-iconv
  local iconv = require("iconv")
  local cd = iconv.new(to .. "//TRANSLIT", from)
  local ostr, err = cd:iconv(text)
  if err == iconv.ERROR_INCOMPLETE then
    return "ERROR: Incomplete input."
  elseif err == iconv.ERROR_INVALID then
    return "ERROR: Invalid input."
  elseif err == iconv.ERROR_NO_MEMORY then
    return "ERROR: Failed to allocate memory."
  elseif err == iconv.ERROR_UNKNOWN then
    return "ERROR: There was an unknown error."
  end
  return ostr
end
function inser_str_for(in_num)
--本函数用在长串数字中插入空格
if in_num == nil then
   out_str = 'error in_num'
else
    -- print("正在转换:" .. in_num .. "\n")
    in_num_len = string.len(in_num)
    tmp_str = string.sub(in_num,1,1)
 for start_len=2,in_num_len,1 do
     sub_str = string.sub(in_num,start_len,start_len)
     tmp_str = tmp_str .. " " .. sub_str
 end
     out_str = tmp_str
end
return out_str
end
-- require "resources.functions.config";
local Database = require "resources.functions.database";
dbh = Database.new('switch');  --调用freeswitch数据库
api = freeswitch.API();
session:answer();
if (session:ready() == true) then
 -- orderid = session:playAndGetDigits(8, 8, 3, 5000, '#', 'first sound:#', 'error_try sound', '\\d+|#');
 orderid = session:playAndGetDigits(12, 12, 3, 8000, '#', 'misc/enter_carryid.wav', 'misc/erro_carryid.wav', '\\d+|#')
end
if (orderid== nil) then
error_code = 0
else 
error_code = 1
end
if (error_code == 1)  then
    session:execute( "playback", "misc/carry_querying.wav" )
    local params = {carry_id_in = orderid}
    local sql = "SELECT * from carry_staut t where t.carry_id= :carry_id_in;"
    dbh:query(sql, params, function(row)
carry_staut = row.info; --返回字段映射
end);
if carry_staut==nil then 
session:streamFile("misc/erro_carryid.wav")
else
  -- out_orderid=inser_str_for(orderid) -- 当使用微软音库时需转换
  out_orderid=orderid -- 当使用"VM Hui"音库时无需转换
  tts_text = "您好,单号:" .. out_orderid ..",7月11日,您从广州寄往深圳的快件当前状态:".. carry_staut ..",签收人李先生,感谢使用顺丰!"
  tts_text = createIconv("utf-8","gbk",tts_text)       --调用函数将字符集转换为gbk
  session:setVariable("sip_h_X-Text",tts_text)
  tts_session = freeswitch.Session("sofia/gateway/80fd35d7-d767-4fb3-907f-72daaa7896ea/1098", session)
  if (tts_session:ready() == true) then
  freeswitch.bridge(session,tts_session)
  tts_session:hangup()
  end
  end
else
session:streamFile("misc/erro_carryid.wav")
end
session:sleep(500)
local digits = session:playAndGetDigits(1, 1, 3, 8000, '', 'misc/continue_carryid.wav', 'ivr/ivr-that_was_an_invalid_entry', '[\\d{1}|*]')
if (digits == "1") then
    session:execute("transfer","6200");
else
    i