upstream version 1.2.2
[debian/freetts] / tools / FestVoxToFreeTTS / FindSTS.java
1 /**
2  * Portions Copyright 2003 Sun Microsystems, Inc.
3  * Portions Copyright 1999-2003 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  */
12
13 import java.io.FileInputStream;
14 import java.io.DataInputStream;
15 import java.io.OutputStreamWriter;
16 import java.io.FileOutputStream;
17 import java.io.IOException;
18 import java.io.FileNotFoundException;
19
20 import javax.sound.sampled.spi.AudioFileReader;
21 import javax.sound.sampled.AudioInputStream;
22
23
24 /**
25  * Performs the generation of STS files in FestVox to FreeTTS
26  * conversion.
27  *
28  * <p>
29  * This program is a port from flite/tools/find_sts_main.c
30  * </p>
31  *
32  * <p>
33  * Note: 
34  * The a/b diff result is slightly different than the C version due to
35  * Intel floating-point math.
36  * </p>
37  */
38 public strictfp class FindSTS {
39     /**
40      * Generate an sts file
41      *
42      * <p>
43      * <b>Usage</b>
44      * </p>
45      * <p>
46      *  <code> java FindSTS lpc_min lpc_range lpcFile waveFile stsFile
47      * </code>
48      * </p>
49      * <p>
50      * <code> stsFile </code> the output file.
51      * </p>
52      */
53     public static void main(String[] args) {
54         try {
55             if (args.length != 5) {
56                 System.err.println("Usage: java FindSTS lpc_min lpc_range "
57                         + "lpcFile waveFile stsFile");
58             } else {
59                 float lpc_min = Float.parseFloat(args[0]);
60                 float lpc_range = Float.parseFloat(args[1]);
61                 FileInputStream lpcFile = new FileInputStream(args[2]);
62                 FileInputStream waveFile = new FileInputStream(args[3]);
63                 FileOutputStream stsFile = new FileOutputStream(args[4]);
64
65                 // Read input
66                 LPC lpc = new LPC(new DataInputStream(lpcFile));
67                 Wave wave = new Wave(new DataInputStream(waveFile));
68                 lpcFile.close();
69                 waveFile.close();
70
71                 // Generate sts data
72                 STS[] stsData = findSTS(wave, lpc, lpc_min, lpc_range);
73
74                 // Verify STS data for sanity
75                 Wave reconstructedWave = new
76                     Wave(wave.getSampleRate(), stsData, lpc,
77                             lpc_min, lpc_range);
78                 wave.compare(reconstructedWave);
79
80                 // Save output
81                 OutputStreamWriter stsWriter = new OutputStreamWriter(stsFile);
82                 saveSTS(stsData, lpc, wave, stsWriter, lpc_min, lpc_range);
83
84                 stsWriter.close();
85                 stsFile.close();
86             }
87         } catch (FileNotFoundException ioe) {
88             throw new Error("Error while running FindSTS" + ioe.getMessage());
89         } catch (IOException ioe) {
90             throw new Error("IO error while finding sts" + ioe.getMessage());
91         }
92     }
93
94     /**
95      * Find the sts data.
96      *
97      * @param wave the data from the wave file
98      * @param lpc the data from the lpc file
99      * @param lpc_min the minimum lpc value
100      * @param lpc_range the range of the lpc values
101      *
102      * @return an <code>STS</code> array containing the data
103      */
104     private static STS[] findSTS(Wave wave, LPC lpc, float lpc_min,
105             float lpc_range) {
106         int size;
107         int start = 0;
108         int end;
109
110         STS[] stsData = new STS[lpc.getNumFrames()];
111
112         // read wave data into a special array.
113         short[] waveData =
114             new short[wave.getNumSamples() + lpc.getNumChannels()];
115         System.arraycopy(wave.getSamples(), 0, waveData,
116                     lpc.getNumChannels(), wave.getNumSamples());
117
118         for (int i = 0; i < lpc.getNumFrames(); i++) {
119             double[] resd;
120             int[] frame;
121             short[] residual;
122
123             end = (int) ((float) wave.getSampleRate() * lpc.getTime(i));
124             size = end - start;
125             if (size <= 0) {
126                 System.out.println("frame size at "
127                         + Float.toString(lpc.getTime(i)) + " is "
128                         + Integer.toString(size) + ".");
129             }
130
131             residual = generateResiduals(waveData,
132                     start + lpc.getNumChannels(), lpc.getFrame(i),
133                     lpc.getNumChannels(), size);
134
135             frame = new int[lpc.getNumChannels() - 1];
136             for (int j = 1; j < lpc.getNumChannels(); j++) {
137                 frame[j - 1] = (int)
138                     ((((lpc.getFrameEntry(i, j) - lpc_min) / lpc_range))
139                      * (float) 65535.0);
140             }
141
142             stsData[i] = new STS(frame, size, residual);
143             start = end;
144         }
145
146         return stsData;
147     }
148
149     /**
150      * Generate the residuals for this sts
151      *
152      * @param wave specially formatted wave data
153      * @param start offset into the wave data
154      * @param frame frame data from the lpc
155      * @param order typically the number of lpc channels
156      * @param size size of the residual
157      *
158      * @return sts residuals
159      */
160     private static short[] generateResiduals(short[] wave, int start,
161             float[] frame, int order, int size) {
162         double r;
163         short[] residual = new short[size];
164         for (int i = 0; i < order; i++) {
165             r = wave[start + i];
166             for (int j = 1; j < order; j++) {
167                 r -= frame[j] * ((double) wave[start + (i - j)]);
168             }
169             residual[i] = Utility.shortToUlaw((short) r);
170         }
171         for (int i = order; i < size; i++) {
172             r = wave[start + i];
173             for (int j = 1; j < order; j++) {
174                 r -= frame[j] * ((double) wave[start + (i - j)]);
175             } 
176             residual[i] = Utility.shortToUlaw((short) r);
177         }
178         return residual;
179     }
180
181     /**
182      * Save the sts data
183      *
184      * @param stsData generated sts data
185      * @param lpc data loaded from the lpc file
186      * @param wave data loaded from the wave file
187      * @param osw the OutputStreamWriter to write the sts data to
188      * @param lpc_min minimum lpc value
189      * @param lpc_range range of lpc values
190      *
191      */
192     private static void saveSTS(STS[] stsData, LPC lpc, Wave wave,
193             OutputStreamWriter osw, float lpc_min, float lpc_range) {
194         try {
195             osw.write("( " + Integer.toString(lpc.getNumFrames())
196                     + " " + Integer.toString(lpc.getNumChannels() - 1)
197                     + " " + Integer.toString(wave.getSampleRate())
198                     + " " + Float.toString(lpc_min)
199                     + " " + Float.toString(lpc_range)
200                     + ")\n");
201             for (int m=0, i=0; i < lpc.getNumFrames(); i++) {
202                 osw.write("( " + Float.toString(lpc.getTime(i)) + " (");
203
204                 // Use the following line instead to compare against
205                 // the flite find_sts output
206                 //osw.write("( " + Utility.hex(lpc.getTime(i)) + " (");
207
208                 for (int j = 1; j < lpc.getNumChannels(); j++) {
209                     osw.write(" " +
210                             Integer.toString(stsData[i].getFrameEntry(j - 1)));
211                 }
212                 osw.write(" ) "
213                         + Integer.toString(stsData[i].getNumSamples())
214                         + " ( ");
215                 for (int j = 0; j < stsData[i].getNumSamples(); j++) {
216                         osw.write(" " +
217                                 Integer.toString(stsData[i].getResidual(j)));
218                 }
219                 osw.write(" ))\n");
220             }
221         } catch (IOException ioe) {
222             throw new Error("IO error while writing sts." + ioe.getMessage());
223         }
224     }
225 }
226
227
228 /**
229  * The lpc data
230  *
231  */
232 class LPC {
233     private int numFrames;
234     private int numChannels;
235     float[] times;
236     float[][] frames;
237
238     /** Create lpc data from an input stream
239      *
240      * @param dis DataInputStream to read the lpc in from
241      *
242      */
243     public LPC(DataInputStream dis) {
244         try {
245             if (!Utility.readWord(dis).equals("EST_File") ||
246                     !Utility.readWord(dis).equals("Track")) {
247                 throw new Error("Lpc file not EST Track file");
248             }
249
250             boolean isBinary = false;
251             boolean isBigEndian = false;
252
253             // Read Header
254             String token = Utility.readWord(dis);
255             while (!token.equals("EST_Header_End")) {
256                 if (token.equals("DataType")) {
257                     if (Utility.readWord(dis).equals("binary")) {
258                         isBinary = true;
259                     } else {
260                         isBinary = false;
261                     }
262                 } else if (token.equals("ByteOrder")) {
263                     if (Utility.readWord(dis).equals("10")) {
264                         isBigEndian = true;
265                     } else {
266                         isBigEndian = false;
267                     }
268                 } else if (token.equals("NumFrames")) {
269                     numFrames = Integer.parseInt(Utility.readWord(dis));
270                 } else if (token.equals("NumChannels")) {
271                     numChannels = Integer.parseInt(Utility.readWord(dis));
272                 }
273                 // Ignore all other content in header
274
275                 token = Utility.readWord(dis);
276             }
277
278             times = new float[numFrames];
279             frames = new float[numFrames][numChannels];
280
281             // read data section
282             if (isBinary) {
283                 loadBinaryData(dis, isBigEndian);
284             } else {
285                 loadTextData(dis);
286             }
287         }
288         catch (IOException ioe) {
289             throw new Error("IO error while parsing lpc" + ioe.getMessage());
290         }
291     }
292
293     /**
294      * load the data section of the lpc file as ascii text
295      *
296      * @param dis DataInputStream to read from
297      *
298      * @throws IOException on ill-formatted input
299      */
300     private void loadTextData(DataInputStream dis) throws IOException {
301         for (int f=0; f < numFrames; f++) {
302             times[f] = Float.parseFloat(Utility.readWord(dis));
303             Utility.readWord(dis);  // can be only 1
304             for (int c=0; c < numChannels; c++) {
305                     frames[f][c] = Float.parseFloat(Utility.readWord(dis));
306             }
307         }
308     }
309
310     /**
311      * load the data section of the lpc file as ascii text
312      *
313      * @param dis DataInputStream to read from
314      * @param isBigEndian whether or not the data in the file is in
315      *          big endian byte order
316      *
317      * @throws IOException on ill-formatted input
318      */
319     private void loadBinaryData(DataInputStream dis, boolean isBigEndian)
320             throws IOException {
321         for (int f=0; f < numFrames; f++) {
322             times[f] = Utility.readFloat(dis, isBigEndian);
323
324             // Ignore the 'breaks' field
325             Utility.readFloat(dis, isBigEndian);
326
327             for (int c=0; c < numChannels; c++) {
328                 frames[f][c] = Utility.readFloat(dis, isBigEndian);
329             }
330         }
331     }
332
333     /**
334      * Get the number of frames in this lpc
335      *
336      * @return number of frames in this lpc
337      */
338     public int getNumFrames() {
339         return numFrames;
340     }
341
342     /**
343      * Get the number of channels in this lpc
344      *
345      * @return number of channels in this lpc
346      */
347     public int getNumChannels() {
348         return numChannels;
349     }
350
351     /**
352      * Get the times associated with this lpc
353      *
354      * @return an array of times associated with this lpc
355      */
356     public float[] getTimes() {
357         return times;
358     }
359
360     /**
361      * Get an individual time associated with this lpc
362      *
363      * @param index index of time to get
364      *
365      * @return time value at given index
366      */
367     public float getTime(int index) {
368         return times[index];
369     }
370
371     /**
372      * Get an individual frame
373      *
374      * @param i index of frame
375      *
376      * @return the frame
377      */
378     public float[] getFrame(int i) {
379         return frames[i];
380     }
381
382     /**
383      * Get an individual frame entry
384      *
385      * @param i index of frame
386      * @param j index into frame
387      *
388      * @return the frame entry in frame <code>i</code> at index
389      *          <code>j</code>
390      */
391     public float getFrameEntry(int i, int j) {
392         return frames[i][j];
393     }
394 }
395
396
397 /**
398  * The wave (riff) data
399  */
400 class Wave {
401     private int numSamples;
402     private int sampleRate;
403     private short[] samples;
404
405     // Only really used in loading of data.
406     private int headerSize;
407     private int numBytes;
408     private int numChannels = 1;  // Only support mono
409
410     static final short RIFF_FORMAT_PCM = 0x0001;
411
412     /**
413      * Read in a wave from a riff format
414      *
415      * @param dis DataInputStream to read data from
416      */
417     public Wave (DataInputStream dis) {
418         try {
419             loadHeader(dis);
420             if (dis.skipBytes(headerSize - 16) != (headerSize - 16)) {
421                 throw new Error("Unexpected error parsing wave file.");
422             }
423
424             // Bunch of potential random headers
425             while (true) {
426                 String s = new String(Utility.readChars(dis, 4));
427
428                 if (s.equals("data")) {
429                     numSamples = Utility.readInt(dis, false) / 2;
430                     break;
431                 } else if (s.equals("fact")) {
432                     int i = Utility.readInt(dis, false);
433                     if (dis.skipBytes(i) != i) {
434                         throw new Error("Unexpected error parsing wave file.");
435                     }
436                 } else {
437                     throw new Error("Unsupported wave header chunk type " + s);
438                 }
439             }
440
441             int dataLength = numSamples * numChannels;
442             samples = new short[numSamples];
443
444             for (int i = 0; i < dataLength; i++) {
445                 samples[i] = Utility.readShort(dis, false);
446             }
447
448         } catch (IOException ioe) {
449             throw new Error("IO error while parsing wave" + ioe.getMessage());
450         }
451     }
452
453     /**
454      * load a RIFF header
455      *
456      * @param dis DataInputStream to read from
457      *
458      * @throws IOException on ill-formatted input
459      */
460     private void loadHeader(DataInputStream dis) throws IOException {
461         if (!checkChars(dis, "RIFF")) {
462             throw new Error("Invalid wave file format.");
463         }
464         numBytes = Utility.readInt(dis,false);
465         if (!checkChars(dis, "WAVEfmt ")) {
466             throw new Error("Invalid wave file format.");
467         }
468
469         headerSize = Utility.readInt(dis, false);
470
471         if (Utility.readShort(dis, false) != RIFF_FORMAT_PCM) {
472             throw new Error("Invalid wave file format.");
473         }
474
475         if (Utility.readShort(dis, false) != 1) {
476             throw new Error("Only mono wave files supported.");
477         }
478         
479         sampleRate = Utility.readInt(dis, false);
480         Utility.readInt(dis, false);
481         Utility.readShort(dis, false);
482         Utility.readShort(dis, false);
483     }
484
485     /**
486      * Reconstruct a wave from a wave, sts, and lpc
487      *
488      * @param sampleRate the sample rate to use
489      * @param lpc lpc
490      * @param lpc_min minimum lpc value
491      * @param lpc_range range of lpc values
492      */
493     public Wave(int sampleRate, STS[] stsData, LPC lpc, float lpc_min,
494             float lpc_range) {
495         // set number of samples and sample rate
496         numSamples = 0;
497         for (int i = 0; i < lpc.getNumFrames(); i++) {
498             numSamples += stsData[i].getNumSamples();
499         }
500         samples = new short[numSamples];
501         this.sampleRate = sampleRate;
502
503         int start = 0;
504         int end;
505         int[] lpcResTimes = new int[lpc.getNumFrames()];
506         int[] lpcResSizes = new int[lpc.getNumFrames()];
507         short[] lpcResResidual = new short[numSamples];
508         int[][] lpcResFrames = new int[lpc.getNumFrames()][];
509         int lpcResNumChannels = lpc.getNumChannels() - 1;
510
511         // load initial data
512         for (int i = 0; i < lpc.getNumFrames(); i++) {
513             lpcResTimes[i] = (int) (lpc.getTime(i) * sampleRate);
514             lpcResFrames[i] = stsData[i].getFrame();
515             end = start + stsData[i].getNumSamples();
516             lpcResSizes[i] = stsData[i].getNumSamples();
517             start = end;
518         }
519
520         for (int r = 0, i = 0; i < lpc.getNumFrames(); i++) {
521             for (int j = 0; j < stsData[i].getNumSamples(); j++, r++) {
522                 lpcResResidual[r] = stsData[i].getResidual(j);
523             }
524         }
525
526         float[] lpcCoefs = new float[lpcResNumChannels];
527         float[] outbuf = new float[lpcResNumChannels + 1];
528         int ci, cr;
529         //float pp = 0;  // the C code uses this unnecessarily (for now)
530
531         for (int r = 0, o = lpcResNumChannels, i = 0; i <
532                 lpc.getNumFrames(); i++) {
533             // residual_fold is hard-coded to 1.
534             int pm_size_samps = lpcResSizes[i];//  * residual_fold;
535
536             // Unpack the LPC coefficients
537             for (int k = 0; k < lpcResNumChannels; k++) {
538                 lpcCoefs[k] = (float)
539                     ((((double) lpcResFrames[i][k])/65535.0) * lpc_range)
540                     + lpc_min;
541             }
542
543
544             // resynthesize the signal
545             for (int j = 0; j < pm_size_samps; j++, r++) {
546                 outbuf[o] = (float)
547                     Utility.ulawToShort(lpcResResidual[r/* /residual_fold */]);
548
549                 cr = (o == 0 ? lpcResNumChannels : o-1);
550                 for (ci = 0; ci < lpcResNumChannels; ci++) {
551                         outbuf[o] += lpcCoefs[ci] * outbuf[cr];
552                         cr = (cr == 0 ? lpcResNumChannels : cr - 1);
553                 }
554                 samples[r] = (short) (outbuf[o]
555                     /* + pp * lpcres->post_emphasis)*/); // post_emphasis = 0
556                 // pp = outbuf[o];
557                 o = (o == lpcResNumChannels ? 0 : o+1);
558             }
559         }
560     }
561
562     /**
563      * Compare two waves and output how close the two are.
564      * Useful for checking the general accuracy of find sts.
565      *
566      * <p>
567      * Output may not exactly match that of flite find_sts
568      * on Intel platforms due to discrepencies in the way that
569      * Intel Pentiums perform floating point computations.
570      * </p>
571      *
572      * @param the wave to compare this wave against
573      *
574      */
575     public void compare(Wave wave2) {
576         if (numSamples > wave2.numSamples) {
577             wave2.compare(this);
578         } else {
579             double r = 0;
580             int i = 0;
581             for (i = 0; i < this.numSamples; i++) {
582                 r += (double)((float)this.samples[i] - (float)wave2.samples[i])
583                     *(double)((float)this.samples[i] - (float)wave2.samples[i]);
584             }
585             r /= this.numSamples;
586             System.out.println("a/b diff " + Double.toString(StrictMath.sqrt(r)));
587         }
588     }
589
590     /**
591      * Make sure that a string of characters appear next in the file
592      *
593      * @param dis DataInputStream to read in
594      * @param chars a String containing the ascii characters you
595      *          want the <code>dis</code> to contain.
596      *
597      * @return <code>true</code> if <code>chars</code> appears next
598      *          in <code>dis</code>, else <code>false</code>
599      * @throws on ill-formatted input (end of file, for example)
600      */
601     private boolean checkChars(DataInputStream dis, String chars)
602             throws IOException {
603         char[] carray = chars.toCharArray();
604         for (int i = 0; i < carray.length; i++) {
605             if ((char) dis.readByte() != carray[i]) {
606                 return false;
607             }
608         }
609         return true;
610     }
611
612     /**
613      * Get the sample rate for this wave
614      *
615      * @return sample rate
616      */
617     public int getSampleRate() {
618         return sampleRate;
619     }
620
621     /**
622      * Get the number of samples for this wave
623      *
624      * @return number of samples
625      */
626     public int getNumSamples() {
627         return numSamples;
628     }
629
630     /* Get the sample data of this wave
631      *
632      * @return samples
633      */
634     public short[] getSamples() {
635         return samples;
636     }
637 }
638
639 /**
640  * The sts data
641  */
642 class STS {
643     private int[] frame;
644     private int numSamples;
645     private short[] residual;
646
647     /**
648      * Create an empty STS
649      */
650     public STS() {
651     }
652
653     /**
654      * Create an sts with the given data
655      *
656      * @param frame frame for this sts
657      * @param numSamples number of samples this sts will contain
658      * @param residual the residual for this sts
659      * 
660      */
661     public STS(int[] frame, int numSamples, short[] residual) {
662         this.frame = new int[frame.length];
663         System.arraycopy(frame, 0, this.frame, 0, frame.length);
664         this.numSamples = numSamples;
665         this.residual = new short[residual.length];
666         System.arraycopy(residual, 0, this.residual, 0, residual.length);
667     }
668
669     /**
670      * Get the number of samples associated with this sts
671      *
672      * @return the number of samples for this sts
673      */
674     public int getNumSamples() {
675         return numSamples;
676     }
677
678     /**
679      * Get the residual associated with this sts
680      *
681      * @return residual associated with this sts
682      */
683     public short getResidual(int i) {
684         return residual[i];
685     }
686
687     /**
688      * Get the frame associated with this sts
689      *
690      * @return a copy of the frame associated with this sts
691      */
692     public int[] getFrame() {
693         int[] f = new int[frame.length];
694         System.arraycopy(frame, 0, f, 0, frame.length);
695         return f;
696     }
697
698     /**
699      * Get an entry out of the frame
700      *
701      * @param index the index into the frame
702      *
703      * @return the entry in the frame at offset <code>index</code>
704      */
705     public int getFrameEntry(int index) {
706         return frame[index];
707     }
708 }
709
710
711 /**
712  * This class is for general purpose functions such as reading and
713  * writing from files, or converting formats of numbers.
714  */
715 class Utility {
716
717     /**
718      * Reads the next word (text separated by whitespace) from the
719      * given stream
720      *
721      * @param dis the input stream
722      *
723      * @return the next word
724      *
725      * @throws IOException on error
726      */
727     public static String readWord(DataInputStream dis) throws IOException {
728         StringBuffer sb = new StringBuffer();
729         char c;
730
731         // skip leading whitespace
732         do {
733             c = readChar(dis);
734         } while(Character.isWhitespace(c));
735
736         // read the word
737         do {
738             sb.append(c);
739             c = readChar(dis);
740         } while (!Character.isWhitespace(c));
741         return sb.toString();
742     }
743
744     /**
745      * Reads a single char from the stream
746      *
747      * @param dis the stream to read
748      * @return the next character on the stream
749      *
750      * @throws IOException if an error occurs
751      */
752     public static char readChar(DataInputStream dis) throws IOException {
753         return (char) dis.readByte();
754     }
755
756     /**
757      * Reads a given number of chars from the stream
758      *
759      * @param dis the stream to read
760      * @param num the number of chars to read
761      * @return a character array containing the next <code>num<code>
762      *          in the stream
763      *
764      * @throws IOException if an error occurs
765      */
766     public static char[] readChars(DataInputStream dis, int num)
767             throws IOException {
768         char[] carray = new char[num];
769         for (int i = 0; i < num; i++) {
770             carray[i] = readChar(dis);
771         }
772         return carray;
773     }
774
775     /**
776      * Read a float from the input stream, byte-swapping as
777      * necessary
778      *
779      * @param dis the inputstream
780      * @param isBigEndian whether or not the data being read in is in
781      *          big endian format.
782      *
783      * @return a floating pint value
784      *
785      * @throws IOException on error
786      */
787     public static float readFloat(DataInputStream dis, boolean isBigEndian)
788             throws IOException {
789         float val;
790         if (!isBigEndian) {
791             val =  readLittleEndianFloat(dis);
792         } else {
793             val =  dis.readFloat();
794         }
795         return val;
796     }
797
798     /**
799      * Reads the next float from the given DataInputStream,
800      * where the data is in little endian.
801      *
802      * @param dataStream the DataInputStream to read from
803      *
804      * @return a float
805      */
806     public static float readLittleEndianFloat(DataInputStream dataStream)
807             throws IOException {
808         return Float.intBitsToFloat(readLittleEndianInt(dataStream));
809     }
810
811     /**
812      * Read an integer from the input stream, byte-swapping as
813      * necessary
814      *
815      * @param dis the inputstream
816      * @param isBigEndian whether or not the data being read in is in
817      *          big endian format.
818      *
819      * @return an integer value
820      *
821      * @throws IOException on error
822      */
823     public static int readInt(DataInputStream dis, boolean isBigEndian)
824             throws IOException {
825         if (!isBigEndian) {
826             return readLittleEndianInt(dis);
827         } else {
828             return dis.readInt();
829         }
830     }
831
832     /**
833      * Reads the next little-endian integer from the given DataInputStream.
834      *
835      * @param dataStream the DataInputStream to read from
836      *
837      * @return an integer
838      */
839     public static int readLittleEndianInt(DataInputStream dataStream)
840             throws IOException {
841         int bits = 0x00000000;
842         for (int shift = 0; shift < 32; shift += 8) {
843             int byteRead = (0x000000ff & dataStream.readByte());
844             bits |= (byteRead << shift);
845         }
846         return bits;
847     }
848
849     /**
850      * Read a short from the input stream, byte-swapping as
851      * necessary
852      *
853      * @param dis the inputstream
854      * @param isBigEndian whether or not the data being read in is in
855      *          big endian format.
856      *
857      * @return an integer value
858      *
859      * @throws IOException on error
860      */
861     public static short readShort(DataInputStream dis, boolean isBigEndian)
862         throws IOException {
863         if (!isBigEndian) {
864             return readLittleEndianShort(dis);
865         } else {
866             return dis.readShort();
867         }
868     }
869
870     /**
871      * Reads the next little-endian short from the given DataInputStream.
872      *
873      * @param dataStream the DataInputStream to read from
874      *
875      * @return a short
876      */
877     public static short readLittleEndianShort(DataInputStream dis)
878         throws IOException {
879         short bits = (short)(0x0000ff & dis.readByte());
880         bits |= (((short)(0x0000ff & dis.readByte())) << 8);
881         return bits;
882     }
883
884     /**
885      * Convert a short to ulaw format
886      * 
887      * @param sample the short to convert
888      *
889      * @return a short containing an unsigned 8-bit quantity
890      *          representing the ulaw
891      */
892     public static short shortToUlaw(short sample) {
893         final int[] exp_lut = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
894                                    4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
895                                    5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
896                                    5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
897                                    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
898                                    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
899                                    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
900                                    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
901                                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
902                                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
903                                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
904                                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
905                                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
906                                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
907                                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
908                                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
909
910         int sign, exponent, mantissa;
911         short ulawbyte;
912
913         final short CLIP = 32635;
914         final short BIAS = 0x0084;
915
916         /* Get the sample into sign-magnitude. */
917         sign = (sample >> 8) & 0x80; /* set aside the sign */
918         if ( sign != 0 ) {
919             sample = (short) -sample; /* get magnitude */
920         }
921         if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
922
923         /* Convert from 16 bit linear to ulaw. */
924         sample = (short) (sample + BIAS);
925         exponent = exp_lut[( sample >> 7 ) & 0xFF];
926         mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
927         ulawbyte = (short)
928             ((~ ( sign | ( exponent << 4 ) | mantissa)) & 0x00FF);
929         if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
930         return ulawbyte;
931     }
932
933     /**
934      * Convert a ulaw format to short
935      * 
936      * @param ulawbyte a short containing an unsigned 8-but quantity
937      *          representing a ulaw
938      *
939      * @return the short equivalent of the ulaw
940      */
941     public static short ulawToShort(short ulawbyte) {
942         final int[] exp_lut = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
943         int sign, exponent, mantissa;
944         short sample;
945
946         ulawbyte = (short) (ulawbyte & 0x00FF);
947         ulawbyte = (short) (~ulawbyte);
948         sign = ( ulawbyte & ((short) 0x80) );
949         exponent = (int) ( (ulawbyte & (short) 0x00FF) >> 4 ) & 0x07;
950         mantissa = ulawbyte & (short) 0x0F;
951         sample = (short) (exp_lut[exponent] + (mantissa << (exponent + 3)));
952         if ( sign != 0 ) sample = (short) (-sample);
953
954         return sample;
955     }
956
957
958     /**
959      * Print a float type's internal bit representation in hex
960      *
961      * @param f the float to print
962      *
963      * @return a string containing the hex value of <code>f</code>
964      */
965     public static String hex(float f) {
966         return Integer.toHexString(Float.floatToIntBits(f));
967     }
968 }
969