upstream version 1.2.2
[debian/freetts] / com / sun / speech / freetts / diphone / DiphonePitchmarkGenerator.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.diphone;
12
13 import com.sun.speech.freetts.FeatureSet;
14 import com.sun.speech.freetts.Item;
15 import com.sun.speech.freetts.relp.LPCResult;
16 import com.sun.speech.freetts.UtteranceProcessor;
17 import com.sun.speech.freetts.Utterance;
18 import com.sun.speech.freetts.Relation;
19 import com.sun.speech.freetts.ProcessException;
20 import com.sun.speech.freetts.relp.SampleInfo;
21
22 /**
23  * Calculates pitchmarks. This is an utterance processor that expects
24  * the utterance to have a target relation. It will create an
25  * LPCResult and add it to the utterance based upon features of the
26  * target relation.
27  *
28  *  @see LPCResult
29  *  @see Relation
30  *  @see SampleInfo
31  */
32 public class DiphonePitchmarkGenerator implements UtteranceProcessor {
33
34     /**
35      * Generates the LPCResult for this utterance.
36      *
37      * @param utterance the utterance to process
38      *
39      * @throws ProcessException if an error occurs while processing
40      *     the utterance
41      * @throws IllegalStateException if the given utterance has no
42      *          relation named Relation.TARGET or a feature named
43      *          SampleInfo.UTT_NAME
44      */
45     public void processUtterance(Utterance utterance) throws ProcessException {
46
47         // precondition that must be satisfied
48         Relation targetRelation = utterance.getRelation(Relation.TARGET);
49         if (targetRelation == null) {
50             throw new IllegalStateException
51                 ("DiphonePitchmarkGenerator: Target relation does not exist");
52         }
53
54         SampleInfo sampleInfo;
55         sampleInfo = (SampleInfo) utterance.getObject(SampleInfo.UTT_NAME);
56         if (sampleInfo == null) {
57             throw new IllegalStateException
58                 ("DiphonePitchmarkGenerator: SampleInfo does not exist");
59         }
60         
61         float pos, f0, m = 0;
62         float lf0 = utterance.getVoice().getPitch();
63         
64         double time = 0;
65         int pitchMarks = 0;  // how many pitch marks
66
67         LPCResult lpcResult;
68         IntLinkedList timesList = new IntLinkedList();
69         
70         // first pass to count how many pitch marks will be required
71         for (Item targetItem = targetRelation.getHead();
72              targetItem != null; targetItem = targetItem.getNext()) {
73             FeatureSet featureSet = targetItem.getFeatures();
74             pos = featureSet.getFloat("pos");
75             f0 = featureSet.getFloat("f0");
76         //System.err.println("Target pos="+pos+", f0="+f0);
77             if (time == pos) {
78             lf0 = f0;
79                 continue;
80             }
81             m = (f0-lf0)/pos;
82         //System.err.println("m=("+f0+"-"+lf0+")/"+pos+"="+m);
83             for (; time < pos; pitchMarks++) {
84                 time += 1/(lf0 + (time * m));
85         //System.err.println("f("+time+")="+((lf0+(time*m))));
86                 // save the time value in a list
87                 timesList.add((int) (time * sampleInfo.getSampleRate()));
88             }
89         lf0 = f0;
90         }
91         lpcResult = new LPCResult();
92         // resize the number of frames to the number of pitchmarks
93         lpcResult.resizeFrames(pitchMarks);
94
95         pitchMarks = 0;
96
97         int[] targetTimes = lpcResult.getTimes();
98         
99         // second pass puts the values in
100         timesList.resetIterator();
101         for (; pitchMarks < targetTimes.length; pitchMarks++) {
102             targetTimes[pitchMarks] = timesList.nextInt();
103         }
104         utterance.setObject("target_lpcres", lpcResult);
105     }
106     
107
108     /**
109      * Returns a string representation of this object.
110      *
111      * @return a string representation of this object
112      */
113     public String toString() {
114         return "DiphonePitchmarkGenerator";
115     }
116 }
117
118 /**
119  * Represents a linked list with each node of the list storing
120  * a primitive int data type. Unlike the java.util.LinkedList, it avoids
121  * the need to wrap the float number in a Float object. This avoids
122  * unnecessary object creation, and is therefore faster and saves memory.
123  * However, it does not implement the java.util.List interface.
124  *
125  * This linked list is used as a replacement for a simple array of
126  * ints. Certain performance critical loops have had performance
127  * issues due to the overhead associated with array index bounds
128  * checking performed by the VM. Using this type of data structure
129  * allowed the checking to be bypassed. Note however that we've seen
130  * great improvement in compiler performance in this area such that we
131  * may be able to revert to using an array without any performance
132  * impact.
133  *
134  * [[[ TODO look at replacing this with a simple int array ]]]
135  */
136 class IntLinkedList {
137     private IntListNode head = null;
138     private IntListNode tail = null;
139     private IntListNode iterator = null;
140
141     /**
142      * Constructs an empty IntLinkedList.
143      */
144     public IntLinkedList() {
145         head = null;
146         tail = null;
147         iterator = null;
148     }
149     
150     /**
151      * Adds the given float to the end of the list.
152      *
153      * @param val the float to add
154      */
155     public void add(int val) {
156         IntListNode node = new IntListNode(val);
157         if (head == null) {
158             head = node;
159             tail = node;
160         } else {
161             tail.next = node;
162             tail = node;
163         }
164     }
165     
166     /**
167      * Moves the iterator to point to the front of the list.
168      */
169     public void resetIterator() {
170         iterator = head;
171     }
172     
173     /**
174      * Returns the next float in the list, advances the iterator.
175      * The <code>hasNext()</code> method MUST be called before calling
176      * this method to check if the iterator is point to null,
177      * otherwise NullPointerException will be thrown.
178      *
179      * @return the next value
180      */
181     public int nextInt() {
182         int val = iterator.val;
183         if (iterator != null) {
184             iterator = iterator.next;
185         }
186         return val;
187     }
188     
189     /**
190      * Checks if there are more elements for the iterator.
191      *
192      * @return <code>true</code>  if there are more elements; 
193      *          otherwise <code>false</code>
194      */ 
195     public boolean hasNext() {
196         return (iterator != null);
197     }
198 }
199
200 /**
201  * Represents a node for the IntList
202  */
203 class IntListNode {
204     int val;
205     IntListNode next;
206
207     /**
208      * Creates a node that wraps the given value.
209      *
210      * @param val the value to be contained in the list
211      */
212     public IntListNode(int val) {
213         this.val = val;
214         next = null;
215     }
216 }