marytts.server.http.SynthesisRequestHandler Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2007 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.server.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import marytts.datatypes.MaryDataType;
import marytts.modules.synthesis.Voice;
import marytts.server.Request;
import marytts.server.RequestHandler.StreamingOutputPiper;
import marytts.server.RequestHandler.StreamingOutputWriter;
import marytts.util.MaryRuntimeUtils;
import marytts.util.MaryUtils;
import marytts.util.data.audio.MaryAudioUtils;
import marytts.util.http.Address;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.log4j.Logger;
/**
* Provides functionality to process synthesis http requests
*
* @author Oytun Türk
*
*/
public class SynthesisRequestHandler extends BaseHttpRequestHandler {
private static int id = 0;
private static synchronized int getId() {
return id++;
}
private StreamingOutputWriter outputToStream;
private StreamingOutputPiper streamToPipe;
private PipedOutputStream pipedOutput;
private PipedInputStream pipedInput;
public SynthesisRequestHandler() {
super();
outputToStream = null;
streamToPipe = null;
pipedOutput = null;
pipedInput = null;
}
@Override
protected void handleClientRequest(String absPath, Map queryItems, HttpResponse response,
Address serverAddressAtClient) throws IOException {
/*
* response.setStatusCode(HttpStatus.SC_OK); TestProducingNHttpEntity entity = new TestProducingNHttpEntity();
* entity.setContentType("audio/x-mp3"); response.setEntity(entity); if (true) return;
*/
logger.debug("New synthesis request: " + absPath);
if (queryItems != null) {
for (String key : queryItems.keySet()) {
logger.debug(" " + key + "=" + queryItems.get(key));
}
}
process(serverAddressAtClient, queryItems, response);
}
public void process(Address serverAddressAtClient, Map queryItems, HttpResponse response) {
if (queryItems == null
|| !(queryItems.containsKey("INPUT_TYPE") && queryItems.containsKey("OUTPUT_TYPE")
&& queryItems.containsKey("LOCALE") && queryItems.containsKey("INPUT_TEXT"))) {
MaryHttpServerUtils.errorMissingQueryParameter(response,
"'INPUT_TEXT' and 'INPUT_TYPE' and 'OUTPUT_TYPE' and 'LOCALE'");
return;
}
String inputText = queryItems.get("INPUT_TEXT");
MaryDataType inputType = MaryDataType.get(queryItems.get("INPUT_TYPE"));
if (inputType == null) {
MaryHttpServerUtils.errorWrongQueryParameterValue(response, "INPUT_TYPE", queryItems.get("INPUT_TYPE"), null);
return;
}
MaryDataType outputType = MaryDataType.get(queryItems.get("OUTPUT_TYPE"));
if (outputType == null) {
MaryHttpServerUtils.errorWrongQueryParameterValue(response, "OUTPUT_TYPE", queryItems.get("OUTPUT_TYPE"), null);
return;
}
boolean isOutputText = true;
boolean streamingAudio = false;
AudioFileFormat.Type audioFileFormatType = null;
if (outputType.name().contains("AUDIO")) {
isOutputText = false;
String audioTypeName = queryItems.get("AUDIO");
if (audioTypeName == null) {
MaryHttpServerUtils.errorMissingQueryParameter(response, "'AUDIO' when OUTPUT_TYPE=AUDIO");
return;
}
if (audioTypeName.endsWith("_STREAM")) {
streamingAudio = true;
}
int lastUnderscore = audioTypeName.lastIndexOf('_');
if (lastUnderscore != -1) {
audioTypeName = audioTypeName.substring(0, lastUnderscore);
}
try {
audioFileFormatType = MaryAudioUtils.getAudioFileFormatType(audioTypeName);
} catch (Exception ex) {
}
if (audioFileFormatType == null) {
MaryHttpServerUtils.errorWrongQueryParameterValue(response, "AUDIO", queryItems.get("AUDIO"), null);
return;
} else if (audioFileFormatType.toString().equals("MP3") && !MaryRuntimeUtils.canCreateMP3()) {
MaryHttpServerUtils.errorWrongQueryParameterValue(response, "AUDIO", queryItems.get("AUDIO"),
"Conversion to MP3 not supported.");
return;
} else if (audioFileFormatType.toString().equals("Vorbis") && !MaryRuntimeUtils.canCreateOgg()) {
MaryHttpServerUtils.errorWrongQueryParameterValue(response, "AUDIO", queryItems.get("AUDIO"),
"Conversion to OGG Vorbis format not supported.");
return;
}
}
// optionally, there may be output type parameters
// (e.g., the list of features to produce for the output type TARGETFEATURES)
String outputTypeParams = queryItems.get("OUTPUT_TYPE_PARAMS");
Locale locale = MaryUtils.string2locale(queryItems.get("LOCALE"));
if (locale == null) {
MaryHttpServerUtils.errorWrongQueryParameterValue(response, "LOCALE", queryItems.get("LOCALE"), null);
return;
}
Voice voice = null;
String voiceName = queryItems.get("VOICE");
if (voiceName != null) {
if (voiceName.equals("male") || voiceName.equals("female")) {
voice = Voice.getVoice(locale, new Voice.Gender(voiceName));
} else {
voice = Voice.getVoice(voiceName);
}
if (voice == null) {
// a voice name was given but there is no such voice
MaryHttpServerUtils.errorWrongQueryParameterValue(response, "VOICE", queryItems.get("VOICE"), null);
return;
}
}
if (voice == null) { // no voice tag -- use locale default if it exists.
voice = Voice.getDefaultVoice(locale);
logger.debug("No voice requested -- using default " + voice);
}
String style = queryItems.get("STYLE");
if (style == null)
style = "";
String effects = toRequestedAudioEffectsString(queryItems);
if (effects.length() > 0)
logger.debug("Audio effects requested: " + effects);
else
logger.debug("No audio effects requested");
String logMsg = queryItems.get("LOG");
if (logMsg != null) {
logger.info("Connection info: " + logMsg);
}
// Now, the parse is complete.
// Construct audio file format -- even when output is not AUDIO,
// in case we need to pass via audio to get our output type.
if (audioFileFormatType == null) {
audioFileFormatType = AudioFileFormat.Type.AU;
}
AudioFormat audioFormat;
if (audioFileFormatType.toString().equals("MP3")) {
audioFormat = MaryRuntimeUtils.getMP3AudioFormat();
} else if (audioFileFormatType.toString().equals("Vorbis")) {
audioFormat = MaryRuntimeUtils.getOggAudioFormat();
} else if (voice != null) {
audioFormat = voice.dbAudioFormat();
} else {
audioFormat = Voice.AF16000;
}
AudioFileFormat audioFileFormat = new AudioFileFormat(audioFileFormatType, audioFormat, AudioSystem.NOT_SPECIFIED);
final Request maryRequest = new Request(inputType, outputType, locale, voice, effects, style, getId(), audioFileFormat,
streamingAudio, outputTypeParams);
// Process the request and send back the data
boolean ok = true;
try {
maryRequest.setInputData(inputText);
logger.info("Read: " + inputText);
} catch (Exception e) {
String message = "Problem reading input";
logger.warn(message, e);
MaryHttpServerUtils.errorInternalServerError(response, message, e);
ok = false;
}
if (ok) {
if (streamingAudio) {
// Start two separate threads:
// 1. one thread to process the request;
new Thread("RH " + maryRequest.getId()) {
public void run() {
Logger myLogger = MaryUtils.getLogger(this.getName());
try {
maryRequest.process();
myLogger.info("Streaming request processed successfully.");
} catch (Throwable t) {
myLogger.error("Processing failed.", t);
}
}
}.start();
// 2. one thread to take the audio data as it becomes available
// and write it into the ProducingNHttpEntity.
// The second one does not depend on the first one practically,
// because the AppendableSequenceAudioInputStream returned by
// maryRequest.getAudio() was already created in the constructor of Request.
AudioInputStream audio = maryRequest.getAudio();
assert audio != null : "Streaming audio but no audio stream -- very strange indeed! :-(";
AudioFileFormat.Type audioType = maryRequest.getAudioFileFormat().getType();
AudioStreamNHttpEntity entity = new AudioStreamNHttpEntity(maryRequest);
new Thread(entity, "HTTPWriter " + maryRequest.getId()).start();
// entity knows its contentType, no need to set explicitly here.
response.setEntity(entity);
response.setStatusCode(HttpStatus.SC_OK);
return;
} else { // not streaming audio
// Process input data to output data
try {
maryRequest.process(); // this may take some time
} catch (Throwable e) {
String message = "Processing failed.";
logger.error(message, e);
MaryHttpServerUtils.errorInternalServerError(response, message, e);
ok = false;
}
if (ok) {
// Write output data to client
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
maryRequest.writeOutputData(outputStream);
String contentType;
if (maryRequest.getOutputType().isXMLType() || maryRequest.getOutputType().isTextType()) // text output
contentType = "text/plain; charset=UTF-8";
else
// audio output
contentType = MaryHttpServerUtils.getMimeType(maryRequest.getAudioFileFormat().getType());
MaryHttpServerUtils.toHttpResponse(outputStream.toByteArray(), response, contentType);
} catch (Exception e) {
String message = "Cannot write output";
logger.warn(message, e);
MaryHttpServerUtils.errorInternalServerError(response, message, e);
ok = false;
}
}
}
}
if (ok)
logger.info("Request handled successfully.");
else
logger.info("Request couldn't be handled successfully.");
if (MaryRuntimeUtils.lowMemoryCondition()) {
logger.info("Low memory condition detected (only " + MaryUtils.availableMemory()
+ " bytes left). Triggering garbage collection.");
Runtime.getRuntime().gc();
logger.info("After garbage collection: " + MaryUtils.availableMemory() + " bytes available.");
}
}
protected String toRequestedAudioEffectsString(Map keyValuePairs) {
StringBuilder effects = new StringBuilder();
StringTokenizer tt;
Set keys = keyValuePairs.keySet();
String currentKey;
String currentEffectName, currentEffectParams;
for (Iterator it = keys.iterator(); it.hasNext();) {
currentKey = it.next();
if (currentKey.startsWith("effect_")) {
if (currentKey.endsWith("_selected")) {
if (keyValuePairs.get(currentKey).compareTo("on") == 0) {
if (effects.length() > 0)
effects.append("+");
tt = new StringTokenizer(currentKey, "_");
if (tt.hasMoreTokens())
tt.nextToken(); // Skip "effects_"
if (tt.hasMoreTokens()) // The next token is the effect name
{
currentEffectName = tt.nextToken();
currentEffectParams = keyValuePairs.get("effect_" + currentEffectName + "_parameters");
if (currentEffectParams != null && currentEffectParams.length() > 0)
effects.append(currentEffectName).append("(").append(currentEffectParams).append(")");
else
effects.append(currentEffectName);
}
}
}
}
}
return effects.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy