upstream version 1.2.2
[debian/freetts] / com / sun / speech / freetts / Segmenter.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;
12
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import com.sun.speech.freetts.lexicon.Lexicon;
17
18 /**
19  * Annotates an utterance with <code>Relation.SYLLABLE</code>,
20  * <code>Relation.SYLLABLE_STRUCTURE</code>, and
21  * <code>Relation.SEGMENT</code>.
22  * To determine stress, the <code>isStressed</code> method relies upon
23  * a phone ending in the number "1".  Subclasses should override
24  * <code>isStressed</code> and <code>deStress</code> if stresses are
25  * determined in other ways.
26  *
27  * @see Relation#SEGMENT
28  * @see Relation#SYLLABLE
29  * @see Relation#SYLLABLE_STRUCTURE
30  */
31 public class Segmenter implements UtteranceProcessor {
32     private final static String STRESS = "1";
33     private final static String NO_STRESS = "0";
34
35     /**
36      * Annotates an utterance with <code>Relation.SYLLABLE</code>,
37      * <code>Relation.SYLLABLE_STRUCTURE</code>, and
38      * <code>Relation.SEGMENT</code>.
39      *
40      * @param utterance the utterance to process/tokenize
41      *
42      * @see Relation#SEGMENT
43      * @see Relation#SYLLABLE
44      * @see Relation#SYLLABLE_STRUCTURE
45      *
46      * @throws ProcessException if an IOException is thrown during the
47      *   processing of the utterance
48      */
49     public void processUtterance(Utterance utterance) throws ProcessException {
50
51         // preconditions
52         if (utterance.getRelation(Relation.WORD) == null) {
53             throw new IllegalStateException(
54                 "Word relation has not been set");
55         } else if (utterance.getRelation(Relation.SYLLABLE) != null) {
56             throw new IllegalStateException(
57                 "Syllable relation has already been set");
58         } else if (utterance.getRelation(Relation.SYLLABLE_STRUCTURE)
59                    != null) {
60             throw new IllegalStateException(
61                 "SylStructure relation has already been set");
62         } else if (utterance.getRelation(Relation.SEGMENT) != null) {
63             throw new IllegalStateException(
64                 "Segment relation has already been set");
65         }
66
67         String stress = NO_STRESS;
68         Relation syl = utterance.createRelation(Relation.SYLLABLE);
69         Relation sylstructure =
70             utterance.createRelation(Relation.SYLLABLE_STRUCTURE);
71         Relation seg = utterance.createRelation(Relation.SEGMENT);
72         Lexicon lex = utterance.getVoice().getLexicon();
73         List syllableList = null;
74
75         for (Item word = utterance.getRelation(Relation.WORD).getHead();
76                         word != null; word = word.getNext()) {
77             Item ssword = sylstructure.appendItem(word);
78             Item sylItem = null;   // item denoting syllable boundaries
79             Item segItem = null;   // item denoting phonelist (segments)
80             Item sssyl = null;     // item denoting syl in word
81
82             String[] phones = null;
83
84             Item token = word.getItemAs("Token");
85             FeatureSet featureSet = null;
86
87             if (token != null) {
88                 Item parent = token.getParent();
89                 featureSet = parent.getFeatures();
90             }
91             
92             if (featureSet != null && featureSet.isPresent("phones")) {
93                 phones = (String[]) featureSet.getObject("phones");
94             } else {
95                 phones = lex.getPhones(word.toString(), null);
96             }
97
98             for (int j = 0; j < phones.length; j++) {
99                 if (sylItem == null) {
100                     sylItem = syl.appendItem();
101                     sssyl = ssword.addDaughter(sylItem);
102                     stress = NO_STRESS;
103                     syllableList = new ArrayList();
104                 }
105                 segItem = seg.appendItem();
106                 if (isStressed(phones[j])) {
107                     stress = STRESS;
108                     phones[j] = deStress(phones[j]);
109                 }
110                 segItem.getFeatures().setString("name", phones[j]);
111                 sssyl.addDaughter(segItem);
112                 syllableList.add(phones[j]);
113                 if (lex.isSyllableBoundary(syllableList, phones, j + 1))  { 
114                     sylItem =  null;
115                     if (sssyl != null) {
116                         sssyl.getFeatures().setString("stress", stress);
117                     }
118                 }
119             }
120         }
121     }
122
123     /**
124      * Determines if the given phonemene is stressed.
125      * To determine stress, this method relies upon
126      * a phone ending in the number "1".  Subclasses should override this
127      * method if stresses are determined in other ways.
128      *
129      * @param phone the phone to check
130      *
131      * @return true if the phone is stressed, otherwise false
132      */
133     protected boolean isStressed(String phone) {
134         return phone.endsWith("1");
135     }
136
137     /**
138      * Converts stressed phoneme to regular phoneme.  This method
139      * merely removes the last character of the phone.  Subclasses
140      * should override this if another method is to be used.
141      *
142      * @param phone the phone to convert
143      *
144      * @return de-stressed phone
145      */
146     protected String deStress(String phone) {
147         String retPhone = phone;
148         if (isStressed(phone)) {
149             retPhone = phone.substring(0, phone.length() - 1);
150         }
151         return retPhone;
152     }
153
154     /**
155      * Returns the simple name of this class.
156      *
157      * @return the simple name of this class
158      */
159     public String toString() {
160         return "Segmenter";
161     }
162 }
163