001/** 002 * Copyright 2002 DFKI GmbH. 003 * All Rights Reserved. Use is subject to license terms. 004 * 005 * See the file "license.terms" for information on usage and 006 * redistribution of this file, and for a DISCLAIMER OF ALL 007 * WARRANTIES. 008 */ 009 010package de.dfki.lt.freetts.mbrola; 011 012import java.io.BufferedInputStream; 013import java.io.IOException; 014import java.io.PrintWriter; 015import java.util.List; 016 017import com.sun.speech.freetts.Item; 018import com.sun.speech.freetts.ProcessException; 019import com.sun.speech.freetts.Relation; 020import com.sun.speech.freetts.Utterance; 021import com.sun.speech.freetts.UtteranceProcessor; 022import com.sun.speech.freetts.util.Utilities; 023 024/** 025 * Calls external MBROLA binary to synthesise the utterance. 026 */ 027public class MbrolaCaller implements UtteranceProcessor { 028 029 private String[] cmd; 030 private long closeDelay = 0l; 031 032 /** 033 * Create an Mbrola caller which will call an external MBROLA binary 034 * using the command <code>cmd</code>. The command string is used 035 * as it is, which means that it must contain full path specifications 036 * and the correct file separators. 037 */ 038 public MbrolaCaller(String[] cmd) { 039 this.cmd = cmd; 040 closeDelay = Utilities.getLong 041 ("de.dfki.lt.freetts.mbrola.MbrolaCaller.closeDelay", 042 100L).longValue(); 043 } 044 045 /** 046 * Call external MBROLA binary to synthesize the utterance. 047 * 048 * @param utterance the utterance to process 049 * 050 * @throws ProcessException if an error occurs while 051 * processing of the utterance 052 */ 053 public void processUtterance(Utterance utterance) throws ProcessException { 054 // Go through Segment relation and print values into Mbrola 055 Relation segmentRelation = utterance.getRelation(Relation.SEGMENT); 056 Item segment = segmentRelation.getHead(); 057 058 if (segment == null) { 059 return; 060 } 061 062 // Open Mbrola 063 Process process; 064 try { 065 process = Runtime.getRuntime().exec(cmd); 066 } catch (Exception e) { 067 throw new ProcessException("Cannot start mbrola program: " + cmd, 068 e); 069 } 070 PrintWriter toMbrola = new PrintWriter(process.getOutputStream()); 071 BufferedInputStream fromMbrola = 072 new BufferedInputStream(process.getInputStream()); 073 074 while (segment != null) { 075 String name = segment.getFeatures().getString("name"); 076 // Individual duration of segment, in milliseconds: 077 int dur = segment.getFeatures().getInt("mbr_dur"); 078 // List of time-f0 targets. In each target, the first value 079 // indicates where on the time axis the target is reached, 080 // expressed in percent of segment duration; the second value in 081 // each pair is f0, in Hz. 082 String targets = segment.getFeatures().getString("mbr_targets"); 083 String output = (name + " " + dur + " " + targets); 084 // System.out.println(output); 085 toMbrola.println(output); 086 segment = segment.getNext(); 087 } 088 089 toMbrola.flush(); 090 091 // BUG: 092 // There is a bug that causes the final 'close' on a stream 093 // going to a sub-process to not be seen by the sub-process on 094 // occasion. This seems to occur mainly when the close occurs 095 // very soon after the creation and writing of data to the 096 // sub-process. This delay can help work around the problem 097 // If we delay before the close by 098 // a small amount (100ms), the hang is averted. This is a WORKAROUND 099 // only and should be removed once the bug in the 'exec' is 100 // fixed. We get the delay from the property: 101 // 102 // de.dfki.lt.freetts.mbrola.MbrolaCaller.closeDelay, 103 // 104 105 if (closeDelay > 0l) { 106 try { 107 Thread.sleep(closeDelay); 108 } catch (InterruptedException ie) { 109 } 110 } 111 toMbrola.close(); 112 113 // reading the audio output 114 byte[] buffer = new byte[1024]; 115 116 // In order to avoid resizing a large array, we save the audio data 117 // in the chunks in which we read it. 118 119 List audioData = new java.util.ArrayList(); 120 int totalSize = 0; 121 int nrRead = -1; // -1 means end of file 122 123 try { 124 while ((nrRead = fromMbrola.read(buffer)) != -1) { 125 if (nrRead < buffer.length) { 126 byte[] slice = new byte[nrRead]; 127 System.arraycopy(buffer, 0, slice, 0, nrRead); 128 audioData.add(slice); 129 } else { 130 audioData.add(buffer); 131 buffer = new byte[buffer.length]; 132 } 133 totalSize += nrRead; 134 } 135 fromMbrola.close(); 136 } catch (IOException e) { 137 throw new ProcessException("Cannot read from mbrola", e); 138 } 139 140 if (totalSize == 0) { 141 throw new ProcessException("No audio data read"); 142 } 143 144 utterance.setObject("mbrolaAudio", audioData); 145 utterance.setInt("mbrolaAudioLength", totalSize); 146 } 147 148 public String toString() { 149 return "MbrolaCaller"; 150 } 151}