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