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.
7 * See the file "license.terms" for information on usage and
8 * redistribution of this file, and for a DISCLAIMER OF ALL
11 package com.sun.speech.freetts.diphone;
12 import com.sun.speech.freetts.relp.Sample;
14 import java.nio.ByteBuffer;
15 import java.io.IOException;
16 import java.io.DataInputStream;
17 import java.io.DataOutputStream;
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.
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;
31 private Sample[] samples;
32 private int unitSizePart1;
33 private int unitSizePart2;
36 * Creates a diphone with the given name, samples and midpoint.
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
42 public Diphone(String name, Sample[] samples, int midPoint) {
44 this.midPoint = midPoint;
45 this.samples = samples;
46 this.unitSizePart1 = 0;
47 this.unitSizePart2 = 0;
49 for (int i = 0; i < midPoint; i++) {
50 unitSizePart1 += samples[i].getResidualSize();
52 for (int i = midPoint; i < samples.length; i++) {
53 unitSizePart2 += samples[i].getResidualSize();
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
62 protected Diphone(String name)
67 this.unitSizePart1 = 0;
68 this.unitSizePart2 = 0;
72 * Returns the samples associated with this diphone.
74 * @return the samples associated with this diphone
76 public Sample[] getSamples() {
81 * Returns a particular sample.
83 * @param which which sample to return
85 * @return the desired sample
87 public Sample getSamples(int which) {
88 return samples[which];
92 * Gets the name of the diphone.
94 * @return the name of the diphone
96 public String getName() {
102 * Returns the midpoint index. the midpoint index is the sample
103 * that divides the diphone into the first and second parts.
105 * @return the midpoint index.
107 public int getMidPoint() {
112 * Returns the midpoint index. the midpoint index is the sample
113 * that divides the diphone into the first and second parts.
115 * @return the midpoint index.
117 public int getPbPositionMillis() {
118 return getMidPoint();
122 * Returns the sample that is closest to uIndex.
124 * @param uIndex the desired index
125 * @param unitPart do we want the first have (1) or the second
128 * @return the sample nearest to the given index in the given
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;
137 for (i = start; i < end; i++) {
138 nSize = iSize + samples[i].getResidualSize();
140 if (Math.abs(uIndex - (float) iSize) <
141 Math.abs(uIndex - (float) nSize)) {
146 return samples[end-1];
150 * Returns the total number of residuals in the given part for this
153 * @param unitPart indicates which part is of interest (1 or 2)
155 * @return the number of residuals in the specified part
157 public int getUnitSize(int unitPart) {
159 return unitSizePart1;
161 return unitSizePart2;
166 * dumps out this Diphone.
169 System.out.println("Diphone: " + name);
170 System.out.println(" MP : " + midPoint);
171 for (int i = 0; i < samples.length; i++) {
177 * Dumps the diphone to the given channel.
179 * @param bb the ByteBuffer to write to
181 * @throws IOException if IO error occurs
183 public void dumpBinary(ByteBuffer bb) throws IOException {
184 char[] nameArray = (name + " ").toCharArray();
187 for (int i = 0; i < NAME_LENGTH; i++) {
188 bb.putChar(nameArray[i]);
191 bb.putInt(samples.length);
193 for (int i = 0; i < samples.length; i++) {
194 samples[i].dumpBinary(bb);
199 * Dumps the diphone to the given channel.
201 * @param os the DataOutputStream to write to
203 * @throws IOException if IO error occurs
205 public void dumpBinary(DataOutputStream os) throws IOException {
206 char[] nameArray = (name + " ").toCharArray();
209 for (int i = 0; i < NAME_LENGTH; i++) {
210 os.writeChar(nameArray[i]);
212 os.writeInt(midPoint);
213 os.writeInt(samples.length);
215 for (int i = 0; i < samples.length; i++) {
216 samples[i].dumpBinary(os);
221 * Determines if the two diphones are equivalent. This is for
222 * testing databases. This is not the same as "equals"
224 * @param other the diphone to compare this one to
226 * @return <code>true</code> if the diphones match; otherwise
229 boolean compare(Diphone other) {
230 if (!name.equals(other.getName())) {
234 if (midPoint != other.getMidPoint()) {
238 if (samples.length != other.getSamples().length) {
242 for (int i = 0; i < samples.length; i++) {
243 if (!samples[i].compare(other.getSamples(i))) {
251 * Loads a new diphone from the given buffer.
253 * @param bb the byte buffer to load the diphone from
255 * @return the new diphone
257 * @throws IOException if IO error occurs
259 public static Diphone loadBinary(ByteBuffer bb) throws IOException {
260 StringBuffer sb = new StringBuffer();
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)) {
273 String name = sb.toString().trim();
275 for (int i = 0; i < NAME_LENGTH; i++) {
276 char c = bb.getChar();
277 if (!Character.isWhitespace(c)) {
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");
287 for (int i = 0; i < NAME_LENGTH; i++) {
288 char c = bb.getChar();
289 if (!Character.isWhitespace(c)) {
294 midPoint = bb.getInt();
295 numSamples = bb.getInt();
297 samples = new Sample[numSamples];
298 for (int i = 0; i < numSamples; i++) {
299 samples[i] = Sample.loadBinary(bb);
301 return new Diphone(sb.toString().trim(), samples, midPoint);
305 * Loads a new diphone from the given DataInputStream.
307 * @param dis the datainput stream to load the diphone from
309 * @return the new diphone
311 * @throws IOException if IO error occurs
313 public static Diphone loadBinary(DataInputStream dis) throws IOException {
314 StringBuffer sb = new StringBuffer();
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)) {
327 String name = sb.toString().trim();
329 for (int i = 0; i < NAME_LENGTH; i++) {
330 char c = dis.readChar();
331 if (!Character.isWhitespace(c)) {
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");
341 for (int i = 0; i < NAME_LENGTH; i++) {
342 char c = dis.readChar();
343 if (!Character.isWhitespace(c)) {
348 midPoint = dis.readInt();
349 numSamples = dis.readInt();
351 samples = new Sample[numSamples];
352 for (int i = 0; i < numSamples; i++) {
353 samples[i] = Sample.loadBinary(dis);
355 return new Diphone(sb.toString().trim(), samples, midPoint);