upstream version 1.2.2
[debian/freetts] / com / sun / speech / freetts / relp / LPCResult.java
1 /**
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.
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 package com.sun.speech.freetts.relp;
12
13 import java.io.BufferedWriter;
14 import java.io.OutputStreamWriter;
15 import java.io.PrintWriter;
16 import java.io.Writer;
17 import java.io.FileWriter;
18 import java.io.IOException;
19 import java.text.DecimalFormat;
20 import javax.sound.sampled.AudioFormat;
21 import com.sun.speech.freetts.Utterance;
22 import com.sun.speech.freetts.FreeTTSSpeakable;
23 import com.sun.speech.freetts.audio.AudioPlayer;
24 import com.sun.speech.freetts.util.WaveUtils;
25 import com.sun.speech.freetts.util.Utilities;
26
27
28 /**
29  * Contains the result of linear predictive coding processing.
30  *
31  */
32 public class LPCResult {
33
34     private static final double POST_EMPHASIS = 0.0;
35
36     private int frameSize = 10;
37     private int numberOfFrames = 0;
38     
39     private short[][] frames = null;
40     private int[] times = null;
41     private int[] sizes = null;
42     
43     /**
44      * this is a normalized version of the residuals; to normalize it,
45      * add 128 to it
46      */
47     private byte[] residuals = null;
48     
49     private int numberOfChannels;
50     private int sampleRate;
51     private int residualFold;
52     
53     private float lpcMinimum;
54     private float lpcRange;
55
56     private final static int MAX_SAMPLE_SIZE = 
57         Utilities.getInteger("com.sun.speech.freetts.LpcResult.maxSamples",
58                 1024).intValue();
59
60     /**
61      * Given a residual, maps it using WaveUtils.ulawToShort() to a float.
62      */
63     private final static float[] residualToFloatMap = new float[256];
64
65     static {
66         for (short i = 0; i < residualToFloatMap.length; i++) {
67             residualToFloatMap[i] = (float) WaveUtils.ulawToShort(i);
68         }
69         residualToFloatMap[128] = (float) WaveUtils.ulawToShort((short) 255);
70     }
71
72     
73     public LPCResult() {
74         residualFold = 1;
75     }
76         
77     /**
78      * Resets the number of frames in this LPCResult.
79      *
80      * @param numberOfFrames  the number of frames in this LPC result
81      */
82     public void resizeFrames(int numberOfFrames) {
83         times = new int[numberOfFrames];
84         frames = new short[numberOfFrames][];
85         sizes = new int[numberOfFrames];
86         this.numberOfFrames = numberOfFrames;
87     }
88
89     /**
90      * Resets the number of residuals, and initialize all of them to 255
91      * (which is 0 for mulaw).
92      *
93      * @param numberOfSamples  the number of samples in this LPC result
94      */
95     public void resizeResiduals(int numberOfSamples) {
96         residuals = new byte[numberOfSamples];
97     }
98
99     /**
100      * A convenience method for setting the LPC values.
101      *
102      * @param numberOfChannels  the number of channels
103      * @param sampleRate  the sample rate
104      * @param lpcMin  the LPC minimum
105      * @param lpcRange  the LPC range
106      */
107     public void setValues(int numberOfChannels,
108                           int sampleRate,
109                           int residualFold,
110                           float lpcMin, float lpcRange) {
111         this.numberOfChannels = numberOfChannels;
112         this.sampleRate = sampleRate;
113         this.lpcMinimum = lpcMin;
114         this.lpcRange = lpcRange;
115     }
116
117     /**
118      * Returns the time difference of the frame at the given position 
119      * with the frame prior to that. If the frame at the given position is
120      * the first frame (position 0), the time of that frame is returned.
121      *
122      * @param frameIndex  the position of the frame
123      *
124      * @return the time difference of the frame at the given position 
125      *     with the frame prior to that
126      */
127     public int getFrameShift(int frameIndex) {
128         if (0 <= frameIndex && frameIndex < times.length) {
129             if (frameIndex > 0) {
130                 return times[frameIndex] - times[frameIndex - 1];
131             } else {
132                 return times[frameIndex];
133             }
134         } else {
135             return 0;
136         }
137     }
138     
139     /**
140      * Returns the sizes of frames in this LPC.
141      *
142      * @return the sizes of frames
143      */
144     public int getFrameSize() {
145         return frameSize;
146     }
147
148     /**
149      * Returns the frame at the given index.
150      *
151      * @param index the index of interest
152      *
153      * @return the frame at the given index
154      */
155     public short[] getFrame(int index) {
156         return frames[index];
157     }
158     
159     /**
160      * Returns the array of times.
161      *
162      * @return the array of times
163      */
164     public int[] getTimes() {
165         return times;
166     }
167     
168     /**
169      * Returns the number of frames in this LPCResult.
170      *
171      * @return the number of frames
172      */
173     public int getNumberOfFrames() {
174         return numberOfFrames;
175     }
176     
177     /**
178      * Returns the number of channels in this LPCResult.
179      *
180      * @return the number of channels
181      */
182     public int getNumberOfChannels() {
183         return numberOfChannels;
184     }
185     
186     /**
187      * Returns the LPC minimum.
188      *
189      * @return the LPC minimum
190      */
191     public float getLPCMin() {
192         return lpcMinimum;
193     }
194     
195     /**
196      * Returns the LPC range.
197      *
198      * @return the LPC range
199      */
200     public float getLPCRange() {
201         return lpcRange;
202     }
203     
204     /**
205      * Returns the number of samples in this LPC result
206      *
207      * @return the number of samples
208      */
209     public int getNumberOfSamples() {
210         if (residuals == null) {
211             return 0;
212         } else {
213             return residuals.length;
214         }
215     }
216     
217     /**
218      * Returns the sample rate.
219      *
220      * @return the sample rate
221      */
222     public int getSampleRate() {
223         return sampleRate;
224     }
225     
226     /**
227      * Returns the array of residuals sizes.
228      *
229      * @return the array of residuals sizes
230      */
231     public int[] getResidualSizes() {
232         return sizes;
233     }
234
235     /**
236      * Returns the array of residuals.
237      *
238      * @return the array of residuals
239      */
240     public byte[] getResiduals() {
241         return residuals;
242     }
243
244     /**
245      * Sets the sizes of frames in this LPC to the given size.
246      *
247      * @param frameSize the new frame size
248      */
249     public void setFrameSize(int frameSize) {
250         this.frameSize = frameSize;
251     }
252
253     /**
254      * Sets the number of frames in this LPC Result.
255      * 
256      * @param numberFrames the number of frames in this result
257      */
258     public void setNumberOfFrames(int numberFrames) {
259         this.numberOfFrames = numberFrames;
260     }
261         
262     /**
263      * Sets the frame at the given index.
264      *
265      * @param index the position of the frame to set
266      * @param newFrames new frame data
267      */
268     public void setFrame(int index, short[] newFrames) {
269         frames[index] = newFrames;
270     }
271
272     /**
273      * Sets the array of times.
274      *
275      * @param times the times data
276      */
277     public void setTimes(int[] times) {
278         this.times = times;
279     }
280     
281     /**
282      * Sets the number of channels.
283      *
284      * @param numberOfChannels the number of channels
285      */
286     public void setNumberOfChannels(int numberOfChannels) {
287         this.numberOfChannels = numberOfChannels;
288     }
289     
290     /**
291      * Sets the LPC minimum.
292      *
293      * @param min the LPC minimum
294      */
295     public void setLPCMin(float min) {
296         this.lpcMinimum = min;
297     }
298     
299     /**
300      * Sets the LPC range.
301      *
302      * @param range the LPC range
303      */
304     public void setLPCRange(float range) {
305         this.lpcRange = range;
306     }
307     
308     /**
309      * Sets the sample rate.
310      *
311      * @param rate the sample rate
312      */
313     public void setSampleRate(int rate) {
314         this.sampleRate = rate;
315     }
316     
317     /**
318      * Sets the array of residual sizes.
319      *
320      * @param sizes the new residual sizes
321      */
322     public void setResidualSizes(int[] sizes) {
323         for (int i = 0; i < this.sizes.length && i < sizes.length; i++) {
324             this.sizes[i] = sizes[i];
325         }
326     }
327
328     /**
329      * Copies the information in the given unit to the array of residuals,
330      * starting at the given index, up until targetSize chars.
331      *
332      * @param source  the unit that holds the information source 
333      * @param targetPosition  start position in the array of residuals
334      * @param targetSize  the maximum number of characters to copy
335      */
336     public void copyResiduals(byte[] source, 
337                               int targetPosition, 
338                               int targetSize) {
339         int unitSize = source.length;
340         if (unitSize < targetSize) {
341             int targetStart = (targetSize - unitSize)/2;
342             System.arraycopy(source, 0,
343                              residuals, targetPosition + targetStart,
344                              source.length);
345         } else {
346             int sourcePosition = (unitSize - targetSize)/2;
347             System.arraycopy(source, sourcePosition,
348                              residuals, targetPosition,
349                              targetSize);
350         }
351     }
352
353     /**
354      * Copies the residual puse in the given unit to the array of residuals,
355      * starting at the given index, up until targetSize chars.
356      *
357      * @param source  the unit that holds the information source 
358      * @param targetPosition  start position in the array of residuals
359      * @param targetSize  the maximum number of characters to copy
360      */
361     public void copyResidualsPulse(byte[] source,
362                                    int targetPosition, int targetSize) {
363         int unitSize = source.length;
364         short sample = (short) (source[0] + 128);
365         if (unitSize < targetSize) {
366             residuals[(targetSize-unitSize)/2] = WaveUtils.shortToUlaw(sample);
367         } else {
368             residuals[(unitSize-targetSize)/2] = WaveUtils.shortToUlaw(sample);
369         }
370     }
371         
372     /**
373      * Given a 16 bit value (represented as an int), extract
374      * the high eight bits and return them
375      *
376      * @param val the 16 bit value
377      *
378      * @return the high eight bits
379      */
380     private final static byte hibyte(int val) {
381         return (byte) (val >>> 8);
382     }
383
384     /**
385      * Given a 16 bit value (represented as an int), extract
386      * the low eight bits and return them
387      *
388      * @param val the 16 bit value
389      *
390      * @return the low eight bits
391      */
392     private final static byte lobyte(int val) {
393         return (byte) (val & 0x000000FF);
394     }
395     
396
397     /**
398      * Synthesize a Wave  from this LPCResult
399      *
400      * @return the wave
401      */
402     public boolean  playWave(AudioPlayer player, Utterance utterance) {
403         return playWaveSamples(player, utterance.getSpeakable(),
404                                getNumberOfSamples() * 2);
405     }
406
407
408     public byte[] getWaveSamples()
409     {
410         return getWaveSamples(2*getNumberOfSamples(), null);
411     }
412
413     /**
414      * get the samples for this utterance
415      *
416      * @param numberSamples the number of samples desirred
417      * @param utterance the utterance
418      *
419      * [[[ TODO: well there is a bunch of duplicated code here ..
420      *     these should be combined into one routine.
421      *  ]]]
422      */
423     private byte[] getWaveSamples(int numberSamples,
424                                   Utterance utterance) {
425         int numberChannels = getNumberOfChannels();
426         int pmSizeSamples;
427         float pp = 0;
428
429         byte[] samples = new byte[numberSamples];
430         byte[] residuals = getResiduals();
431         int[] residualSizes = getResidualSizes();
432         
433         FloatList outBuffer = FloatList.createList(numberChannels + 1);
434         FloatList lpcCoefficients = FloatList.createList(numberChannels);
435         
436         double multiplier = (double) getLPCRange() / 65535.0;
437         int s = 0;
438
439         // for each frame in the LPC result
440         for (int r = 0, i = 0; i < numberOfFrames; i++) {
441             
442             // unpack the LPC coefficients
443             short[] frame =  getFrame(i);
444
445             FloatList lpcCoeffs = lpcCoefficients;
446             for (int k = 0; k < numberChannels; k++) {
447                 lpcCoeffs.value = (float) ( (frame[k] + 32768.0) 
448                     * multiplier) + lpcMinimum;
449                 lpcCoeffs = lpcCoeffs.next;
450             }
451             
452             pmSizeSamples = residualSizes[i];
453
454             // resynthesis the signal, pmSizeSamples ~= 90
455             // what's in the loop is done for each residual
456             for (int j = 0; j < pmSizeSamples; j++, r++) {
457
458                 FloatList backBuffer = outBuffer.prev;
459                 float ob = residualToFloatMap[residuals[r] + 128];
460
461                 lpcCoeffs = lpcCoefficients;
462                 do {
463                     ob += lpcCoeffs.value * backBuffer.value;
464                     backBuffer = backBuffer.prev;
465                     lpcCoeffs = lpcCoeffs.next;
466                 } while (lpcCoeffs != lpcCoefficients);
467
468                 int sample = (int) (ob + (pp * POST_EMPHASIS));
469                 samples[s++] = (byte) hibyte(sample);
470                 samples[s++] = (byte) lobyte(sample);
471
472
473                 outBuffer.value = pp = ob;
474                 outBuffer = outBuffer.next;
475             }
476         }
477         return samples;
478     }
479
480     /**
481      * Play the sample data on the given player
482      *
483      * @param player where to send the audio
484      * @param numberSamples the number of samples
485      */
486     private boolean  playWaveSamples(AudioPlayer player, 
487                                      FreeTTSSpeakable speakable,
488                                      int numberSamples) {
489         boolean ok = true;
490         int numberChannels = getNumberOfChannels();
491         int pmSizeSamples;
492         float pp = 0;
493
494         byte[] samples = new byte[MAX_SAMPLE_SIZE];
495         byte[] residuals = getResiduals();
496         int[] residualSizes = getResidualSizes();
497         
498         FloatList outBuffer = FloatList.createList(numberChannels + 1);
499         FloatList lpcCoefficients = FloatList.createList(numberChannels);
500         
501         double multiplier = (double) getLPCRange() / 65535.0;
502         int s = 0;
503
504         // for each frame in the LPC result
505         player.begin(numberSamples);
506         for (int r = 0, i = 0;
507              (ok &= !speakable.isCompleted()) && 
508                  i < numberOfFrames; i++) {
509             
510             // unpack the LPC coefficients
511             short[] frame =  getFrame(i);
512
513             FloatList lpcCoeffs = lpcCoefficients;
514             for (int k = 0; k < numberChannels; k++) {
515                 lpcCoeffs.value = (float) ( (frame[k] + 32768.0) 
516                     * multiplier) + lpcMinimum;
517                 lpcCoeffs = lpcCoeffs.next;
518             }
519             
520             pmSizeSamples = residualSizes[i];
521
522             // resynthesis the signal, pmSizeSamples ~= 90
523             // what's in the loop is done for each residual
524             for (int j = 0; j < pmSizeSamples; j++, r++) {
525
526                 FloatList backBuffer = outBuffer.prev;
527                 float ob = residualToFloatMap[residuals[r] + 128];
528
529                 lpcCoeffs = lpcCoefficients;
530                 do {
531                     ob += lpcCoeffs.value * backBuffer.value;
532                     backBuffer = backBuffer.prev;
533                     lpcCoeffs = lpcCoeffs.next;
534                 } while (lpcCoeffs != lpcCoefficients);
535
536                 int sample = (int) (ob + (pp * POST_EMPHASIS));
537                 samples[s++] = hibyte(sample);
538                 samples[s++] = lobyte(sample);
539
540                 if (s >= MAX_SAMPLE_SIZE) {
541                     if ((ok &= !speakable.isCompleted()) && 
542                         !player.write(samples)) {
543                         ok = false;
544                     }
545                     s = 0;
546                 }
547
548                 outBuffer.value = pp = ob;
549                 outBuffer = outBuffer.next;
550             }
551         }
552
553         // write out the very last samples
554         if ((ok &= !speakable.isCompleted()) && s > 0) {
555             ok = player.write(samples, 0, s);
556             s = 0;
557         }
558
559         // tell the AudioPlayer it is the end of Utterance
560         if (ok &= !speakable.isCompleted()) {
561             ok = player.end();
562         }
563
564         return ok;
565     }
566
567     /**
568      * Dumps this LPCResult to standard out
569      */
570     public void dump() {
571         dump(new OutputStreamWriter(System.out));
572     }
573
574     /**
575      * Dumps this LPCResult to the given stream.
576      *
577      * @param writer the output stream
578      */
579     public void dump(Writer writer) {
580         DecimalFormat numberFormat = new DecimalFormat();
581         numberFormat.setMaximumFractionDigits(6);
582         numberFormat.setMinimumFractionDigits(6);
583         PrintWriter pw = new PrintWriter(new BufferedWriter(writer));
584
585         if (getNumberOfFrames() == 0) {
586             pw.println("# ========== LPCResult ==========");
587             pw.println("# Num_of_Frames: " + getNumberOfFrames());
588             pw.flush();
589             return;
590         }
591         pw.println("========== LPCResult ==========");
592         pw.println("Num_of_Frames: " + getNumberOfFrames());
593         pw.println("Num_of_Channels: " + getNumberOfChannels());
594         pw.println("Num_of_Samples: " + getNumberOfSamples());
595         pw.println("Sample_Rate: " + sampleRate);
596         pw.println("LPC_Minimum: " + numberFormat.format(lpcMinimum));
597         pw.println("LPC_Range: " + numberFormat.format(lpcRange));
598         pw.println("Residual_Fold: " + residualFold);
599         pw.println("Post_Emphasis: " + numberFormat.format(POST_EMPHASIS));
600                 
601         int i;
602         pw.print("Times:\n");
603         for (i = 0; i < getNumberOfFrames(); i++) {
604             pw.print(times[i] + " ");
605         }
606         pw.print("\nFrames: ");
607         for (i = 0; i < getNumberOfFrames(); i++) {
608             // for each frame, print all elements
609             short[] frame = getFrame(i);
610             for (int j = 0; j < frame.length; j++) {
611                 pw.print(( ((int) frame[j]) + 32768) + "\n");
612             }
613         }
614         pw.print("\nSizes: ");
615         for (i = 0; i < getNumberOfFrames(); i++) {
616             pw.print(sizes[i] + " ");
617         }
618         pw.print("\nResiduals: ");
619         for (i = 0; i < getNumberOfSamples(); i++) {
620             if (residuals[i] == 0) {
621                 pw.print(255);
622             } else {
623                 pw.print(( ((int) residuals[i]) + 128));
624             }
625             pw.print("\n");
626             pw.flush();
627         }
628         pw.flush();
629     }
630
631
632     /**
633      * Dumps the wave data associated with this result
634      */
635     public void dumpASCII() {
636         dumpASCII(new OutputStreamWriter(System.out));
637     }
638
639     /**
640      * Dumps the wave data associated with this result
641      *
642      * @param path the path where the wave data is appended to
643      *
644      * @throws IOException if an IO error occurs
645      */
646     public void dumpASCII(String path) throws IOException {
647         Writer writer = new FileWriter(path, true);
648         getWave().dump(writer);
649     }
650
651     /**
652      * Synthesize a Wave  from this LPCResult
653      *
654      * @return the wave
655      */
656     private  Wave getWave() {
657         // construct a new wave object
658         AudioFormat audioFormat = new AudioFormat
659             (getSampleRate(),
660              Wave.DEFAULT_SAMPLE_SIZE_IN_BITS, 1,
661              Wave.DEFAULT_SIGNED, true);
662         return new Wave(audioFormat,
663                 getWaveSamples( getNumberOfSamples() * 2, null));
664     }
665
666     /**
667      * Dumps the wave out to the given stream
668      *
669      * @param writer the output stream
670      */
671     public void dumpASCII(Writer writer)  {
672         Wave wave = getWave();
673         wave.dump(writer);
674     }
675
676     /**
677      * A Wave is an immutable class that contains the AudioFormat and
678      * the actual wave samples, which currently is in the form 
679      * of AudioInputStream.
680      */
681     private static  class Wave {
682         /**
683          * The default sample size of the Wave, which is 16.
684          */
685         public static final int DEFAULT_SAMPLE_SIZE_IN_BITS = 16;
686
687         /**
688          * A boolean indicating that the Wave is signed, i.e., 
689          * this value is true.
690          */
691         public static final boolean DEFAULT_SIGNED = true;
692
693         /**
694          * A boolean indicating that the Wave samples are represented as
695          * little endian, i.e., this value is false.
696          */
697         public static final boolean DEFAULT_BIG_ENDIAN = false;
698
699
700         private byte[] samples = null;
701         private AudioFormat audioFormat = null;
702             
703         /**
704          * Constructs a Wave with the given audio format and wave samples.
705          *
706          * @param audioFormat the audio format of the wave
707          * @param samples the wave samples
708          */
709          Wave(AudioFormat audioFormat, byte[] samples) {
710             this.audioFormat = audioFormat;
711             this.samples = samples;
712         }
713
714
715         /**
716          * Dumps the wave out to the given stream
717          * @param writer the output stream
718          */
719         public void dump(Writer writer)  {
720             PrintWriter pw = new PrintWriter(new BufferedWriter(writer));
721             pw.println("#========== Wave ==========");
722             pw.println("#Type: NULL");
723             pw.println("#Sample_Rate: " + (int)audioFormat.getSampleRate());
724             pw.println("#Num_of_Samples: " + samples.length / 2);
725             pw.println("#Num_of_Channels: " + audioFormat.getChannels());
726             if (samples != null) {
727                 for (int i = 0; i < samples.length; i+=2) {
728                     pw.println(
729                         WaveUtils.bytesToShort(samples[i], samples[i+1]));
730                 }
731             }
732             pw.flush();
733         }
734     }
735 }
736
737     
738
739 /**
740  * FloatList is used to maintain a circular buffer of float values.
741  * It is essentially an index-free array of floats that can easily be
742  * iterated through forwards or backwards. Keeping values in an index
743  * free list like this eliminates index bounds checking which can
744  * save us some time.
745  */
746 class FloatList {
747     float value;
748     FloatList next;
749     FloatList prev;
750
751     /**
752      * Creates a new node
753      */
754     FloatList() {
755         value = 0.0F;
756         next = null;
757         prev = null;
758     }
759
760     /**
761      * Creates a circular list of nodes of the given size
762      *
763      * @param size the number of nodes in the list
764      *
765      * @return an entry in the list.
766      */
767     static FloatList createList(int size) {
768         FloatList prev = null;
769         FloatList first = null;
770
771         for (int i = 0; i < size; i++) {
772             FloatList cur = new FloatList();
773             cur.prev = prev;
774             if (prev == null) {
775                 first = cur;
776             } else {
777                 prev.next = cur;
778             }
779             prev = cur;
780         }
781         first.prev = prev;
782         prev.next = first;
783
784         return first;
785     }
786
787     /**
788      * prints out the contents of this list
789      * 
790      * @param title the title of the dump
791      * @param list the list to dump
792      */
793     static void dump(String title, FloatList list) {
794         System.out.println(title);
795
796         FloatList cur = list;
797         do {
798             System.out.println("Item: " + cur.value);
799             cur = cur.next;
800         } while (cur != list);
801     }
802 }
803
804
805
806
807