/**
* Copyright 2002 DFKI GmbH.
* All Rights Reserved. Use is subject to license terms.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
*/
package de.dfki.lt.freetts.mbrola;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import com.sun.speech.freetts.Item;
import com.sun.speech.freetts.ProcessException;
import com.sun.speech.freetts.Relation;
import com.sun.speech.freetts.Utterance;
import com.sun.speech.freetts.UtteranceProcessor;
import com.sun.speech.freetts.util.Utilities;
/**
* Calls external MBROLA binary to synthesise the utterance.
*/
public class MbrolaCaller implements UtteranceProcessor {
private String[] cmd;
private long closeDelay = 0l;
/**
* Create an Mbrola caller which will call an external MBROLA binary
* using the command <code>cmd</code>. The command string is used
* as it is, which means that it must contain full path specifications
* and the correct file separators.
*/
public MbrolaCaller(String[] cmd) {
this.cmd = cmd;
closeDelay = Utilities.getLong
("de.dfki.lt.freetts.mbrola.MbrolaCaller.closeDelay",
100L).longValue();
}
/**
* Call external MBROLA binary to synthesize the utterance.
*
* @param utterance the utterance to process
*
* @throws ProcessException if an error occurs while
* processing of the utterance
*/
public void processUtterance(Utterance utterance) throws ProcessException {
// Go through Segment relation and print values into Mbrola
Relation segmentRelation = utterance.getRelation(Relation.SEGMENT);
Item segment = segmentRelation.getHead();
if (segment == null) {
return;
}
// Open Mbrola
Process process;
try {
process = Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
throw new ProcessException("Cannot start mbrola program: " + cmd);
}
PrintWriter toMbrola = new PrintWriter(process.getOutputStream());
BufferedInputStream fromMbrola =
new BufferedInputStream(process.getInputStream());
while (segment != null) {
String name = segment.getFeatures().getString("name");
// Individual duration of segment, in milliseconds:
int dur = segment.getFeatures().getInt("mbr_dur");
// List of time-f0 targets. In each target, the first value
// indicates where on the time axis the target is reached,
// expressed in percent of segment duration; the second value in
// each pair is f0, in Hz.
String targets = segment.getFeatures().getString("mbr_targets");
String output = (name + " " + dur + " " + targets);
// System.out.println(output);
toMbrola.println(output);
segment = segment.getNext();
}
toMbrola.flush();
// BUG:
// There is a bug that causes the final 'close' on a stream
// going to a sub-process to not be seen by the sub-process on
// occasion. This seems to occur mainly when the close occurs
// very soon after the creation and writing of data to the
// sub-process. This delay can help work around the problem
// If we delay before the close by
// a small amount (100ms), the hang is averted. This is a WORKAROUND
// only and should be removed once the bug in the 'exec' is
// fixed. We get the delay from the property:
//
// de.dfki.lt.freetts.mbrola.MbrolaCaller.closeDelay,
//
if (closeDelay > 0l) {
try {
Thread.sleep(closeDelay);
} catch (InterruptedException ie) {
}
}
toMbrola.close();
// reading the audio output
byte[] buffer = new byte[1024];
// In order to avoid resizing a large array, we save the audio data
// in the chunks in which we read it.
List audioData = new java.util.ArrayList();
int totalSize = 0;
int nrRead = -1; // -1 means end of file
try {
while ((nrRead = fromMbrola.read(buffer)) != -1) {
if (nrRead < buffer.length) {
byte[] slice = new byte[nrRead];
System.arraycopy(buffer, 0, slice, 0, nrRead);
audioData.add(slice);
} else {
audioData.add(buffer);
buffer = new byte[buffer.length];
}
totalSize += nrRead;
}
fromMbrola.close();
} catch (IOException e) {
throw new ProcessException("Cannot read from mbrola");
}
if (totalSize == 0) {
throw new ProcessException("No audio data read");
}
utterance.setObject("mbrolaAudio", audioData);
utterance.setInt("mbrolaAudioLength", totalSize);
}
public String toString() {
return "MbrolaCaller";
}
}