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.relp; 012 013import java.io.BufferedReader; 014import java.io.DataInputStream; 015import java.io.DataOutputStream; 016import java.io.IOException; 017import java.nio.ByteBuffer; 018import java.util.NoSuchElementException; 019import java.util.StringTokenizer; 020 021/** 022 * A single short term sample containing Residual Excited Linear Predictive 023 * (RELP) frame and residual voice data. 024 */ 025public class Sample { 026 private final short[] frameData; 027 private final byte[] residualData; 028 private final int residualSize; 029 030 031 /** 032 * Constructs a RELP Sample from its component parts 033 * 034 * @param frameData the framedata 035 * @param residualData the residual data 036 */ 037 public Sample(short[] frameData, byte[] residualData) { 038 this.frameData = frameData; 039 this.residualData = residualData; 040 this.residualSize = 0; 041 } 042 043 /** 044 * Constructs a Sample from its component parts 045 * 046 * @param frameData the framedata 047 * @param residualData the residual data 048 */ 049 public Sample(short[] frameData, byte[] residualData, int residualSize) { 050 this.frameData = frameData; 051 this.residualData = residualData; 052 this.residualSize = residualSize; 053 } 054 055 /** 056 * Reads a sample from the input reader. 057 * 058 * @param reader the input reader to read the data from 059 * @param numChannels the number of channels per frame 060 */ 061 public Sample(BufferedReader reader, int numChannels) { 062 try { 063 String line = reader.readLine(); 064 065 StringTokenizer tok = new StringTokenizer(line); 066 if (!tok.nextToken().equals("FRAME")) { 067 throw new Error("frame Parsing sample error"); 068 } 069 070 frameData = new short[numChannels]; 071 072 for (int i = 0; i < numChannels; i++) { 073 int svalue = Integer.parseInt(tok.nextToken()) - 32768; 074 075 if ( svalue < -32768 || svalue > 32767) { 076 throw new Error("data out of short range"); 077 } 078 frameData[i] = (short) svalue; 079 } 080 081 line = reader.readLine(); 082 tok = new StringTokenizer(line); 083 if (!tok.nextToken().equals("RESIDUAL")) { 084 throw new Error("residual Parsing sample error"); 085 } 086 087 residualSize = Integer.parseInt(tok.nextToken()); 088 residualData = new byte[residualSize]; 089 090 for (int i = 0; i < residualSize; i++) { 091 int bvalue = Integer.parseInt(tok.nextToken()) - 128; 092 093 if ( bvalue < -128 || bvalue > 127) { 094 throw new Error("data out of byte range"); 095 } 096 residualData[i] = (byte) bvalue; 097 } 098 } catch (NoSuchElementException nse) { 099 throw new Error("Parsing sample error " + nse.getMessage()); 100 } catch (IOException ioe) { 101 throw new Error("IO error while parsing sample" + ioe.getMessage()); 102 } 103 } 104 105 /** 106 * Gets the frame data associated with this sample 107 * 108 * @return the frame data associated with this sample 109 */ 110 public short[] getFrameData() { 111 return frameData; 112 } 113 114 /** 115 * Gets the residual data associated with this sample 116 * 117 * @return the residual data associated with this sample 118 */ 119 public byte[] getResidualData() { 120 return residualData; 121 } 122 123 /** 124 * Returns the number of residuals in this Sample. 125 * 126 * @return the number of residuals in this sample 127 */ 128 public int getResidualSize() { 129 return residualSize; 130 } 131 132 133 /** 134 * Returns the normalized residual data. You may not want to 135 * call this function because of the overhead involved. 136 * 137 * @param which the index of the data of interest 138 * 139 * @return the normalized data. 140 */ 141 public int getResidualData(int which) { 142 return ((int)residualData[which]) + 128; 143 } 144 145 /** 146 * Returns the normalized frame data. You may not want to 147 * call this function because of the overhead involved. 148 * 149 * @param which the index of the data of interest 150 * 151 * @return the normalized data. 152 */ 153 public int getFrameData(int which) { 154 return ((int)frameData[which]) + 32768; 155 } 156 157 158 /** 159 * Dumps the sample: 160 */ 161 public void dump() { 162 System.out.println(" FD Count: " + getFrameData().length); 163 for (int i = 0; i < getFrameData().length; i++) { 164 System.out.print(" " + getFrameData(i)); 165 } 166 System.out.println(); 167 System.out.println(" RD Count: " + getResidualSize()); 168 // getResidualData().length); 169 for (int i = 0; i < getResidualData().length; i++) { 170 System.out.print(" " + getResidualData(i)); 171 } 172 System.out.println(); 173 } 174 175 /** 176 * Dumps the samples to the given ByteBuffer 177 * 178 * @param bb the ByteBuffer to write the data to. 179 * 180 * @throws IOException if IO error occurs 181 */ 182 public void dumpBinary(ByteBuffer bb) throws IOException { 183 bb.putInt(frameData.length); 184 for (int i = 0; i < frameData.length; i++) { 185 bb.putShort(frameData[i]); 186 } 187 bb.putInt(residualData.length); 188 bb.put(residualData); 189 } 190 191 /** 192 * Dumps the samples to the given stream 193 * 194 * @param os the DataOutputStream to write the data to. 195 * 196 * @throws IOException if IO error occurs 197 */ 198 public void dumpBinary(DataOutputStream os) throws IOException { 199 os.writeInt(frameData.length); 200 for (int i = 0; i < frameData.length; i++) { 201 os.writeShort(frameData[i]); 202 } 203 os.writeInt(residualData.length); 204 for (int i = 0; i < residualData.length; i++) { 205 os.writeByte(residualData[i]); 206 } 207 } 208 209 /** 210 * Loads the samples from the byte bufer 211 * 212 * @param bb the byte buffer to read the data from. 213 * 214 * @throws IOException if IO error occurs 215 */ 216 public static Sample loadBinary(ByteBuffer bb) throws IOException { 217 int frameDataSize = bb.getInt(); 218 219 short[] frameData = new short[frameDataSize]; 220 221 for (int i = 0; i < frameData.length; i++) { 222 frameData[i] = bb.getShort(); 223 } 224 225 int residualDataSize = bb.getInt(); 226 byte[] residualData = new byte[residualDataSize]; 227 228 for (int i = 0; i < residualData.length; i++) { 229 residualData[i] = bb.get(); 230 } 231 232 return new Sample(frameData, residualData, residualDataSize); 233 } 234 235 /** 236 * Loads the samples from the given channel 237 * 238 * @param dis the DataInputStream to read the data from. 239 * 240 * @throws IOException if IO error occurs 241 */ 242 public static Sample loadBinary(DataInputStream dis) 243 throws IOException { 244 int frameDataSize = dis.readInt(); 245 246 short[] frameData = new short[frameDataSize]; 247 248 for (int i = 0; i < frameData.length; i++) { 249 frameData[i] = dis.readShort(); 250 } 251 252 int residualDataSize = dis.readInt(); 253 byte[] residualData = new byte[residualDataSize]; 254 255 for (int i = 0; i < residualData.length; i++) { 256 residualData[i] = dis.readByte(); 257 } 258 259 return new Sample(frameData, residualData, residualDataSize); 260 } 261 262 /** 263 * Compares two samples. Note that this is not the same as 264 * "equals" 265 * 266 * @param other the other sample to compare this one to 267 * 268 * @return <code>true</code> if they compare; otherwise 269 * <code>false</code> 270 */ 271 public boolean compare(Sample other) { 272 273 if (frameData.length != other.getFrameData().length) { 274 return false; 275 } 276 277 for (int i = 0; i < frameData.length; i++) { 278 if (frameData[i] != other.frameData[i]) { 279 return false; 280 } 281 } 282 283 if (residualData.length != other.residualData.length) { 284 return false; 285 } 286 287 for (int i = 0; i < residualData.length; i++) { 288 if (residualData[i] != other.residualData[i]) { 289 return false; 290 } 291 } 292 return true; 293 } 294} 295 296 297