原文地址:http://blog.csdn.net/luhanglei/article/details/73246146
市場部同學忽然說,能不能在叮咚音箱的skill裏,用方言對用戶進行回覆。因爲叮咚音箱支持回覆一段媒體文件,所以應該具有可行性。查了下,支持方言的TTS,只找到了訊飛一家。但是他家的java SDK只有播放和下載兩種,而下載還是PCM格式的,因此需要把訊飛家SDK實現爲一種通過網址進行請求的模式。
隱藏BUFF:BAE環境下,只有特定的路徑是可以進行寫操作的,所以臨時文件路徑有要求。
最終效果:打開http://***.duapp.com/tts?text=你要說的話,即可獲取到一段wav音頻
1.導入訊飛SDK
把lib裏的兩個jar文件放到項目的Lib裏;
dll和so文件,通過git或者svn傳到ROOT.war所在的文件夾裏;
並按照百度官方的說明,配置好tomcat的路徑
2.servlet代碼如下
原理就是,利用訊飛的java API,把生成的PCM 文件放到bae允許進行寫操作的臨時路徑下,並轉成WAV格式,進行輸出。
請求參數只有一個,text,值就是要轉換的文字
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String text = request.getParameter("text");// 獲取需要說的話
File f = new File("/home/bae/" + text + ".wav");// bae允許的臨時文件目錄
// TODO Auto-generated method stub
SpeechUtility.createUtility(SpeechConstant.APPID + "=******** ");// 訊飛APPID
// 1.創建SpeechSynthesizer對象
SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer();
// 2.合成參數設置,詳見《MSC Reference Manual》SpeechSynthesizer 類
mTts.setParameter(SpeechConstant.VOICE_NAME, "xiaokun");// 設置發音人
mTts.setParameter(SpeechConstant.SPEED, "50");// 設置語速,範圍0~100
mTts.setParameter(SpeechConstant.PITCH, "50");// 設置語調,範圍0~100
mTts.setParameter(SpeechConstant.VOLUME, "100");// 設置音量,範圍0~100
// 3.開始合成
// 設置合成音頻保存位置
// 合成監聽器
MySynthesizeToUriListener synthesizeToUriListener = new MySynthesizeToUriListener();
mTts.synthesizeToUri(text, "/home/bae/" + text + ".pcm", synthesizeToUriListener);
while (!synthesizeToUriListener.isFinish()) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
try {
convertAudioFiles("/home/bae/" + text + ".pcm", "/home/bae/" + text + ".wav");
} catch (Exception e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
return;
}
// 處理請求
// 讀取要下載的文件
if (f.exists()) {
FileInputStream fis = new FileInputStream(f);
byte[] b = new byte[fis.available()];
fis.read(b);
response.setCharacterEncoding("utf-8");
response.setHeader("Content-type", "audio/x-wav");
// 獲取響應報文輸出流對象
ServletOutputStream out = response.getOutputStream();
// 輸出
out.write(b);
out.flush();
out.close();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
class MySynthesizeToUriListener implements SynthesizeToUriListener {
public boolean isFinish = false;
// progress爲合成進度0~100
public void onBufferProgress(int progress) {
if (progress == 100)
isFinish = true;
}
// 會話合成完成回調接口
// uri爲合成保存地址,error爲錯誤信息,爲null時表示合成會話成功
@Override
public void onSynthesizeCompleted(String uri, SpeechError error) {
isFinish = true;
}
@Override
public void onEvent(int arg0, int arg1, int arg2, int arg3, Object arg4, Object arg5) {
// TODO 自動生成的方法存根
}
public boolean isFinish() {
return isFinish;
}
public void setFinish(boolean isFinish) {
this.isFinish = isFinish;
}
}
private void convertAudioFiles(String src, String target) throws Exception {
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(target);
// 計算長度
byte[] buf = new byte[1024 * 4];
int size = fis.read(buf);
int PCMSize = 0;
while (size != -1) {
PCMSize += size;
size = fis.read(buf);
}
fis.close();
// 填入參數,比特率等等。這裏用的是16位單聲道 8000 hz
WaveHeader header = new WaveHeader();
// 長度字段 = 內容的大小(PCMSize) + 頭部字段的大小(不包括前面4字節的標識符RIFF以及fileLength本身的4字節)
header.fileLength = PCMSize + (44 - 8);
header.FmtHdrLeth = 16;
header.BitsPerSample = 16;
header.Channels = 1;
header.FormatTag = 0x0001;
header.SamplesPerSec = 16000;
header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);
header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
header.DataHdrLeth = PCMSize;
byte[] h = header.getHeader();
assert h.length == 44; // WAV標準,頭部應該是44字節
// write header
fos.write(h, 0, h.length);
// write data stream
fis = new FileInputStream(src);
size = fis.read(buf);
while (size != -1) {
fos.write(buf, 0, size);
size = fis.read(buf);
}
fis.close();
fos.close();
}
PCM轉碼感謝:http://blog.csdn.net/xiunai78/article/details/6867331
原文地址:http://blog.csdn.net/luhanglei/article/details/73246146