001/** 002 * Portions Copyright 2001 Sun Microsystems, Inc. 003 * Portions Copyright 1999-2001 Language Technologies Institute, 004 * Carnegie Mellon University. 005 * All Rights Reserved. Use is subject to license terms. 006 * 007 * See the file "license.terms" for information on usage and 008 * redistribution of this file, and for a DISCLAIMER OF ALL 009 * WARRANTIES. 010 */ 011package com.sun.speech.freetts.diphone; 012import com.sun.speech.freetts.relp.Sample; 013 014import java.nio.ByteBuffer; 015import java.io.IOException; 016import java.io.DataInputStream; 017import java.io.DataOutputStream; 018 019 020/** 021 * Represents two adjacent phones. A diphone is defined by its name, 022 * the set of audio data, and information used to help stitch diphones 023 * together. This class is immutable. 024 */ 025public class Diphone { 026 protected final static int MAGIC = 0xFACE0FF; 027 protected final static int ALIAS_MAGIC = 0xBABAF00; 028 protected final static int NAME_LENGTH = 8; 029 private String name; 030 private int midPoint; 031 private Sample[] samples; 032 private int unitSizePart1; 033 private int unitSizePart2; 034 035 /** 036 * Creates a diphone with the given name, samples and midpoint. 037 * 038 * @param name the name of the diphone 039 * @param samples the set of samples for the diphone 040 * @param midPoint the index of the sample midpoint 041 */ 042 public Diphone(String name, Sample[] samples, int midPoint) { 043 this.name = name; 044 this.midPoint = midPoint; 045 this.samples = samples; 046 this.unitSizePart1 = 0; 047 this.unitSizePart2 = 0; 048 049 for (int i = 0; i < midPoint; i++) { 050 unitSizePart1 += samples[i].getResidualSize(); 051 } 052 for (int i = midPoint; i < samples.length; i++) { 053 unitSizePart2 += samples[i].getResidualSize(); 054 } 055 } 056 057 /** 058 * Constructor to be used only by subclasses who do not use the 059 * variables except for the name 060 * @param name the name of the diphone 061 */ 062 protected Diphone(String name) 063 { 064 this.name = name; 065 this.midPoint = 0; 066 this.samples = null; 067 this.unitSizePart1 = 0; 068 this.unitSizePart2 = 0; 069 } 070 071 /** 072 * Returns the samples associated with this diphone. 073 * 074 * @return the samples associated with this diphone 075 */ 076 public Sample[] getSamples() { 077 return samples; 078 } 079 080 /** 081 * Returns a particular sample. 082 * 083 * @param which which sample to return 084 * 085 * @return the desired sample 086 */ 087 public Sample getSamples(int which) { 088 return samples[which]; 089 } 090 091 /** 092 * Gets the name of the diphone. 093 * 094 * @return the name of the diphone 095 */ 096 public String getName() { 097 return name; 098 } 099 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