marytts.client.http.MaryHttpClient Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2000-2006 DFKI GmbH.
* All Rights Reserved. Use is subject to license terms.
*
* This file is part of MARY TTS.
*
* MARY TTS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
*/
package marytts.client.http;
// General Java Classes
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioFormat.Encoding;
import javax.sound.sampled.UnsupportedAudioFileException;
import marytts.client.AudioFormatOutputStream;
import marytts.client.MaryClient;
import marytts.client.MaryGUIClient;
import marytts.util.http.Address;
import marytts.util.io.FileUtils;
import marytts.util.string.StringUtils;
/**
* An HTTP client implementing the MARY protocol. It can be used as a command line client or from within java code.
*
* @author Marc Schröder, oytun.turk
* @see MaryGUIClient A GUI interface to this client
* @see marytts.server.MaryServer Description of the MARY protocol.
*/
public class MaryHttpClient extends MaryClient {
/**
* The simplest way to create a mary client. It will connect to the MARY server running at DFKI. Only use this for testing
* purposes!
*
* @throws IOException
* if communication with the server fails
*/
public MaryHttpClient() throws IOException {
super();
}
public MaryHttpClient(boolean quiet) throws IOException {
super(quiet);
}
/**
* The typical way to create a mary client. It will connect to the MARY client running at the given host and port. This
* constructor reads two system properties:
*
* mary.client.profile
(=true/false) - determines whether profiling (timing) information is calculated;
* mary.client.quiet
(=true/false) - tells the client not to print any of the normal information to stderr.
*
*
* @param serverAddress
* the address of the server
* @throws IOException
* if communication with the server fails
*/
public MaryHttpClient(Address serverAddress) throws IOException {
super(serverAddress);
}
/**
* An alternative way to create a mary client, which works with applets. It will connect to the MARY client running at the
* given host and port. Note that in applets, the host must be the same as the one from which the applet was loaded;
* otherwise, a security exception is thrown.
*
* @param serverAddress
* the address of the server
* @param profile
* determines whether profiling (timing) information is calculated
* @param quiet
* tells the client not to print any of the normal information to stderr
* @throws IOException
* if communication with the server fails
*/
public MaryHttpClient(Address serverAddress, boolean profile, boolean quiet) throws IOException {
super(serverAddress, profile, quiet);
}
// /////////////////////////////////////////////////////////////////////
// ////////////////////// Information requests /////////////////////////
// /////////////////////////////////////////////////////////////////////
@Override
protected void fillAudioFileFormatAndOutTypes() throws IOException {
String audioFormatInfo = serverInfoRequest("audioformats", null);
data.audioOutTypes = new Vector(Arrays.asList(StringUtils.toStringArray(audioFormatInfo)));
data.audioFileFormatTypes = new Vector();
for (String af : data.audioOutTypes) {
if (af.endsWith("_FILE")) {
String typeName = af.substring(0, af.indexOf("_"));
try {
AudioFileFormat.Type type = MaryClient.getAudioFileFormatType(typeName);
data.audioFileFormatTypes.add(typeName + " " + type.getExtension());
} catch (Exception e) {
}
}
}
}
@Override
protected void fillServerVersion() throws IOException {
data.toServerVersionInfo(serverInfoRequest("version", null));
}
@Override
protected void fillDataTypes() throws IOException {
data.toDataTypes(serverInfoRequest("datatypes", null));
}
protected void fillVoices() throws IOException {
data.toVoices(serverInfoRequest("voices", null));
}
@Override
protected void fillLocales() throws IOException {
data.toLocales(serverInfoRequest("locales", null));
}
@Override
protected void fillVoiceExampleTexts(String voicename) throws IOException {
Map queryItems = new HashMap();
queryItems.put("voice", voicename);
String info = serverInfoRequest("exampletext", queryItems);
if (info.length() == 0)
throw new IOException("Could not get example text from Mary server");
StringTokenizer st = new StringTokenizer(info, "\n");
Vector sentences = new Vector();
while (st.hasMoreTokens()) {
sentences.add(st.nextToken());
}
data.voiceExampleTextsLimitedDomain.put(voicename, sentences);
}
@Override
protected void fillServerExampleText(String dataType, String locale) throws IOException {
Map queryItems = new HashMap();
queryItems.put("datatype", dataType);
queryItems.put("locale", locale);
String info = serverInfoRequest("exampletext", queryItems);
if (info.length() == 0)
throw new IOException("Could not get example text from Mary server");
data.serverExampleTexts.put(dataType + " " + locale, info.replaceAll("\n", System.getProperty("line.separator")));
}
/**
* Request the available audio effects for a voice from the server
*
* @return A string of available audio effects and default parameters, i.e. "FIRFilter,Robot(amount=50)"
* @throws IOException
* IOException
*/
@Override
protected String getDefaultAudioEffects() throws IOException {
return serverInfoRequest("audioeffects", null);
}
@Override
public String requestDefaultEffectParameters(String effectName) throws IOException {
Map queryItems = new HashMap();
queryItems.put("effect", effectName);
String info = serverInfoRequest("audioeffect-default-param", queryItems);
if (info == null || info.length() == 0)
return "";
return info.replaceAll("\n", System.getProperty("line.separator"));
}
@Override
public String requestFullEffect(String effectName, String currentEffectParameters) throws IOException {
Map queryItems = new HashMap();
queryItems.put("effect", effectName);
queryItems.put("params", currentEffectParameters);
String info = serverInfoRequest("audioeffect-full", queryItems);
if (info.length() == 0)
return "";
return info.replaceAll("\n", System.getProperty("line.separator"));
}
@Override
protected void fillEffectHelpText(String effectName) throws IOException {
Map queryItems = new HashMap();
queryItems.put("effect", effectName);
String info = serverInfoRequest("audioeffect-help", queryItems);
data.audioEffectHelpTextsMap.put(effectName, info.replaceAll("\n", System.getProperty("line.separator")));
}
@Override
public boolean isHMMEffect(String effectName) throws IOException {
Map queryItems = new HashMap();
queryItems.put("effect", effectName);
String info = serverInfoRequest("audioeffect-is-hmm-effect", queryItems);
if (info.length() == 0)
return false;
boolean bRet = false;
info = info.toLowerCase();
if (info.indexOf("yes") > -1)
bRet = true;
return bRet;
}
public String getFeatures(String locale) throws IOException {
return serverInfoRequest("features?locale=" + locale, null);
}
public String getDiscreteFeatures(String locale) throws IOException {
return serverInfoRequest("features-discrete?locale=" + locale, null);
}
public String getFeaturesForVoice(String voice) throws IOException {
return serverInfoRequest("features?voice=" + voice, null);
}
private String serverInfoRequest(String request, Map queryItems) throws IOException {
StringBuilder url = new StringBuilder();
url.append(data.hostAddress.getHttpAddress()).append("/").append(request);
if (queryItems != null) {
url.append("?");
boolean first = true;
for (String key : queryItems.keySet()) {
if (first)
first = false;
else
url.append("&");
url.append(key).append("=");
url.append(URLEncoder.encode(queryItems.get(key), "UTF-8"));
}
}
return serverInfoRequest(new URL(url.toString()));
}
private String serverInfoRequest(URL url) throws IOException {
HttpURLConnection http = (HttpURLConnection) url.openConnection();
http.setRequestMethod("GET");
http.connect();
if (http.getResponseCode() != HttpURLConnection.HTTP_OK) {
String errorData = "";
try {
errorData = FileUtils.getStreamAsString(http.getErrorStream(), "UTF-8");
} catch (Exception e) {
}
throw new IOException(http.getResponseCode() + ":" + http.getResponseMessage() + "\n" + errorData);
}
return FileUtils.getStreamAsString(http.getInputStream(), "UTF-8");
/*
* The following is example code if we were to use HttpClient: HttpClient httpclient = new DefaultHttpClient();
*
* HttpGet httpget = new HttpGet("http://www.google.com/");
*
* System.out.println("executing request " + httpget.getURI());
*
* // Create a response handler ResponseHandler responseHandler = new BasicResponseHandler(); String responseBody
* = httpclient.execute(httpget, responseHandler); System.out.println(responseBody);
*/
}
/*
* private String getFromServer(String key) throws IOException { return getFromServer(key, null); }
*
* //This handles each request one by one private String getFromServer(String key, String params) throws IOException { if
* (data.keyValuePairs==null) data.keyValuePairs = new HashMap();
*
* Map singleKeyValuePair = new HashMap();
*
* if (params==null || params.length()<1) singleKeyValuePair.put(key, "?"); else singleKeyValuePair.put(key, "? " + params);
*
* singleKeyValuePair = httpRequester.request(data.hostAddress, singleKeyValuePair);
*
* data.keyValuePairs.put(key, singleKeyValuePair.get(key));
*
* return data.keyValuePairs.get(key); }
*/
// /////////////////////////////////////////////////////////////////////
// ////////////////////// Actual synthesis requests ////////////////////
// /////////////////////////////////////////////////////////////////////
private InputStream requestInputStream(String input, String inputType, String outputType, String locale, String audioType,
String defaultVoiceName, String defaultStyle, Map effects, // String defaultEffects,
boolean streamingAudio, String outputTypeParams) throws IOException {
StringBuilder params = new StringBuilder();
params.append("INPUT_TEXT=").append(URLEncoder.encode(input, "UTF-8"));
params.append("&INPUT_TYPE=").append(URLEncoder.encode(inputType, "UTF-8"));
params.append("&OUTPUT_TYPE=").append(URLEncoder.encode(outputType, "UTF-8"));
if (locale != null) {
params.append("&LOCALE=").append(URLEncoder.encode(locale, "UTF-8"));
}
if (audioType != null) {
params.append("&AUDIO=").append(
URLEncoder.encode((streamingAudio && data.serverCanStream) ? audioType + "_STREAM" : audioType + "_FILE",
"UTF-8"));
}
if (outputTypeParams != null) {
params.append("&OUTPUT_TYPE_PARAMS=").append(URLEncoder.encode(outputTypeParams, "UTF-8"));
}
if (defaultVoiceName != null) {
params.append("&VOICE=").append(URLEncoder.encode(defaultVoiceName, "UTF-8"));
}
if (defaultStyle != null) {
params.append("&STYLE=").append(URLEncoder.encode(defaultStyle, "UTF-8"));
}
if (effects != null) {
for (String key : effects.keySet()) {
params.append("&").append(key).append("=").append(URLEncoder.encode(effects.get(key), "UTF-8"));
}
}
// to make HTTP Post request with HttpURLConnection
URL url = new URL(data.hostAddress.getHttpAddress() + "/process");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setAllowUserInteraction(false); // no user interact [like pop up]
conn.setDoOutput(true); // want to send
conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
OutputStream ost = conn.getOutputStream();
PrintWriter pw = new PrintWriter(ost);
pw.print(params.toString()); // here we "send" our body!
pw.flush();
pw.close();
// and InputStream from here will be body
try {
return conn.getInputStream();
} catch (IOException e) {
String error;
try {
error = FileUtils.getStreamAsString(conn.getErrorStream(), "UTF-8");
} catch (IOException errE) {
// ok cannot get error message, just re-throw original e
throw new IOException("No detailed error message available", e);
}
throw new IOException("Error message from server:\n" + error, e);
}
}
private Map effectsString2EffectsMap(String effectsString) {
if (effectsString == null)
return null;
Map effectsMap = new HashMap();
StringTokenizer st = new StringTokenizer(effectsString, ",");
while (st.hasMoreTokens()) {
String oneEffect = st.nextToken().trim();
String name;
String params;
int iBracket = oneEffect.indexOf('(');
if (iBracket > -1) {
name = oneEffect.substring(0, iBracket);
params = oneEffect.substring(iBracket + 1, oneEffect.indexOf(')', iBracket + 1));
} else { // no parameters
name = oneEffect;
params = "";
}
effectsMap.put("effect_" + name + "_selected", "on");
effectsMap.put("effect_" + name + "_parameters", params);
}
return effectsMap;
}
@Override
protected void _process(String input, String inputType, String outputType, String locale, String audioType,
String defaultVoiceName, String defaultStyle, String defaultEffects, Object output, long timeout,
boolean streamingAudio, String outputTypeParams, AudioPlayerListener playerListener) throws IOException {
boolean isMaryAudioPlayer = false;
if (output instanceof marytts.util.data.audio.AudioPlayer) {
isMaryAudioPlayer = true;
} else if (output instanceof OutputStream) {
} else {
throw new IllegalArgumentException("Expected OutputStream or AudioPlayer, got " + output.getClass().getName());
}
final long startTime = System.currentTimeMillis();
final InputStream fromServerStream = requestInputStream(input, inputType, outputType, locale, audioType,
defaultVoiceName, defaultStyle, effectsString2EffectsMap(defaultEffects), streamingAudio, outputTypeParams);
// If timeout is > 0, create a timer. It will close the input stream,
// thus causing an IOException in the reading code.
final Timer timer;
if (timeout <= 0) {
timer = null;
} else {
timer = new Timer();
TimerTask timerTask = new TimerTask() {
public void run() {
System.err.println("Timer closes connection");
/*
* try { maryDataSocket.close(); } catch (IOException ioe) { ioe.printStackTrace(); }
*/
}
};
timer.schedule(timerTask, timeout);
}
if (isMaryAudioPlayer) {
final marytts.util.data.audio.AudioPlayer player = (marytts.util.data.audio.AudioPlayer) output;
final AudioPlayerListener listener = playerListener;
Thread t = new Thread() {
public void run() {
try {
InputStream in = fromServerStream;
if (doProfile)
System.err.println("After " + (System.currentTimeMillis() - startTime)
+ " ms: Trying to read data from server");
in = new BufferedInputStream(in);
in.mark(1000);
AudioInputStream fromServerAudio = AudioSystem.getAudioInputStream(in);
if (fromServerAudio.getFrameLength() == 0) { // weird bug under Java 1.4
// in.reset();
fromServerAudio = new AudioInputStream(in, fromServerAudio.getFormat(), AudioSystem.NOT_SPECIFIED);
}
// System.out.println("Audio framelength: "+fromServerAudio.getFrameLength());
// System.out.println("Audio frame size: "+fromServerAudio.getFormat().getFrameSize());
// System.out.println("Audio format: "+fromServerAudio.getFormat());
if (doProfile)
System.err.println("After " + (System.currentTimeMillis() - startTime) + " ms: Audio available: "
+ in.available());
AudioFormat audioFormat = fromServerAudio.getFormat();
if (!audioFormat.getEncoding().equals(Encoding.PCM_SIGNED)) { // need conversion, e.g. for mp3
audioFormat = new AudioFormat(fromServerAudio.getFormat().getSampleRate(), 16, 1, true, false);
fromServerAudio = AudioSystem.getAudioInputStream(audioFormat, fromServerAudio);
}
player.setAudio(fromServerAudio);
player.run(); // not start(), i.e. execute in this thread
if (timer != null)
timer.cancel();
if (listener != null)
listener.playerFinished();
// toServerInfo.close();
// fromServerInfo.close();
// maryInfoSocket.close();
// toServerData.close();
// maryDataSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
// try {
// warningReader.join();
// } catch (InterruptedException ie) {}
// if (warningReader.getWarnings().length() > 0) { // there are warnings
// String warnings = warningReader.getWarnings();
// System.err.println(warnings);
// if (listener != null) listener.playerException(new IOException(warnings));
// }
if (doProfile) {
long endTime = System.currentTimeMillis();
long processingTime = endTime - startTime;
System.err.println("Processed request in " + processingTime + " ms.");
}
}
};
if (streamingAudio)
t.start();
else
t.run(); // execute code in the current thread
} else // output is an OutputStream
{
OutputStream os = (OutputStream) output;
InputStream bis = new BufferedInputStream(fromServerStream);
if (os instanceof AudioFormatOutputStream) {
AudioFormatOutputStream afos = (AudioFormatOutputStream) os;
try {
AudioFileFormat format = AudioSystem.getAudioFileFormat(bis);
afos.setFormat(format.getFormat());
} catch (UnsupportedAudioFileException e) {
throw new IOException(e.getMessage(), e);
}
}
byte[] bbuf = new byte[1024];
int nr;
while ((nr = bis.read(bbuf, 0, bbuf.length)) != -1) {
// System.err.println("Read " + nr + " bytes from server.");
os.write(bbuf, 0, nr);
}
os.flush();
if (timeout > 0)
timer.cancel();
// toServerInfo.close();
// fromServerInfo.close();
// maryInfoSocket.close();
// toServerData.close();
// maryDataSocket.close();
// try {
// warningReader.join();
// } catch (InterruptedException ie) {}
// if (warningReader.getWarnings().length() > 0) // there are warnings
// throw new IOException(warningReader.getWarnings());
if (doProfile) {
long endTime = System.currentTimeMillis();
long processingTime = endTime - startTime;
System.err.println("Processed request in " + processingTime + " ms.");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy