upstream version 1.2.2
[debian/freetts] / com / sun / speech / freetts / diphone / Diphone.java
1 /**
2  * Portions Copyright 2001 Sun Microsystems, Inc.
3  * Portions Copyright 1999-2001 Language Technologies Institute, 
4  * Carnegie Mellon University.
5  * All Rights Reserved.  Use is subject to license terms.
6  * 
7  * See the file "license.terms" for information on usage and
8  * redistribution of this file, and for a DISCLAIMER OF ALL 
9  * WARRANTIES.
10  */
11 package com.sun.speech.freetts.diphone;
12 import com.sun.speech.freetts.relp.Sample;
13
14 import java.nio.ByteBuffer;
15 import java.io.IOException;
16 import java.io.DataInputStream;
17 import java.io.DataOutputStream;
18
19
20 /**
21  * Represents two adjacent phones. A diphone is defined by its name,
22  * the set of audio data, and information used to help stitch diphones
23  * together. This class is immutable.
24  */
25 public class Diphone {
26     protected final static int MAGIC = 0xFACE0FF;
27     protected final static int ALIAS_MAGIC = 0xBABAF00;
28     protected final static int NAME_LENGTH = 8; 
29     private String name;
30     private int midPoint;
31     private Sample[] samples;
32     private int unitSizePart1;
33     private int unitSizePart2;
34
35     /**
36      * Creates a diphone with the given name, samples and midpoint.
37      *
38      * @param name the name of the diphone
39      * @param samples the set of samples for the diphone
40      * @param midPoint the index of the sample midpoint
41      */
42     public Diphone(String name, Sample[] samples, int midPoint) {
43         this.name = name;
44         this.midPoint = midPoint;
45         this.samples = samples;
46         this.unitSizePart1 = 0;
47         this.unitSizePart2 = 0;
48
49         for (int i = 0; i < midPoint; i++) {
50             unitSizePart1 += samples[i].getResidualSize();
51         }
52         for (int i = midPoint; i < samples.length; i++) {
53             unitSizePart2 += samples[i].getResidualSize();
54         }
55     }
56
57     /**
58      * Constructor to be used only by subclasses who do not use the
59      * variables except for the name
60      * @param name the name of the diphone 
61      */
62     protected Diphone(String name)
63     {
64         this.name = name;
65         this.midPoint = 0;
66         this.samples = null;
67         this.unitSizePart1 = 0;
68         this.unitSizePart2 = 0;
69     }
70
71     /**
72      * Returns the samples associated with this diphone.
73      *
74      * @return the samples associated with this diphone
75      */
76     public Sample[] getSamples() {
77         return samples;
78     }
79
80     /**
81      * Returns a particular sample.
82      *
83      * @param which which sample to return
84      *
85      * @return the desired sample
86      */
87     public Sample getSamples(int which) {
88         return samples[which];
89     }
90
91     /**
92      * Gets the name of the diphone. 
93      *
94      * @return the name of the diphone
95      */
96     public String getName() {
97         return name;
98     }
99     
100
101     /**
102      * Returns the midpoint index. the midpoint index is the sample
103      * that divides the diphone into the first and second parts.
104      *
105      * @return the midpoint index.
106      */
107     public int getMidPoint() {
108         return midPoint;
109     }
110     
111     /**
112      * Returns the midpoint index. the midpoint index is the sample
113      * that divides the diphone into the first and second parts.
114      *
115      * @return the midpoint index.
116      */
117     public int getPbPositionMillis() {
118         return getMidPoint();
119     }
120
121     /**
122      * Returns the sample that is closest to uIndex.
123      *
124      * @param uIndex the desired index
125      * @param unitPart do we want the first have (1) or the second
126      *          half (2)
127      *
128      * @return the sample nearest to the given index in the given
129      *          part
130      */ 
131     public Sample nearestSample(float uIndex, int unitPart) {
132         int i, iSize = 0, nSize;
133         // loop through all the Samples in this Diphone
134         int start = (unitPart == 1) ? 0 : midPoint;
135         int end = (unitPart == 1) ? midPoint : samples.length;
136         
137         for (i = start; i < end; i++) {
138             nSize = iSize + samples[i].getResidualSize();
139
140             if (Math.abs(uIndex - (float) iSize) <
141                 Math.abs(uIndex - (float) nSize)) {
142                 return samples[i];
143             }
144             iSize = nSize;
145         }
146         return samples[end-1];
147     }
148
149     /**
150      * Returns the total number of residuals in the given part for this
151      * diphone.
152      *
153      * @param unitPart indicates which part is of interest (1 or 2)
154      *
155      * @return the number of residuals in the specified part
156      */
157     public int getUnitSize(int unitPart) {
158         if (unitPart == 1) {
159             return unitSizePart1;
160         } else {
161             return unitSizePart2;
162         }
163     }
164
165     /**
166      * dumps out this Diphone.
167      */
168     public void dump() {
169         System.out.println("Diphone: " + name);
170         System.out.println("    MP : " + midPoint);
171         for (int i = 0; i < samples.length; i++) {
172             samples[i].dump();
173         }
174     }
175
176     /**
177      * Dumps the diphone to the given channel.
178      *
179      * @param bb the ByteBuffer to write to
180      *
181      * @throws IOException if IO error occurs
182      */
183     public void dumpBinary(ByteBuffer bb) throws IOException {
184         char[] nameArray = (name + "        ").toCharArray();
185
186         bb.putInt(MAGIC);
187         for (int i = 0; i < NAME_LENGTH; i++) {
188             bb.putChar(nameArray[i]);
189         }
190         bb.putInt(midPoint);
191         bb.putInt(samples.length);
192
193         for (int i = 0; i < samples.length; i++) {
194             samples[i].dumpBinary(bb);
195         }
196     }
197
198     /**
199      * Dumps the diphone to the given channel.
200      *
201      * @param os the DataOutputStream to write to
202      *
203      * @throws IOException if IO error occurs
204      */
205     public void dumpBinary(DataOutputStream os) throws IOException {
206         char[] nameArray = (name + "        ").toCharArray();
207
208         os.writeInt(MAGIC);
209         for (int i = 0; i < NAME_LENGTH; i++) {
210             os.writeChar(nameArray[i]);
211         }
212         os.writeInt(midPoint);
213         os.writeInt(samples.length);
214
215         for (int i = 0; i < samples.length; i++) {
216             samples[i].dumpBinary(os);
217         }
218     }
219
220     /**
221      * Determines if the two diphones are equivalent. This is for
222      * testing databases. This is not the same as "equals"
223      *
224      * @param other the diphone to compare this one to
225      *
226      * @return <code>true</code> if the diphones match; otherwise
227      *          <code>false</code> 
228      */
229     boolean compare(Diphone other) {
230         if (!name.equals(other.getName())) {
231             return false;
232         }
233
234         if (midPoint != other.getMidPoint()) {
235             return false;
236         }
237
238         if (samples.length != other.getSamples().length) {
239             return false;
240         }
241
242         for (int i = 0; i < samples.length; i++) {
243             if (!samples[i].compare(other.getSamples(i))) {
244                 return false;
245             }
246         }
247         return true;
248     }
249
250     /**
251      * Loads a new diphone from  the given buffer.
252      *
253      * @param bb the byte buffer to load the diphone from
254      *
255      * @return the new diphone
256      *
257      * @throws IOException if IO error occurs
258      */
259     public static Diphone loadBinary(ByteBuffer bb) throws IOException {
260         StringBuffer sb = new StringBuffer();
261         int midPoint;
262         int numSamples;
263         Sample[] samples;
264
265         int magic = bb.getInt();
266     if (magic == ALIAS_MAGIC) {
267         for (int i = 0; i < NAME_LENGTH; i++) {
268             char c = bb.getChar();
269             if (!Character.isWhitespace(c)) {
270                 sb.append(c);
271             }
272         }
273         String name = sb.toString().trim();
274         sb.setLength(0);
275         for (int i = 0; i < NAME_LENGTH; i++) {
276             char c = bb.getChar();
277             if (!Character.isWhitespace(c)) {
278                 sb.append(c);
279             }
280         }
281         String origName = sb.toString().trim();
282         return new AliasDiphone(name, origName);
283     } else if (magic != MAGIC) {
284             throw new Error("Bad magic number in diphone");
285         }
286
287         for (int i = 0; i < NAME_LENGTH; i++) {
288             char c = bb.getChar();
289             if (!Character.isWhitespace(c)) {
290                 sb.append(c);
291             }
292         }
293
294         midPoint = bb.getInt();
295         numSamples = bb.getInt();
296
297         samples = new Sample[numSamples];
298         for (int i = 0; i < numSamples; i++) {
299             samples[i] = Sample.loadBinary(bb);
300         }
301         return new Diphone(sb.toString().trim(), samples, midPoint);
302     }
303
304     /**
305      * Loads a new  diphone from  the given DataInputStream.
306      *
307      * @param dis the datainput stream to load the diphone from
308      *
309      * @return the new diphone
310      *
311      * @throws IOException if IO error occurs
312      */
313     public static Diphone loadBinary(DataInputStream dis) throws IOException {
314         StringBuffer sb = new StringBuffer();
315         int midPoint;
316         int numSamples;
317         Sample[] samples;
318
319     int magic = dis.readInt(); 
320     if (magic == ALIAS_MAGIC) {
321         for (int i = 0; i < NAME_LENGTH; i++) {
322             char c = dis.readChar();
323             if (!Character.isWhitespace(c)) {
324                 sb.append(c);
325             }
326         }
327         String name = sb.toString().trim();
328         sb.setLength(0);
329         for (int i = 0; i < NAME_LENGTH; i++) {
330             char c = dis.readChar();
331             if (!Character.isWhitespace(c)) {
332                 sb.append(c);
333             }
334         }
335         String origName = sb.toString().trim();
336         return new AliasDiphone(name, origName);
337     } else if (magic != MAGIC) {
338             throw new Error("Bad magic number in diphone");
339         }
340
341         for (int i = 0; i < NAME_LENGTH; i++) {
342             char c = dis.readChar();
343             if (!Character.isWhitespace(c)) {
344                 sb.append(c);
345             }
346         }
347
348         midPoint = dis.readInt();
349         numSamples = dis.readInt();
350
351         samples = new Sample[numSamples];
352         for (int i = 0; i < numSamples; i++) {
353             samples[i] = Sample.loadBinary(dis);
354         }
355         return new Diphone(sb.toString().trim(), samples, midPoint);
356     }
357 }
358