林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka
摘要:Text to Speech 服务提供的应用程序编程接口 (API) 使用 IBM 的语音合成功能将文本转换成音频信号。此服务提供了具象状态传输 (REST) 接口,支持将各种语言、口音和语音的文本合成自然语音。此服务目前可将英语或西班牙语的书面文本合成男声(英语和西班牙语)或女声(仅限英语)朗读的音频信号。音频可通过流式方法传输回客户机,延迟时间最短。
本文工程下载:http://download.csdn.net/detail/evankaka/9427964
本文实例访问:http://texttospeechtest.eu-gb.mybluemix.net/
一、Bluemix创建web工程并添加文本转语音服务
1、创建Bluemix上的web工程.(如何创建工程可看基于IBM Bluemix部署Java Web项目实战演练)
2、添加文本转语音服务
添加服务
3、最后绑定的服务的工程如下:
二、本地创建工程
这里需要创建一个dynamic 的web工程,本文是直接在https://github.com/watson-developer-cloud/text-to-speech-java这上工程的基础上来做修改的。
最终目录如下:
下面来分析一下关键的代码,这里是以Servlet来实现语音的获取的
(1)DemoServlet.java
这是本工程的核心代码
package com.ibm.cloudoe.samples;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import com.ibm.ws.xs.json.java.JSONArray;
import com.ibm.ws.xs.json.java.JSONObject;
@MultipartConfig
public class DemoServlet extends HttpServlet {
private static Logger logger = Logger.getLogger(DemoServlet.class.getName());
private static final long serialVersionUID = 1L;
private String serviceName = "text_to_speech";
// 此处需要修改成您自己的
private String baseURL = "https://stream.watsonplatform.net/text-to-speech/api";
private String username = "bd546fff-2e43-42c9-8c76-71075c257128";
private String password = "AL7sK4TRu0T0";
/**
* 处理前台传过来的请求
*/
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
if (req.getParameter("text") == null || req.getParameter("voice") == null) { //如果文本或语音为空,返回到初始界面
req.getRequestDispatcher("/index.jsp").forward(req, resp);
} else {
boolean download = false;
if (req.getParameter("download") != null && req.getParameter("download").equalsIgnoreCase("true")) { //判断是否下载语音
download = true;
}
req.setCharacterEncoding("UTF-8");
try {
String queryStr = req.getQueryString();
String url = baseURL + "/v1/synthesize";
if (queryStr != null) {
url += "?" + queryStr;//连接url
}
URI uri = new URI(url).normalize();
Request newReq = Request.Get(uri); //构造请求头
newReq.addHeader("Accept", "audio/ogg; codecs=opus"); //设置语音格式,还可以为"audio/wav"或者 "audio/flac"
Executor executor = Executor.newInstance().auth(username, password);
Response response = executor.execute(newReq);//发送请求报文,并取得返回
if (download)
{
resp.setHeader("content-disposition", "attachment; filename=transcript.ogg");//下载语音
}
ServletOutputStream servletOutputStream = resp.getOutputStream();
response.returnResponse().getEntity()
.writeTo(servletOutputStream);//将语音流返回前台
servletOutputStream.flush();
servletOutputStream.close();
} catch (Exception e) {
// Log something and return an error message
logger.log(Level.SEVERE, "got error: " + e.getMessage(), e);
resp.setStatus(HttpStatus.SC_BAD_GATEWAY);
}
}
}
/**
* 取得环境变量
*/
private JSONObject getVcapServices() {
String envServices = System.getenv("VCAP_SERVICES");
if (envServices == null) return null;
JSONObject sysEnv = null;
try {
sysEnv = JSONObject.parse(envServices);
} catch (IOException e) {
// Do nothing, fall through to defaults
logger.log(Level.SEVERE, "Error parsing VCAP_SERVICES: "+e.getMessage(), e);
}
return sysEnv;
}
@Override
public void init() throws ServletException {
super.init();
processVCAP_Services();
}
/**
* 取得环境变量的参数
*/
private void processVCAP_Services() {
logger.info("Processing VCAP_SERVICES");
JSONObject sysEnv = getVcapServices();
if (sysEnv == null) return;
logger.info("Looking for: "+ serviceName );
for (Object key : sysEnv.keySet()) {
String keyString = (String) key;
logger.info("found key: " + key);
if (keyString.startsWith(serviceName)) {
JSONArray services = (JSONArray)sysEnv.get(key);
JSONObject service = (JSONObject)services.get(0);
JSONObject credentials = (JSONObject)service.get("credentials");
baseURL = (String)credentials.get("url");
username = (String)credentials.get("username");
password = (String)credentials.get("password");
logger.info("baseURL = "+baseURL);
logger.info("username = "+username);
logger.info("password = "+password);
} else {
logger.info("Doesn't match /^"+serviceName+"/");
}
}
}
}
这里的原理其实就是前台传文本过来后,在doget方法中再构造request请求头到语音处理中心,它将结果返回到servlet,再返回到前台。
(2)前台关键代码
这是页面的内容:
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html lang="en">
<head>
<title>文本转语音实例</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<link rel="icon" href="images/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="css/watson-bootstrap-dark.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-lg-7 col-md-7 col-xs-12">
<h2>文本转语音</h2>
<div class="well">
<form method="get" class="form-horizontal">
<fieldset>
<div class="row">
<div class="col-lg-12 col-xs-12">
<label for="textArea" class="control-label">请输入或复制要转换成语音的文本:</label>
<textarea id="textArea" name="text" rows="8" required class="form-control"></textarea><span class="help-block"><small>注意只能输入现一种语言,语音返回会有点慢,请耐心等待</small></span>
</div>
</div>
<div style="margin-bottom:30px;" class="row">
<label for="voice" class="col-lg-12 col-xs-12 control-label">请选择语音类型:</label>
<div class="col-lg-12 col-xs-12">
<select class="select-voice" id="voice" style="width:100%" name="voice" required class="form-control">
</select>
</div>
</div>
<div class="row">
<div class="col-lg-4 col-xs-4 download-container">
<input value="Download" class="btn btn-block download-button">
</div>
<div class="col-lg-4 col-xs-4 text-center"></div>
<div class="col-lg-4 col-xs-4 ie-speak">
<input value="Speak" class="btn btn-block speak-button">
<div class="arrow-box">
<p>请使用谷歌或火狐浏览器</p>
</div>
</div>
</div>
</fieldset>
</form>
</div>
</div>
<div class="col-lg-5 col-md-5 col-xs-12">
<h2>语音输出</h2>
<div class="row">
<div class="col-lg-12 col-xs-12">
<div style="display:none" class="well result">
<div class="text-center">
<audio autoplay preload="auto" autobuffer controls class="audio"></audio>
</div>
<div><span class="help-block">返回的语音是以<a href="http://www.vorbis.com/">Ogg Vorbis</a> 格式的,它可以使用<a href="http://www.videolan.org/vlc/index.html">VLC</a>, <a href="http://audacity.sourceforge.net/">Audacity</a> 等来播放</span>
</div>
</div>
<div style="display:none" class="well error">
<div class="form-group row">
<div class="col-lg-12 col-xs-12">
<p class="errorMsg">处理请求失败</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="help-block text-center"><small>请使用谷歌浏览器 <a href="https://www.google.com/intl/en/chrome/browser/desktop/" target="_blank">Chrome </a>火狐浏览器 <a href="https://www.mozilla.org/en-US/firefox/new/" target="_blank">Firefox.</a></small>
</div>
</div>
</div>
<script type="text/javascript" src="js/browser-detect.js"></script>
<script type="text/javascript" src="js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="js/constants.js"></script>
<script type="text/javascript" src="js/demo.js"></script>
</body>
</html>
demo.js,引用的js文件内容:
'use strict';
//页面一加载完成就执行
$(function () {
var audio = $('.audio').get(0),//语音播放器
textArea = $('#textArea'); //文本内容
//为语音选择器添加内容
Object.keys(VOICES).forEach(function(key) {
$('<option>', { value : key })
.appendTo($('.select-voice'))
.text(VOICES[key]);
});
//设置初始文本内容
function updateSampleText() {
var lang = $('.select-voice').val().substr(0,5);
$('#textArea').text(SAMPLE_TEXT[lang]);
}
$('.select-voice').change(updateSampleText);
updateSampleText();
// IE 和 Safari 不支持此实例语音播放,让speak按钮 无效
if ($('body').hasClass('ie') || $('body').hasClass('safari')) {
$('.speak-button').prop('disabled', true);
}
if ($('.speak-button').prop('disabled')) {
$('.ie-speak .arrow-box').show();
}
//播放错误信息
$('.audio').on('error', function () {
$('.result').hide();
$('.errorMgs').text('Error processing the request.');
$('.errorMsg').css('color','red');
$('.error').show();
});
//播放语音
$('.audio').on('loadeddata', function () {
$('.result').show();
$('.error').hide();
});
//download点击事件
$('.download-button').click(function() {
textArea.focus();
if (validText(textArea.val())) {
window.location.href = '?download=true&' + $('form').serialize();
}
});
//speak按钮点击事件
$('.speak-button').click(function() {
$('.result').hide();
audio.pause();
$('#textArea').focus();
if (validText(textArea.val())) {
audio.setAttribute('src','?&' + $('form').serialize());
}
});
//校验文本的输入内容
function validText(text) {
$('.error').hide();
$('.errorMsg').css('color','#5a5a5a');
if ($.trim(text).length === 0) {
$('.errorMsg').text('Please enter the text you would like to synthesize in the text window.');
$('.errorMsg').css('color','#5a5a5a');
$('.error').show();
return false;
}
if (!containsAllLatin1(text)) {
$('.errorMsg').text('Language not supported. Please use only ISO 8859 characters');
$('.error').show();
return false;
}
return true;
}
/**
* Check that the text doesn't contains non latin-1 characters.
* @param String The string to test
* @return true if the string is latin-1
*/
function containsAllLatin1(str) {
return /^[A-z\u00C0-\u00ff\s?@¿''\.,-\/#!$%\^&\*;:{}=\-_`~()0-9]+$/.test(str);
}
});
其它具体的请看工程代码,这里不再做分析。
(3)添加jar包
本地运行需要添加如下jar包,记得!
其中json-org.jar和ogclient.jar可到这里下载ftp://public.dhe.ibm.com/cloud/bluemix/datacache/
同时工程右键设置属性中,编辑的JDK选择1.6,web版本选择2.5
(4)本地发布运行
右键运行即可,
输入http://localhost:8088/app/
测试过后没有问题。下一步发布到个人bluemix中心。
三、发布到个人bluemix中心
(1)打包war包
因为本工程使用了ant.其实内容如下:
build.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE xml>
<project basedir="." default="build" name="WebApp">
<property environment="env" />
<property name="srcDir" value="." />
<property name="debuglevel" value="source,lines,vars" />
<property name="target" value="1.6" />
<property name="source" value="1.6" />
<property name="ARCHIVE_DIR" value="output" />
<property name="LIB_DIR" value="./lib" />
<property name="WEB_INF_LIB_DIR" value="./WebContent/WEB-INF/lib" />
<property name="warname" value="webApp.war" />
<path id="classpathDir">
<pathelement location="build/bin" />
<fileset dir="${LIB_DIR}">
<include name="*.jar" />
</fileset>
<fileset dir="${WEB_INF_LIB_DIR}">
<include name="*.jar" />
</fileset>
</path>
<target name="init">
<mkdir dir="build" />
<mkdir dir="build/bin" />
</target>
<target name="clean">
<delete dir="build" />
<delete file="${ARCHIVE_DIR}/${warname}" />
</target>
<target name="build" depends="build-project,build-war" />
<target name="cleanall" depends="clean" />
<target name="build-project" depends="clean,init">
<copy todir="${ARCHIVE_DIR}">
<fileset file="manifest.yml" />
</copy>
<echo message="${ant.project.name}: ${ant.file}" />
<javac debug="true" debuglevel="${debuglevel}" destdir="build/bin" source="${source}" target="${target}" includeantruntime="false">
<src path="src" />
<classpath refid="classpathDir" />
</javac>
</target>
<target name="build-war" depends="build-project">
<war destfile="${ARCHIVE_DIR}/${warname}" webxml="WebContent/WEB-INF/web.xml">
<webinf dir="WebContent/WEB-INF" />
<zipfileset dir="src" prefix="WEB-INF/src" />
<fileset dir="WebContent">
<include name="**/*" />
</fileset>
<lib dir="WebContent/WEB-INF/lib" />
<classes dir="build/bin" />
</war>
</target>
</project>
另外,要发布到bluemix中去,本工程还配置了初始的参数
---
declared-services:
text-to-speech-service:
label: text_to_speech
plan: standard
applications:
- services:
- text-to-speech-service
name: TextToSpeechTest #改成你的Bluxmix中心上对应的web工程名
path: webApp.war #改成你的war包名,上传时这个注释要去掉
memory: 512M
现在来打包下:
打包后的输出路径:
(2)上传到个个Bluxmix中心
登陆
cf login
输入用户名、邮箱、选择工程空间(记得选择你此次web项目所在的空间)
上传
cf push TextToSpeechTest -p D:\marJee-workspace\text-to-speech-java-master\output\webApp.war -m 512M
最后,打开网址:http://texttospeechtest.eu-gb.mybluemix.net/
点击Speak按钮,即可!注意,等待语音返回比较慢,请耐心等待!注意,等待语音返回比较慢,请耐心等待!注意,等待语音返回比较慢,请耐心等待!